aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf5
-rw-r--r--configure.json1
-rw-r--r--examples/qml/referenceexamples/adding/main.cpp2
-rw-r--r--examples/qml/referenceexamples/adding/person.h2
-rw-r--r--examples/qml/referenceexamples/attached/birthdayparty.cpp2
-rw-r--r--examples/qml/referenceexamples/attached/birthdayparty.h2
-rw-r--r--examples/qml/referenceexamples/attached/main.cpp2
-rw-r--r--examples/qml/referenceexamples/attached/person.h6
-rw-r--r--examples/qml/referenceexamples/binding/birthdayparty.h2
-rw-r--r--examples/qml/referenceexamples/binding/happybirthdaysong.cpp2
-rw-r--r--examples/qml/referenceexamples/binding/happybirthdaysong.h4
-rw-r--r--examples/qml/referenceexamples/binding/main.cpp2
-rw-r--r--examples/qml/referenceexamples/binding/person.h8
-rw-r--r--examples/qml/referenceexamples/coercion/birthdayparty.cpp2
-rw-r--r--examples/qml/referenceexamples/coercion/birthdayparty.h2
-rw-r--r--examples/qml/referenceexamples/coercion/main.cpp2
-rw-r--r--examples/qml/referenceexamples/coercion/person.h6
-rw-r--r--examples/qml/referenceexamples/default/birthdayparty.cpp2
-rw-r--r--examples/qml/referenceexamples/default/birthdayparty.h2
-rw-r--r--examples/qml/referenceexamples/default/main.cpp2
-rw-r--r--examples/qml/referenceexamples/default/person.h6
-rw-r--r--examples/qml/referenceexamples/extended/lineedit.cpp50
-rw-r--r--examples/qml/referenceexamples/extended/main.cpp2
-rw-r--r--examples/qml/referenceexamples/grouped/birthdayparty.cpp2
-rw-r--r--examples/qml/referenceexamples/grouped/birthdayparty.h2
-rw-r--r--examples/qml/referenceexamples/grouped/main.cpp2
-rw-r--r--examples/qml/referenceexamples/grouped/person.h8
-rw-r--r--examples/qml/referenceexamples/methods/birthdayparty.cpp4
-rw-r--r--examples/qml/referenceexamples/methods/birthdayparty.h2
-rw-r--r--examples/qml/referenceexamples/methods/main.cpp2
-rw-r--r--examples/qml/referenceexamples/methods/person.h2
-rw-r--r--examples/qml/referenceexamples/properties/birthdayparty.cpp4
-rw-r--r--examples/qml/referenceexamples/properties/birthdayparty.h2
-rw-r--r--examples/qml/referenceexamples/properties/main.cpp2
-rw-r--r--examples/qml/referenceexamples/properties/person.h2
-rw-r--r--examples/qml/referenceexamples/signal/birthdayparty.cpp2
-rw-r--r--examples/qml/referenceexamples/signal/birthdayparty.h2
-rw-r--r--examples/qml/referenceexamples/signal/main.cpp2
-rw-r--r--examples/qml/referenceexamples/signal/person.h8
-rw-r--r--examples/qml/referenceexamples/valuesource/birthdayparty.cpp2
-rw-r--r--examples/qml/referenceexamples/valuesource/birthdayparty.h2
-rw-r--r--examples/qml/referenceexamples/valuesource/happybirthdaysong.cpp2
-rw-r--r--examples/qml/referenceexamples/valuesource/happybirthdaysong.h4
-rw-r--r--examples/qml/referenceexamples/valuesource/main.cpp2
-rw-r--r--examples/qml/referenceexamples/valuesource/person.h8
-rw-r--r--examples/quick/imageelements/content/multi.icobin0 -> 27110 bytes
-rw-r--r--examples/quick/imageelements/framestepping.qml82
-rw-r--r--examples/quick/imageelements/imageelements.qml2
-rw-r--r--examples/quick/imageelements/imageelements.qrc3
-rw-r--r--examples/quick/imageelements/multiframeborderimage.qml83
-rw-r--r--examples/quick/imageresponseprovider/imageresponseprovider.cpp71
-rw-r--r--examples/quick/localstorage/localstorage/Header.qml10
-rw-r--r--examples/quick/scenegraph/d3d11underqml/d3d11squircle.cpp426
-rw-r--r--examples/quick/scenegraph/d3d11underqml/d3d11squircle.h86
-rw-r--r--examples/quick/scenegraph/d3d11underqml/d3d11underqml.pro12
-rw-r--r--examples/quick/scenegraph/d3d11underqml/d3d11underqml.qrc7
-rw-r--r--examples/quick/scenegraph/d3d11underqml/doc/images/d3d11underqml-example.jpgbin0 -> 79343 bytes
-rw-r--r--examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc58
-rw-r--r--examples/quick/scenegraph/d3d11underqml/main.cpp69
-rw-r--r--examples/quick/scenegraph/d3d11underqml/main.qml89
-rw-r--r--examples/quick/scenegraph/d3d11underqml/squircle.frag35
-rw-r--r--examples/quick/scenegraph/d3d11underqml/squircle.vert30
-rw-r--r--examples/quick/scenegraph/fboitem/doc/images/fboitem-example.jpg (renamed from examples/quick/scenegraph/textureinsgnode/doc/images/textureinsgnode-example.jpg)bin25863 -> 25863 bytes
-rw-r--r--examples/quick/scenegraph/fboitem/doc/src/fboitem.qdoc (renamed from examples/quick/scenegraph/textureinsgnode/doc/src/textureinsgnode.qdoc)4
-rw-r--r--examples/quick/scenegraph/fboitem/fboinsgrenderer.cpp (renamed from examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.cpp)0
-rw-r--r--examples/quick/scenegraph/fboitem/fboinsgrenderer.h (renamed from examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.h)0
-rw-r--r--examples/quick/scenegraph/fboitem/fboitem.pro (renamed from examples/quick/scenegraph/textureinsgnode/textureinsgnode.pro)4
-rw-r--r--examples/quick/scenegraph/fboitem/fboitem.qrc (renamed from examples/quick/scenegraph/textureinsgnode/textureinsgnode.qrc)2
-rw-r--r--examples/quick/scenegraph/fboitem/main.cpp (renamed from examples/quick/scenegraph/textureinsgnode/main.cpp)2
-rw-r--r--examples/quick/scenegraph/fboitem/main.qml (renamed from examples/quick/scenegraph/textureinsgnode/main.qml)0
-rw-r--r--examples/quick/scenegraph/metaltextureimport/doc/images/metaltextureimport-example.jpgbin0 -> 44545 bytes
-rw-r--r--examples/quick/scenegraph/metaltextureimport/doc/src/metaltextureimport.qdoc101
-rw-r--r--examples/quick/scenegraph/metaltextureimport/main.cpp70
-rw-r--r--examples/quick/scenegraph/metaltextureimport/main.qml123
-rw-r--r--examples/quick/scenegraph/metaltextureimport/metaltextureimport.h88
-rw-r--r--examples/quick/scenegraph/metaltextureimport/metaltextureimport.mm424
-rw-r--r--examples/quick/scenegraph/metaltextureimport/metaltextureimport.pro12
-rw-r--r--examples/quick/scenegraph/metaltextureimport/metaltextureimport.qrc7
-rw-r--r--examples/quick/scenegraph/metaltextureimport/squircle.frag29
-rw-r--r--examples/quick/scenegraph/metaltextureimport/squircle.vert23
-rw-r--r--examples/quick/scenegraph/metalunderqml/doc/images/metalunderqml-example.jpgbin0 -> 55194 bytes
-rw-r--r--examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc69
-rw-r--r--examples/quick/scenegraph/metalunderqml/main.cpp69
-rw-r--r--examples/quick/scenegraph/metalunderqml/main.qml89
-rw-r--r--examples/quick/scenegraph/metalunderqml/metalsquircle.h86
-rw-r--r--examples/quick/scenegraph/metalunderqml/metalsquircle.mm369
-rw-r--r--examples/quick/scenegraph/metalunderqml/metalunderqml.pro12
-rw-r--r--examples/quick/scenegraph/metalunderqml/metalunderqml.qrc7
-rw-r--r--examples/quick/scenegraph/metalunderqml/squircle.frag29
-rw-r--r--examples/quick/scenegraph/metalunderqml/squircle.vert23
-rw-r--r--examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc47
-rw-r--r--examples/quick/scenegraph/openglunderqml/squircle.cpp53
-rw-r--r--examples/quick/scenegraph/openglunderqml/squircle.h3
-rw-r--r--examples/quick/scenegraph/rendernode/customrenderitem.cpp70
-rw-r--r--examples/quick/scenegraph/rendernode/d3d12renderer.cpp34
-rw-r--r--examples/quick/scenegraph/rendernode/d3d12renderer.h11
-rw-r--r--examples/quick/scenegraph/rendernode/doc/images/rendernode-example.jpgbin0 -> 77210 bytes
-rw-r--r--examples/quick/scenegraph/rendernode/doc/src/rendernode.qdoc165
-rw-r--r--examples/quick/scenegraph/rendernode/main.cpp2
-rw-r--r--examples/quick/scenegraph/rendernode/main.qml90
-rw-r--r--examples/quick/scenegraph/rendernode/metalrenderer.h104
-rw-r--r--examples/quick/scenegraph/rendernode/metalrenderer.mm333
-rw-r--r--examples/quick/scenegraph/rendernode/metalshader.frag28
-rw-r--r--examples/quick/scenegraph/rendernode/metalshader.vert31
-rw-r--r--examples/quick/scenegraph/rendernode/openglrenderer.cpp48
-rw-r--r--examples/quick/scenegraph/rendernode/openglrenderer.h10
-rw-r--r--examples/quick/scenegraph/rendernode/rendernode.pro6
-rw-r--r--examples/quick/scenegraph/rendernode/rendernode.qrc2
-rw-r--r--examples/quick/scenegraph/rendernode/softwarerenderer.cpp26
-rw-r--r--examples/quick/scenegraph/rendernode/softwarerenderer.h7
-rw-r--r--examples/quick/scenegraph/scenegraph.pro16
-rw-r--r--examples/quick/scenegraph/shared/squircle_rhi.frag16
-rw-r--r--examples/quick/scenegraph/shared/squircle_rhi.vert13
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/doc/images/vulkanunderqml-example.jpgbin0 -> 39962 bytes
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/doc/src/vulkanunderqml.qdoc68
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/main.cpp70
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/main.qml89
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/squircle.frag.spvbin0 -> 1432 bytes
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/squircle.vert.spvbin0 -> 644 bytes
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp623
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/vulkansquircle.h86
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.pro10
-rw-r--r--examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.qrc7
-rw-r--r--examples/quick/shadereffects/content/shaders/+qsb/blur.fragbin0 -> 2247 bytes
-rw-r--r--examples/quick/shadereffects/content/shaders/+qsb/colorize.fragbin0 -> 2106 bytes
-rw-r--r--examples/quick/shadereffects/content/shaders/+qsb/genie.vertbin0 -> 2629 bytes
-rw-r--r--examples/quick/shadereffects/content/shaders/+qsb/outline.fragbin0 -> 2538 bytes
-rw-r--r--examples/quick/shadereffects/content/shaders/+qsb/shadow.fragbin0 -> 2151 bytes
-rw-r--r--examples/quick/shadereffects/content/shaders/+qsb/wobble.fragbin0 -> 2049 bytes
-rw-r--r--examples/quick/shadereffects/content/shaders/rhi/blur.frag21
-rw-r--r--examples/quick/shadereffects/content/shaders/rhi/colorize.frag20
-rwxr-xr-xexamples/quick/shadereffects/content/shaders/rhi/compile.bat56
-rw-r--r--examples/quick/shadereffects/content/shaders/rhi/genie.vert29
-rw-r--r--examples/quick/shadereffects/content/shaders/rhi/outline.frag24
-rw-r--r--examples/quick/shadereffects/content/shaders/rhi/shadow.frag21
-rw-r--r--examples/quick/shadereffects/content/shaders/rhi/wobble.frag20
-rw-r--r--examples/quick/shadereffects/doc/src/shadereffects.qdoc16
-rw-r--r--examples/quick/shadereffects/shadereffects.qrc6
-rw-r--r--examples/quick/shapes/content/interactive.qml113
-rw-r--r--examples/quick/shared/CheckBox.qml2
-rw-r--r--examples/quick/shared/LauncherList.qml11
-rw-r--r--examples/quick/shared/Slider.qml2
-rw-r--r--examples/quick/tutorials/helloworld/Cell.qml2
-rw-r--r--src/3rdparty/llvm/LICENSE.TXT68
-rw-r--r--src/3rdparty/llvm/include/llvm-c/DataTypes.h90
-rw-r--r--src/3rdparty/llvm/include/llvm/ADT/PointerIntPair.h233
-rw-r--r--src/3rdparty/llvm/include/llvm/ADT/ilist.h431
-rw-r--r--src/3rdparty/llvm/include/llvm/ADT/ilist_base.h93
-rw-r--r--src/3rdparty/llvm/include/llvm/ADT/ilist_iterator.h199
-rw-r--r--src/3rdparty/llvm/include/llvm/ADT/ilist_node.h306
-rw-r--r--src/3rdparty/llvm/include/llvm/ADT/ilist_node_base.h53
-rw-r--r--src/3rdparty/llvm/include/llvm/ADT/ilist_node_options.h133
-rw-r--r--src/3rdparty/llvm/include/llvm/ADT/iterator.h339
-rw-r--r--src/3rdparty/llvm/include/llvm/ADT/iterator_range.h68
-rw-r--r--src/3rdparty/llvm/include/llvm/ADT/simple_ilist.h315
-rw-r--r--src/3rdparty/llvm/include/llvm/Demangle/Compiler.h524
-rw-r--r--src/3rdparty/llvm/include/llvm/Support/Compiler.h19
-rw-r--r--src/3rdparty/llvm/include/llvm/Support/DataTypes.h17
-rw-r--r--src/3rdparty/llvm/include/llvm/Support/PointerLikeTypeTraits.h116
-rw-r--r--src/3rdparty/llvm/llvm.pri18
-rw-r--r--src/3rdparty/llvm/qt_attribution.json14
-rw-r--r--src/3rdparty/masm/assembler/ARM64Assembler.h7
-rw-r--r--src/3rdparty/masm/assembler/ARMv7Assembler.h31
-rw-r--r--src/3rdparty/masm/assembler/AbstractMacroAssembler.h14
-rw-r--r--src/3rdparty/masm/assembler/LinkBuffer.h6
-rw-r--r--src/3rdparty/masm/assembler/MacroAssembler.h14
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerARM64.h39
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerARMv7.h71
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerX86.h32
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerX86_64.h44
-rw-r--r--src/3rdparty/masm/assembler/X86Assembler.h41
-rw-r--r--src/3rdparty/masm/masm-defs.pri4
-rw-r--r--src/3rdparty/masm/masm.pri7
-rw-r--r--src/3rdparty/masm/stubs/wtf/Vector.h9
-rw-r--r--src/3rdparty/masm/wtf/OSAllocatorPosix.cpp14
-rw-r--r--src/3rdparty/masm/wtf/Platform.h6
-rw-r--r--src/3rdparty/masm/yarr/YarrCanonicalize.h1
-rw-r--r--src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp122
-rw-r--r--src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js21
-rw-r--r--src/3rdparty/masm/yarr/YarrErrorCode.h7
-rw-r--r--src/3rdparty/masm/yarr/YarrInterpreter.cpp43
-rw-r--r--src/3rdparty/masm/yarr/YarrJIT.cpp538
-rw-r--r--src/3rdparty/masm/yarr/YarrJIT.h5
-rw-r--r--src/3rdparty/masm/yarr/YarrParser.h23
-rw-r--r--src/3rdparty/masm/yarr/YarrPattern.cpp137
-rw-r--r--src/3rdparty/masm/yarr/YarrPattern.h22
-rw-r--r--src/3rdparty/masm/yarr/YarrSyntaxChecker.cpp4
-rw-r--r--src/3rdparty/masm/yarr/create_regex_tables2
-rw-r--r--src/3rdparty/masm/yarr/generateYarrCanonicalizeUnicode16
-rw-r--r--src/imports/builtins/builtins.qmltypes24
-rw-r--r--src/imports/folderlistmodel/plugins.qmltypes276
-rw-r--r--src/imports/folderlistmodel/qquickfolderlistmodel.cpp4
-rw-r--r--src/imports/imports.pro5
-rw-r--r--src/imports/labsanimation/dependencies.json2
-rw-r--r--src/imports/labsanimation/labsanimation.pro11
-rw-r--r--src/imports/labsanimation/plugin.cpp81
-rw-r--r--src/imports/labsanimation/plugins.qmltypes36
-rw-r--r--src/imports/labsanimation/qmldir3
-rw-r--r--src/imports/labsmodels/labsmodels.pro2
-rw-r--r--src/imports/labsmodels/plugins.qmltypes373
-rw-r--r--src/imports/layouts/plugins.qmltypes2
-rw-r--r--src/imports/localstorage/plugin.cpp3
-rw-r--r--src/imports/localstorage/plugins.qmltypes2
-rw-r--r--src/imports/models/models.pro2
-rw-r--r--src/imports/models/plugins.qmltypes127
-rw-r--r--src/imports/particles/plugins.qmltypes60
-rw-r--r--src/imports/qtqml/dependencies.json2
-rw-r--r--src/imports/qtqml/plugin.cpp91
-rw-r--r--src/imports/qtqml/plugins.qmltypes48
-rw-r--r--src/imports/qtqml/qmldir2
-rw-r--r--src/imports/qtqml/qtqml.pro16
-rw-r--r--src/imports/qtquick2/plugin.cpp18
-rw-r--r--src/imports/qtquick2/plugins.qmltypes507
-rw-r--r--src/imports/qtquick2/qtquick2.pro4
-rw-r--r--src/imports/shapes/plugin.cpp3
-rw-r--r--src/imports/shapes/plugins.qmltypes10
-rw-r--r--src/imports/statemachine/plugins.qmltypes2
-rw-r--r--src/imports/statemachine/signaltransition.cpp7
-rw-r--r--src/imports/statemachine/signaltransition.h6
-rw-r--r--src/imports/statemachine/statemachine.cpp13
-rw-r--r--src/imports/statemachine/statemachine.h3
-rw-r--r--src/imports/testlib/main.cpp19
-rw-r--r--src/imports/testlib/plugins.qmltypes8
-rw-r--r--src/imports/wavefrontmesh/plugins.qmltypes2
-rw-r--r--src/imports/window/plugins.qmltypes28
-rw-r--r--src/imports/workerscript/dependencies.json2
-rw-r--r--src/imports/workerscript/plugin.cpp81
-rw-r--r--src/imports/workerscript/plugins.qmltypes26
-rw-r--r--src/imports/workerscript/qmldir3
-rw-r--r--src/imports/workerscript/workerscript.pro11
-rw-r--r--src/particles/particles.qrc11
-rw-r--r--src/particles/qquickcustomaffector.cpp25
-rw-r--r--src/particles/qquickcustomaffector_p.h4
-rw-r--r--src/particles/qquickcustomparticle.cpp2
-rw-r--r--src/particles/qquickimageparticle.cpp737
-rw-r--r--src/particles/qquickimageparticle_p.h29
-rw-r--r--src/particles/qquickitemparticle.cpp4
-rw-r--r--src/particles/qquickmaskextruder.cpp4
-rw-r--r--src/particles/qquickmaskextruder_p.h4
-rw-r--r--src/particles/qquickparticleemitter.cpp7
-rw-r--r--src/particles/qquickparticleemitter_p.h2
-rw-r--r--src/particles/qquickparticlepainter.cpp2
-rw-r--r--src/particles/qquickparticlepainter_p.h1
-rw-r--r--src/particles/qquickparticlesystem.cpp2
-rw-r--r--src/particles/qquickparticlesystem_p.h5
-rw-r--r--src/particles/qquicktrailemitter.cpp10
-rw-r--r--src/particles/qquicktrailemitter_p.h2
-rw-r--r--src/particles/qquickv4particledata.cpp6
-rw-r--r--src/particles/qquickv4particledata_p.h3
-rwxr-xr-xsrc/particles/shaders_ng/compile.bat53
-rw-r--r--src/particles/shaders_ng/imageparticle.frag55
-rw-r--r--src/particles/shaders_ng/imageparticle.vert145
-rw-r--r--src/particles/shaders_ng/imageparticle_colored.frag.qsbbin0 -> 1990 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_colored.vert.qsbbin0 -> 3677 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_deformed.frag.qsbbin0 -> 2028 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_deformed.vert.qsbbin0 -> 5044 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_simple.frag.qsbbin0 -> 2000 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_simple.vert.qsbbin0 -> 3639 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_sprite.frag.qsbbin0 -> 2369 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_sprite.vert.qsbbin0 -> 5964 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_tabled.frag.qsbbin0 -> 2240 bytes
-rw-r--r--src/particles/shaders_ng/imageparticle_tabled.vert.qsbbin0 -> 5462 bytes
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp67
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h3
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp7
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp1
-rw-r--r--src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp7
-rw-r--r--src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp3
-rw-r--r--src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.h1
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp4
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h6
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp4
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h2
-rw-r--r--src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp14
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12context.cpp3
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12context_p.h2
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12layer.cpp9
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12layer_p.h10
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp2
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h2
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp2
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp54
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h1
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp12
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12texture_p.h13
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp16
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvgcontext.cpp12
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h10
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.h1
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvglayer.cpp8
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvglayer.h10
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvgpainternode.cpp1
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp6
-rw-r--r--src/qml/common/common.pri35
-rw-r--r--src/qml/common/qqmlapiversion_p.h56
-rw-r--r--src/qml/common/qqmljsdiagnosticmessage_p.h92
-rw-r--r--src/qml/common/qqmljsfixedpoolarray_p.h140
-rw-r--r--src/qml/common/qqmljsmemorypool_p.h (renamed from src/qml/parser/qqmljsmemorypool_p.h)81
-rw-r--r--src/qml/common/qv4alloca_p.h (renamed from src/qml/jsruntime/qv4alloca_p.h)0
-rw-r--r--src/qml/common/qv4calldata_p.h (renamed from src/qml/jit/qv4jithelpers_p.h)89
-rw-r--r--src/qml/common/qv4compileddata_p.h (renamed from src/qml/compiler/qv4compileddata_p.h)563
-rw-r--r--src/qml/common/qv4staticvalue_p.h559
-rw-r--r--src/qml/common/qv4stringtoarrayindex_p.h96
-rw-r--r--src/qml/compiler/compiler.pri26
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp582
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h139
-rw-r--r--src/qml/compiler/qv4bytecodegenerator.cpp4
-rw-r--r--src/qml/compiler/qv4bytecodegenerator_p.h64
-rw-r--r--src/qml/compiler/qv4bytecodehandler.cpp7
-rw-r--r--src/qml/compiler/qv4bytecodehandler_p.h3
-rw-r--r--src/qml/compiler/qv4codegen.cpp546
-rw-r--r--src/qml/compiler/qv4codegen_p.h301
-rw-r--r--src/qml/compiler/qv4compiler.cpp79
-rw-r--r--src/qml/compiler/qv4compiler_p.h11
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp26
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h14
-rw-r--r--src/qml/compiler/qv4compilercontrolflow_p.h11
-rw-r--r--src/qml/compiler/qv4compilerglobal_p.h (renamed from src/qml/qmldirparser/qqmlsourcecoordinate_p.h)28
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp62
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h101
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp107
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h64
-rw-r--r--src/qml/compiler/qv4util_p.h (renamed from src/qml/jsruntime/qv4util_p.h)1
-rw-r--r--src/qml/configure.json94
-rw-r--r--src/qml/debugger/debugger.pri2
-rw-r--r--src/qml/debugger/qqmlconfigurabledebugservice_p.h4
-rw-r--r--src/qml/debugger/qqmlmemoryprofiler.cpp144
-rw-r--r--src/qml/debugger/qqmlprofiler_p.h15
-rw-r--r--src/qml/doc/images/cpp-qml-integration-flowchart.odgbin15028 -> 15311 bytes
-rw-r--r--src/qml/doc/images/cpp-qml-integration-flowchart.pngbin80498 -> 64865 bytes
-rw-r--r--src/qml/doc/qtqml.qdocconf10
-rw-r--r--src/qml/doc/snippets/qml/events.qml8
-rw-r--r--src/qml/doc/snippets/qml/qtbinding/functions-qml/MyItem.qml2
-rw-r--r--src/qml/doc/snippets/qml/qtbinding/functions-qml/main.cpp10
-rw-r--r--src/qml/doc/snippets/qml/qtbinding/signals-qml/MyItem.qml2
-rw-r--r--src/qml/doc/snippets/qml/statemachine/guardcondition.qml2
-rw-r--r--src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc33
-rw-r--r--src/qml/doc/src/qmlfunctions.qdoc140
-rw-r--r--src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc20
-rw-r--r--src/qml/jit/jit.pri2
-rw-r--r--src/qml/jit/qv4assemblercommon.cpp4
-rw-r--r--src/qml/jit/qv4assemblercommon_p.h11
-rw-r--r--src/qml/jit/qv4baselineassembler.cpp64
-rw-r--r--src/qml/jit/qv4baselineassembler_p.h4
-rw-r--r--src/qml/jit/qv4baselinejit.cpp298
-rw-r--r--src/qml/jit/qv4baselinejit_p.h63
-rw-r--r--src/qml/jit/qv4jithelpers.cpp174
-rw-r--r--src/qml/jsapi/qjsengine.cpp40
-rw-r--r--src/qml/jsapi/qjsengine.h3
-rw-r--r--src/qml/jsapi/qjsengine_p.h4
-rw-r--r--src/qml/jsapi/qjsvalue.cpp9
-rw-r--r--src/qml/jsruntime/jsruntime.pri47
-rw-r--r--src/qml/jsruntime/qv4argumentsobject.cpp61
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp15
-rw-r--r--src/qml/jsruntime/qv4booleanobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper.cpp (renamed from src/qml/compiler/qv4compilationunitmapper.cpp)2
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper_p.h (renamed from src/qml/compiler/qv4compilationunitmapper_p.h)0
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper_unix.cpp (renamed from src/qml/compiler/qv4compilationunitmapper_unix.cpp)4
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper_win.cpp (renamed from src/qml/compiler/qv4compilationunitmapper_win.cpp)4
-rw-r--r--src/qml/jsruntime/qv4context.cpp24
-rw-r--r--src/qml/jsruntime/qv4context_p.h5
-rw-r--r--src/qml/jsruntime/qv4dateobject.cpp23
-rw-r--r--src/qml/jsruntime/qv4dateobject_p.h2
-rw-r--r--src/qml/jsruntime/qv4debugging_p.h1
-rw-r--r--src/qml/jsruntime/qv4engine.cpp520
-rw-r--r--src/qml/jsruntime/qv4engine_p.h177
-rw-r--r--src/qml/jsruntime/qv4enginebase_p.h14
-rw-r--r--src/qml/jsruntime/qv4errorobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4executableallocator.cpp4
-rw-r--r--src/qml/jsruntime/qv4executableallocator_p.h2
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit.cpp (renamed from src/qml/compiler/qv4compileddata.cpp)607
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit_p.h333
-rw-r--r--src/qml/jsruntime/qv4function.cpp53
-rw-r--r--src/qml/jsruntime/qv4function_p.h61
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp13
-rw-r--r--src/qml/jsruntime/qv4functionobject_p.h14
-rw-r--r--src/qml/jsruntime/qv4generatorobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4global_p.h77
-rw-r--r--src/qml/jsruntime/qv4globalobject.cpp8
-rw-r--r--src/qml/jsruntime/qv4identifier.cpp22
-rw-r--r--src/qml/jsruntime/qv4identifiertable.cpp21
-rw-r--r--src/qml/jsruntime/qv4include.cpp30
-rw-r--r--src/qml/jsruntime/qv4internalclass.cpp101
-rw-r--r--src/qml/jsruntime/qv4internalclass_p.h4
-rw-r--r--src/qml/jsruntime/qv4lookup.cpp12
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h33
-rw-r--r--src/qml/jsruntime/qv4managed_p.h1
-rw-r--r--src/qml/jsruntime/qv4mapobject.cpp6
-rw-r--r--src/qml/jsruntime/qv4math_p.h40
-rw-r--r--src/qml/jsruntime/qv4module.cpp4
-rw-r--r--src/qml/jsruntime/qv4module_p.h4
-rw-r--r--src/qml/jsruntime/qv4object.cpp29
-rw-r--r--src/qml/jsruntime/qv4object_p.h22
-rw-r--r--src/qml/jsruntime/qv4objectproto.cpp8
-rw-r--r--src/qml/jsruntime/qv4objectproto_p.h2
-rw-r--r--src/qml/jsruntime/qv4profiling.cpp2
-rw-r--r--src/qml/jsruntime/qv4profiling_p.h20
-rw-r--r--src/qml/jsruntime/qv4promiseobject.cpp26
-rw-r--r--src/qml/jsruntime/qv4propertykey_p.h6
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp42
-rw-r--r--src/qml/jsruntime/qv4qmlcontext_p.h1
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp77
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h2
-rw-r--r--src/qml/jsruntime/qv4reflect.cpp2
-rw-r--r--src/qml/jsruntime/qv4regexpobject.cpp45
-rw-r--r--src/qml/jsruntime/qv4regexpobject_p.h15
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp557
-rw-r--r--src/qml/jsruntime/qv4runtime_p.h6
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h587
-rw-r--r--src/qml/jsruntime/qv4runtimecodegen.cpp16
-rw-r--r--src/qml/jsruntime/qv4runtimecodegen_p.h6
-rw-r--r--src/qml/jsruntime/qv4scopedvalue_p.h2
-rw-r--r--src/qml/jsruntime/qv4script.cpp38
-rw-r--r--src/qml/jsruntime/qv4script_p.h10
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp25
-rw-r--r--src/qml/jsruntime/qv4sequenceobject_p.h2
-rw-r--r--src/qml/jsruntime/qv4setobject.cpp6
-rw-r--r--src/qml/jsruntime/qv4stackframe_p.h56
-rw-r--r--src/qml/jsruntime/qv4string.cpp11
-rw-r--r--src/qml/jsruntime/qv4string_p.h47
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp17
-rw-r--r--src/qml/jsruntime/qv4typedarray.cpp46
-rw-r--r--src/qml/jsruntime/qv4value.cpp29
-rw-r--r--src/qml/jsruntime/qv4value_p.h568
-rw-r--r--src/qml/jsruntime/qv4variantobject.cpp1
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp341
-rw-r--r--src/qml/jsruntime/qv4vme_moth_p.h1
-rw-r--r--src/qml/jsruntime/qv4vtable_p.h6
-rw-r--r--src/qml/memory/memory.pri6
-rw-r--r--src/qml/memory/qv4heap_p.h4
-rw-r--r--src/qml/memory/qv4mm.cpp32
-rw-r--r--src/qml/memory/qv4writebarrier_p.h2
-rw-r--r--src/qml/parser/parser.pri2
-rw-r--r--src/qml/parser/qqmljs.g426
-rw-r--r--src/qml/parser/qqmljsast.cpp88
-rw-r--r--src/qml/parser/qqmljsast_p.h266
-rw-r--r--src/qml/parser/qqmljsastfwd_p.h7
-rw-r--r--src/qml/parser/qqmljsastvisitor_p.h11
-rw-r--r--src/qml/parser/qqmljsengine_p.h25
-rw-r--r--src/qml/parser/qqmljslexer.cpp123
-rw-r--r--src/qml/parser/qqmljslexer_p.h10
-rw-r--r--src/qml/qml.pro35
-rw-r--r--src/qml/qml/ftw/ftw.pri7
-rw-r--r--src/qml/qml/ftw/qflagpointer_p.h16
-rw-r--r--src/qml/qml/ftw/qhashedstring.cpp78
-rw-r--r--src/qml/qml/ftw/qhashedstring_p.h851
-rw-r--r--src/qml/qml/ftw/qlinkedstringhash_p.h238
-rw-r--r--src/qml/qml/ftw/qprimefornumbits_p.h80
-rw-r--r--src/qml/qml/ftw/qqmlrefcount_p.h29
-rw-r--r--src/qml/qml/ftw/qqmlthread.cpp3
-rw-r--r--src/qml/qml/ftw/qstringhash_p.h807
-rw-r--r--src/qml/qml/qml.pri63
-rw-r--r--src/qml/qml/qqml.cpp161
-rw-r--r--src/qml/qml/qqml.h58
-rw-r--r--src/qml/qml/qqmlapplicationengine.cpp36
-rw-r--r--src/qml/qml/qqmlapplicationengine.h1
-rw-r--r--src/qml/qml/qqmlapplicationengine_p.h6
-rw-r--r--src/qml/qml/qqmlbinding.cpp18
-rw-r--r--src/qml/qml/qqmlbinding_p.h3
-rw-r--r--src/qml/qml/qqmlboundsignal.cpp4
-rw-r--r--src/qml/qml/qqmlcomponent.cpp84
-rw-r--r--src/qml/qml/qqmlcomponent.h9
-rw-r--r--src/qml/qml/qqmlcomponent_p.h6
-rw-r--r--src/qml/qml/qqmlcontext.cpp51
-rw-r--r--src/qml/qml/qqmlcontext_p.h8
-rw-r--r--src/qml/qml/qqmlcustomparser.cpp9
-rw-r--r--src/qml/qml/qqmlcustomparser_p.h11
-rw-r--r--src/qml/qml/qqmldata_p.h8
-rw-r--r--src/qml/qml/qqmldatablob.cpp639
-rw-r--r--src/qml/qml/qqmldatablob_p.h259
-rw-r--r--src/qml/qml/qqmldelayedcallqueue.cpp2
-rw-r--r--src/qml/qml/qqmldirdata.cpp95
-rw-r--r--src/qml/qml/qqmldirdata_p.h86
-rw-r--r--src/qml/qml/qqmlengine.cpp279
-rw-r--r--src/qml/qml/qqmlengine.h9
-rw-r--r--src/qml/qml/qqmlengine_p.h46
-rw-r--r--src/qml/qml/qqmlenumdata_p.h66
-rw-r--r--src/qml/qml/qqmlenumvalue_p.h68
-rw-r--r--src/qml/qml/qqmlerror.cpp (renamed from src/qml/qmldirparser/qqmlerror.cpp)48
-rw-r--r--src/qml/qml/qqmlerror.h (renamed from src/qml/qmldirparser/qqmlerror.h)2
-rw-r--r--src/qml/qml/qqmlexpression.cpp10
-rw-r--r--src/qml/qml/qqmlglobal.cpp69
-rw-r--r--src/qml/qml/qqmlglobal_p.h23
-rw-r--r--src/qml/qml/qqmlimport.cpp253
-rw-r--r--src/qml/qml/qqmlimport_p.h7
-rw-r--r--src/qml/qml/qqmlincubator.cpp3
-rw-r--r--src/qml/qml/qqmlincubator_p.h2
-rw-r--r--src/qml/qml/qqmlinfo.cpp6
-rw-r--r--src/qml/qml/qqmlinfo.h1
-rw-r--r--src/qml/qml/qqmlirloader.cpp206
-rw-r--r--src/qml/qml/qqmlirloader_p.h82
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp9
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h6
-rw-r--r--src/qml/qml/qqmllist_p.h2
-rw-r--r--src/qml/qml/qqmllistwrapper.cpp1
-rw-r--r--src/qml/qml/qqmllocale.cpp2
-rw-r--r--src/qml/qml/qqmlloggingcategory_p.h2
-rw-r--r--src/qml/qml/qqmlmetaobject.cpp327
-rw-r--r--src/qml/qml/qqmlmetaobject_p.h187
-rw-r--r--src/qml/qml/qqmlmetatype.cpp2299
-rw-r--r--src/qml/qml/qqmlmetatype_p.h284
-rw-r--r--src/qml/qml/qqmlmetatypedata.cpp226
-rw-r--r--src/qml/qml/qqmlmetatypedata_p.h148
-rw-r--r--src/qml/qml/qqmlnotifier.cpp4
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp99
-rw-r--r--src/qml/qml/qqmlobjectcreator_p.h17
-rw-r--r--src/qml/qml/qqmlobjectorgadget.cpp60
-rw-r--r--src/qml/qml/qqmlobjectorgadget_p.h83
-rw-r--r--src/qml/qml/qqmlopenmetaobject.cpp1
-rw-r--r--src/qml/qml/qqmlprivate.h11
-rw-r--r--src/qml/qml/qqmlproperty.cpp24
-rw-r--r--src/qml/qml/qqmlproperty_p.h5
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp427
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h643
-rw-r--r--src/qml/qml/qqmlpropertycachecreator.cpp (renamed from src/qml/compiler/qqmlpropertycachecreator.cpp)33
-rw-r--r--src/qml/qml/qqmlpropertycachecreator_p.h (renamed from src/qml/compiler/qqmlpropertycachecreator_p.h)329
-rw-r--r--src/qml/qml/qqmlpropertycachemethodarguments_p.h77
-rw-r--r--src/qml/qml/qqmlpropertycachevector_p.h (renamed from src/qml/debugger/qqmlmemoryprofiler_p.h)106
-rw-r--r--src/qml/qml/qqmlpropertydata_p.h411
-rw-r--r--src/qml/qml/qqmlpropertyresolver.cpp92
-rw-r--r--src/qml/qml/qqmlpropertyresolver_p.h87
-rw-r--r--src/qml/qml/qqmlpropertyvalidator.cpp (renamed from src/qml/compiler/qqmlpropertyvalidator.cpp)175
-rw-r--r--src/qml/qml/qqmlpropertyvalidator_p.h (renamed from src/qml/compiler/qqmlpropertyvalidator_p.h)36
-rw-r--r--src/qml/qml/qqmlscriptblob.cpp263
-rw-r--r--src/qml/qml/qqmlscriptblob_p.h97
-rw-r--r--src/qml/qml/qqmlscriptdata.cpp166
-rw-r--r--src/qml/qml/qqmlscriptdata_p.h108
-rw-r--r--src/qml/qml/qqmlsourcecoordinate_p.h95
-rw-r--r--src/qml/qml/qqmlstaticmetaobject.cpp51
-rw-r--r--src/qml/qml/qqmlstaticmetaobject_p.h68
-rw-r--r--src/qml/qml/qqmltype.cpp885
-rw-r--r--src/qml/qml/qqmltype_p.h196
-rw-r--r--src/qml/qml/qqmltype_p_p.h170
-rw-r--r--src/qml/qml/qqmltypecompiler.cpp (renamed from src/qml/compiler/qqmltypecompiler.cpp)259
-rw-r--r--src/qml/qml/qqmltypecompiler_p.h (renamed from src/qml/compiler/qqmltypecompiler_p.h)40
-rw-r--r--src/qml/qml/qqmltypedata.cpp842
-rw-r--r--src/qml/qml/qqmltypedata_p.h163
-rw-r--r--src/qml/qml/qqmltypeloader.cpp2284
-rw-r--r--src/qml/qml/qqmltypeloader_p.h434
-rw-r--r--src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp74
-rw-r--r--src/qml/qml/qqmltypeloadernetworkreplyproxy_p.h86
-rw-r--r--src/qml/qml/qqmltypeloaderqmldircontent.cpp121
-rw-r--r--src/qml/qml/qqmltypeloaderqmldircontent_p.h95
-rw-r--r--src/qml/qml/qqmltypeloaderthread.cpp198
-rw-r--r--src/qml/qml/qqmltypeloaderthread_p.h110
-rw-r--r--src/qml/qml/qqmltypemodule.cpp174
-rw-r--r--src/qml/qml/qqmltypemodule_p.h102
-rw-r--r--src/qml/qml/qqmltypemodule_p_p.h89
-rw-r--r--src/qml/qml/qqmltypemoduleversion.cpp95
-rw-r--r--src/qml/qml/qqmltypemoduleversion_p.h87
-rw-r--r--src/qml/qml/qqmltypenamecache_p.h3
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp244
-rw-r--r--src/qml/qml/qqmltypewrapper_p.h6
-rw-r--r--src/qml/qml/qqmlvaluetype.cpp61
-rw-r--r--src/qml/qml/qqmlvaluetype_p.h3
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp2
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp385
-rw-r--r--src/qml/qml/qqmlvmemetaobject_p.h6
-rw-r--r--src/qml/qml/qqmlxmlhttprequest.cpp24
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp67
-rw-r--r--src/qml/qml/v8/qv8engine.cpp331
-rw-r--r--src/qml/qml/v8/qv8engine_p.h243
-rw-r--r--src/qml/qml/v8/v8.pri2
-rw-r--r--src/qml/qmldirparser/qmldirparser.pri7
-rw-r--r--src/qml/qmldirparser/qqmldirparser.cpp36
-rw-r--r--src/qml/qmldirparser/qqmldirparser_p.h12
-rw-r--r--src/qml/qtqmlcompilerglobal.h58
-rw-r--r--src/qml/qtqmlcompilerglobal_p.h59
-rw-r--r--src/qml/qtqmlglobal.h4
-rw-r--r--src/qml/qtqmlglobal_p.h3
-rw-r--r--src/qml/types/qqmlbind.cpp198
-rw-r--r--src/qml/types/qqmlbind_p.h27
-rw-r--r--src/qml/types/qqmlconnections.cpp81
-rw-r--r--src/qml/types/qqmlconnections_p.h12
-rw-r--r--src/qml/types/types.pri45
-rw-r--r--src/qml/util/util.pri14
-rw-r--r--src/qmldebug/qqmlenginedebugclient.cpp132
-rw-r--r--src/qmldebug/qqmlenginedebugclient_p.h90
-rw-r--r--src/qmldebug/qqmlenginedebugclient_p_p.h2
-rw-r--r--src/qmldevtools/qmldevtools.pro6
-rw-r--r--src/qmlmodels/configure.json46
-rw-r--r--src/qmlmodels/doc/qtqmlmodels.qdocconf37
-rw-r--r--src/qmlmodels/doc/snippets/delegatemodel/delegatemodel.qml (renamed from src/qml/doc/snippets/delegatemodel/delegatemodel.qml)0
-rw-r--r--src/qmlmodels/doc/snippets/delegatemodel/delegatemodel_rootindex/main.cpp (renamed from src/qml/doc/snippets/delegatemodel/delegatemodel_rootindex/main.cpp)0
-rw-r--r--src/qmlmodels/doc/snippets/delegatemodel/delegatemodel_rootindex/view.qml (renamed from src/qml/doc/snippets/delegatemodel/delegatemodel_rootindex/view.qml)0
-rw-r--r--src/qmlmodels/doc/snippets/delegatemodel/delegatemodelgroup.qml (renamed from src/qml/doc/snippets/delegatemodel/delegatemodelgroup.qml)0
-rw-r--r--src/qmlmodels/doc/snippets/package/Delegate.qml (renamed from src/qml/doc/snippets/package/Delegate.qml)0
-rw-r--r--src/qmlmodels/doc/snippets/package/view.qml (renamed from src/qml/doc/snippets/package/view.qml)0
-rw-r--r--src/qmlmodels/doc/snippets/qml/listmodel/listelements.qml (renamed from src/qml/doc/snippets/qml/listmodel/listelements.qml)0
-rw-r--r--src/qmlmodels/doc/snippets/qml/listmodel/listmodel-modify.qml (renamed from src/qml/doc/snippets/qml/listmodel/listmodel-modify.qml)0
-rw-r--r--src/qmlmodels/doc/snippets/qml/listmodel/listmodel-nested.qml (renamed from src/qml/doc/snippets/qml/listmodel/listmodel-nested.qml)0
-rw-r--r--src/qmlmodels/doc/snippets/qml/listmodel/listmodel-simple.qml (renamed from src/qml/doc/snippets/qml/listmodel/listmodel-simple.qml)0
-rw-r--r--src/qmlmodels/doc/snippets/qml/listmodel/listmodel.qml (renamed from src/qml/doc/snippets/qml/listmodel/listmodel.qml)0
-rw-r--r--src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-complex.qml134
-rw-r--r--src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.qml130
-rw-r--r--src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.qml119
-rw-r--r--src/qmlmodels/qmlmodels.pro71
-rw-r--r--src/qmlmodels/qqmladaptormodel.cpp (renamed from src/qml/util/qqmladaptormodel.cpp)3
-rw-r--r--src/qmlmodels/qqmladaptormodel_p.h (renamed from src/qml/util/qqmladaptormodel_p.h)6
-rw-r--r--src/qmlmodels/qqmlchangeset.cpp (renamed from src/qml/util/qqmlchangeset.cpp)0
-rw-r--r--src/qmlmodels/qqmlchangeset_p.h (renamed from src/qml/util/qqmlchangeset_p.h)8
-rw-r--r--src/qmlmodels/qqmldelegatecomponent.cpp (renamed from src/qml/types/qqmldelegatecomponent.cpp)2
-rw-r--r--src/qmlmodels/qqmldelegatecomponent_p.h (renamed from src/qml/types/qqmldelegatecomponent_p.h)8
-rw-r--r--src/qmlmodels/qqmldelegatemodel.cpp (renamed from src/qml/types/qqmldelegatemodel.cpp)51
-rw-r--r--src/qmlmodels/qqmldelegatemodel_p.h (renamed from src/qml/types/qqmldelegatemodel_p.h)18
-rw-r--r--src/qmlmodels/qqmldelegatemodel_p_p.h (renamed from src/qml/types/qqmldelegatemodel_p_p.h)6
-rw-r--r--src/qmlmodels/qqmlinstantiator.cpp (renamed from src/qml/types/qqmlinstantiator.cpp)9
-rw-r--r--src/qmlmodels/qqmlinstantiator_p.h (renamed from src/qml/types/qqmlinstantiator_p.h)6
-rw-r--r--src/qmlmodels/qqmlinstantiator_p_p.h (renamed from src/qml/types/qqmlinstantiator_p_p.h)4
-rw-r--r--src/qmlmodels/qqmlitemmodels.qdoc (renamed from src/qml/types/qqmlitemmodels.qdoc)2
-rw-r--r--src/qmlmodels/qqmlitemselectionmodel.qdoc (renamed from src/qml/types/qqmlitemselectionmodel.qdoc)0
-rw-r--r--src/qmlmodels/qqmllistaccessor.cpp (renamed from src/qml/util/qqmllistaccessor.cpp)0
-rw-r--r--src/qmlmodels/qqmllistaccessor_p.h (renamed from src/qml/util/qqmllistaccessor_p.h)0
-rw-r--r--src/qmlmodels/qqmllistcompositor.cpp (renamed from src/qml/util/qqmllistcompositor.cpp)0
-rw-r--r--src/qmlmodels/qqmllistcompositor_p.h (renamed from src/qml/util/qqmllistcompositor_p.h)0
-rw-r--r--src/qmlmodels/qqmllistmodel.cpp (renamed from src/qml/types/qqmllistmodel.cpp)62
-rw-r--r--src/qmlmodels/qqmllistmodel_p.h (renamed from src/qml/types/qqmllistmodel_p.h)21
-rw-r--r--src/qmlmodels/qqmllistmodel_p_p.h (renamed from src/qml/types/qqmllistmodel_p_p.h)5
-rw-r--r--src/qmlmodels/qqmllistmodelworkeragent.cpp (renamed from src/qml/types/qqmllistmodelworkeragent.cpp)14
-rw-r--r--src/qmlmodels/qqmllistmodelworkeragent_p.h (renamed from src/qml/types/qqmllistmodelworkeragent_p.h)38
-rw-r--r--src/qmlmodels/qqmlmodelsmodule.cpp (renamed from src/qml/types/qqmlmodelsmodule.cpp)59
-rw-r--r--src/qmlmodels/qqmlmodelsmodule_p.h (renamed from src/qml/types/qqmlmodelsmodule_p.h)9
-rw-r--r--src/qmlmodels/qqmlobjectmodel.cpp (renamed from src/qml/types/qqmlobjectmodel.cpp)4
-rw-r--r--src/qmlmodels/qqmlobjectmodel_p.h (renamed from src/qml/types/qqmlobjectmodel_p.h)13
-rw-r--r--src/qmlmodels/qqmltableinstancemodel.cpp (renamed from src/qml/types/qqmltableinstancemodel.cpp)2
-rw-r--r--src/qmlmodels/qqmltableinstancemodel_p.h (renamed from src/qml/types/qqmltableinstancemodel_p.h)10
-rw-r--r--src/qmlmodels/qqmltablemodel.cpp1059
-rw-r--r--src/qmlmodels/qqmltablemodel_p.h172
-rw-r--r--src/qmlmodels/qqmltablemodelcolumn.cpp200
-rw-r--r--src/qmlmodels/qqmltablemodelcolumn_p.h226
-rw-r--r--src/qmlmodels/qquickpackage.cpp (renamed from src/qml/types/qquickpackage.cpp)7
-rw-r--r--src/qmlmodels/qquickpackage_p.h (renamed from src/qml/types/qquickpackage_p.h)3
-rw-r--r--src/qmlmodels/qtqmlmodelsglobal.h59
-rw-r--r--src/qmlmodels/qtqmlmodelsglobal_p.h61
-rw-r--r--src/qmltest/quicktest.cpp23
-rw-r--r--src/qmltest/quicktestevent.cpp3
-rw-r--r--src/qmltest/quicktestresult.cpp11
-rw-r--r--src/qmltest/quicktestresult_p.h1
-rw-r--r--src/qmlworkerscript/doc/qtqmlworkerscript.qdocconf37
-rw-r--r--src/qmlworkerscript/qmlworkerscript.pro22
-rw-r--r--src/qmlworkerscript/qqmlworkerscriptmodule.cpp62
-rw-r--r--src/qmlworkerscript/qqmlworkerscriptmodule_p.h69
-rw-r--r--src/qmlworkerscript/qquickworkerscript.cpp (renamed from src/qml/types/qquickworkerscript.cpp)120
-rw-r--r--src/qmlworkerscript/qquickworkerscript_p.h (renamed from src/qml/types/qquickworkerscript_p.h)3
-rw-r--r--src/qmlworkerscript/qtqmlworkerscriptglobal.h58
-rw-r--r--src/qmlworkerscript/qtqmlworkerscriptglobal_p.h60
-rw-r--r--src/qmlworkerscript/qv4serialize.cpp (renamed from src/qml/jsruntime/qv4serialize.cpp)76
-rw-r--r--src/qmlworkerscript/qv4serialize_p.h (renamed from src/qml/jsruntime/qv4serialize_p.h)0
-rw-r--r--src/quick/configure.json17
-rw-r--r--src/quick/designer/qqmldesignermetaobject.cpp5
-rw-r--r--src/quick/designer/qquickdesignercustomparserobject.cpp4
-rw-r--r--src/quick/designer/qquickdesignercustomparserobject_p.h4
-rw-r--r--src/quick/designer/qquickdesignerwindowmanager.cpp11
-rw-r--r--src/quick/designer/qquickdesignerwindowmanager_p.h5
-rw-r--r--src/quick/doc/qtquick.qdocconf2
-rw-r--r--src/quick/doc/snippets/cmake-macros/examples.cmake6
-rw-r--r--src/quick/doc/snippets/pointerHandlers/handlerFlick.qml88
-rw-r--r--src/quick/doc/snippets/pointerHandlers/wheelHandler.qml63
-rw-r--r--src/quick/doc/snippets/qml/boundaryRule.qml74
-rw-r--r--src/quick/doc/snippets/qml/qml-extending-types/signals/Button.qml2
-rw-r--r--src/quick/doc/snippets/qml/regularexpression.qml56
-rw-r--r--src/quick/doc/snippets/qml/tableview/cpp-tablemodel.cpp (renamed from src/quick/doc/snippets/qml/tableview/tablemodel.cpp)0
-rw-r--r--src/quick/doc/snippets/qml/tableview/cpp-tablemodel.qml (renamed from src/quick/doc/snippets/qml/tableview/tablemodel.qml)0
-rw-r--r--src/quick/doc/snippets/qml/tableview/qml-tablemodel.qml92
-rw-r--r--src/quick/doc/snippets/qml/texthandling.qml4
-rw-r--r--src/quick/doc/src/cmake-macros.qdoc56
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc58
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc338
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/topic.qdoc17
-rw-r--r--src/quick/doc/src/examples.qdoc7
-rw-r--r--src/quick/handlers/handlers.pri7
-rw-r--r--src/quick/handlers/qquickdraghandler.cpp45
-rw-r--r--src/quick/handlers/qquickdraghandler_p.h13
-rw-r--r--src/quick/handlers/qquickhoverhandler.cpp11
-rw-r--r--src/quick/handlers/qquickmultipointhandler.cpp170
-rw-r--r--src/quick/handlers/qquickmultipointhandler_p.h18
-rw-r--r--src/quick/handlers/qquickmultipointhandler_p_p.h83
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp58
-rw-r--r--src/quick/handlers/qquickpinchhandler_p.h1
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler.cpp3
-rw-r--r--src/quick/handlers/qquicktaphandler.cpp16
-rw-r--r--src/quick/handlers/qquickwheelhandler.cpp520
-rw-r--r--src/quick/handlers/qquickwheelhandler_p.h128
-rw-r--r--src/quick/handlers/qquickwheelhandler_p_p.h87
-rw-r--r--src/quick/items/context2d/qquickcanvascontext_p.h3
-rw-r--r--src/quick/items/context2d/qquickcanvasitem.cpp10
-rw-r--r--src/quick/items/context2d/qquickcanvasitem_p.h6
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp11
-rw-r--r--src/quick/items/context2d/qquickcontext2d_p.h2
-rw-r--r--src/quick/items/context2d/qquickcontext2dtexture.cpp13
-rw-r--r--src/quick/items/items.pri14
-rw-r--r--src/quick/items/qquickanimatedimage.cpp7
-rw-r--r--src/quick/items/qquickanimatedimage_p.h7
-rw-r--r--src/quick/items/qquickanimatedsprite.cpp2
-rw-r--r--src/quick/items/qquickanimatedsprite_p.h4
-rw-r--r--src/quick/items/qquickborderimage.cpp22
-rw-r--r--src/quick/items/qquickborderimage_p_p.h2
-rw-r--r--src/quick/items/qquickdrag.cpp4
-rw-r--r--src/quick/items/qquickdrag_p.h6
-rw-r--r--src/quick/items/qquickdroparea.cpp21
-rw-r--r--src/quick/items/qquickdroparea_p.h4
-rw-r--r--src/quick/items/qquickevents.cpp9
-rw-r--r--src/quick/items/qquickevents_p_p.h1
-rw-r--r--src/quick/items/qquickflickable.cpp9
-rw-r--r--src/quick/items/qquickflickablebehavior_p.h5
-rw-r--r--src/quick/items/qquickframebufferobject.cpp4
-rw-r--r--src/quick/items/qquickframebufferobject.h3
-rw-r--r--src/quick/items/qquickgenericshadereffect.cpp53
-rw-r--r--src/quick/items/qquickgenericshadereffect_p.h4
-rw-r--r--src/quick/items/qquickgraphicsinfo.cpp7
-rw-r--r--src/quick/items/qquickgraphicsinfo_p.h11
-rw-r--r--src/quick/items/qquickimage.cpp19
-rw-r--r--src/quick/items/qquickimage_p.h8
-rw-r--r--src/quick/items/qquickimagebase.cpp47
-rw-r--r--src/quick/items/qquickimagebase_p.h9
-rw-r--r--src/quick/items/qquickimagebase_p_p.h6
-rw-r--r--src/quick/items/qquickitem.cpp55
-rw-r--r--src/quick/items/qquickitem.h4
-rw-r--r--src/quick/items/qquickitem_p.h5
-rw-r--r--src/quick/items/qquickitemsmodule.cpp93
-rw-r--r--src/quick/items/qquickitemview.cpp38
-rw-r--r--src/quick/items/qquickitemview_p.h4
-rw-r--r--src/quick/items/qquickitemview_p_p.h8
-rw-r--r--src/quick/items/qquicklistview_p.h8
-rw-r--r--src/quick/items/qquickmousearea.cpp30
-rw-r--r--src/quick/items/qquickmousearea_p.h12
-rw-r--r--src/quick/items/qquickmousearea_p_p.h2
-rw-r--r--src/quick/items/qquickmultipointtoucharea.cpp4
-rw-r--r--src/quick/items/qquickopenglshadereffect.cpp12
-rw-r--r--src/quick/items/qquickopenglshadereffectnode.cpp1
-rw-r--r--src/quick/items/qquickpathview.cpp179
-rw-r--r--src/quick/items/qquickpincharea_p.h4
-rw-r--r--src/quick/items/qquickrectangle.cpp26
-rw-r--r--src/quick/items/qquickrendercontrol.cpp16
-rw-r--r--src/quick/items/qquickrepeater.cpp1
-rw-r--r--src/quick/items/qquickscreen_p.h8
-rw-r--r--src/quick/items/qquickshadereffect.cpp18
-rw-r--r--src/quick/items/qquickshadereffect_p.h2
-rw-r--r--src/quick/items/qquickshadereffectsource.cpp2
-rw-r--r--src/quick/items/qquickshadereffectsource_p.h4
-rw-r--r--src/quick/items/qquickspriteengine.cpp12
-rw-r--r--src/quick/items/qquickspriteengine_p.h4
-rw-r--r--src/quick/items/qquickspritesequence_p.h1
-rw-r--r--src/quick/items/qquickspritesequence_p_p.h3
-rw-r--r--src/quick/items/qquickstateoperations.cpp12
-rw-r--r--src/quick/items/qquickstateoperations_p.h12
-rw-r--r--src/quick/items/qquicktableview.cpp1122
-rw-r--r--src/quick/items/qquicktableview_p.h19
-rw-r--r--src/quick/items/qquicktableview_p_p.h65
-rw-r--r--src/quick/items/qquicktext.cpp16
-rw-r--r--src/quick/items/qquicktext_p.h1
-rw-r--r--src/quick/items/qquicktext_p_p.h1
-rw-r--r--src/quick/items/qquicktextcontrol.cpp46
-rw-r--r--src/quick/items/qquicktextcontrol_p.h9
-rw-r--r--src/quick/items/qquicktextcontrol_p_p.h3
-rw-r--r--src/quick/items/qquicktextdocument.cpp8
-rw-r--r--src/quick/items/qquicktextdocument_p.h3
-rw-r--r--src/quick/items/qquicktextedit.cpp31
-rw-r--r--src/quick/items/qquicktextedit_p.h4
-rw-r--r--src/quick/items/qquicktextedit_p_p.h3
-rw-r--r--src/quick/items/qquicktextinput.cpp9
-rw-r--r--src/quick/items/qquicktextinput_p.h8
-rw-r--r--src/quick/items/qquicktextnode.cpp2
-rw-r--r--src/quick/items/qquicktextnodeengine.cpp13
-rw-r--r--src/quick/items/qquickview.cpp21
-rw-r--r--src/quick/items/qquickview.h1
-rw-r--r--src/quick/items/qquickview_p.h2
-rw-r--r--src/quick/items/qquickwindow.cpp627
-rw-r--r--src/quick/items/qquickwindow.h20
-rw-r--r--src/quick/items/qquickwindow_p.h26
-rw-r--r--src/quick/items/qquickwindowmodule.cpp9
-rw-r--r--src/quick/items/qquickwindowmodule_p.h4
-rw-r--r--src/quick/qtquick2.cpp1
-rw-r--r--src/quick/quick.pro2
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp5
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h4
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp3
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h2
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp8
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h10
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp11
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h11
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp1
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp2
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp2
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp16
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp16
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h6
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp179
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h23
-rw-r--r--src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp73
-rw-r--r--src/quick/scenegraph/coreapi/qsgabstractrenderer.h10
-rw-r--r--src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h1
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp2749
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h306
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.cpp5
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.h6
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterial.cpp572
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterial.h98
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp630
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialrhishader.h182
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialrhishader_p.h114
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader.cpp556
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader.h136
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader_p.h8
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialtype.h51
-rw-r--r--src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp365
-rw-r--r--src/quick/scenegraph/coreapi/qsgopenglvisualizer_p.h91
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer.cpp31
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer_p.h55
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendererinterface.cpp106
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendererinterface.h21
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendernode.cpp103
-rw-r--r--src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp929
-rw-r--r--src/quick/scenegraph/coreapi/qsgrhivisualizer_p.h233
-rw-r--r--src/quick/scenegraph/coreapi/qsgtexture.cpp (renamed from src/quick/scenegraph/util/qsgtexture.cpp)336
-rw-r--r--src/quick/scenegraph/coreapi/qsgtexture.h (renamed from src/quick/scenegraph/util/qsgtexture.h)21
-rw-r--r--src/quick/scenegraph/coreapi/qsgtexture_p.h109
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer.cpp29
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h42
-rw-r--r--src/quick/scenegraph/qsgcontext.cpp55
-rw-r--r--src/quick/scenegraph/qsgcontext_p.h29
-rw-r--r--src/quick/scenegraph/qsgdefaultcontext.cpp114
-rw-r--r--src/quick/scenegraph/qsgdefaultcontext_p.h6
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode.cpp13
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p.cpp493
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p.h6
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p_p.h23
-rw-r--r--src/quick/scenegraph/qsgdefaultinternalimagenode.cpp61
-rw-r--r--src/quick/scenegraph/qsgdefaultinternalimagenode_p.h5
-rw-r--r--src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp52
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext.cpp200
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext_p.h82
-rw-r--r--src/quick/scenegraph/qsgdefaultspritenode.cpp77
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode.cpp16
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp415
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode_p.h3
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h6
-rw-r--r--src/quick/scenegraph/qsgopengldistancefieldglyphcache.cpp (renamed from src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp)43
-rw-r--r--src/quick/scenegraph/qsgopengldistancefieldglyphcache_p.h (renamed from src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h)13
-rw-r--r--src/quick/scenegraph/qsgopengllayer.cpp (renamed from src/quick/scenegraph/qsgdefaultlayer.cpp)56
-rw-r--r--src/quick/scenegraph/qsgopengllayer_p.h (renamed from src/quick/scenegraph/qsgdefaultlayer_p.h)22
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp468
-rw-r--r--src/quick/scenegraph/qsgrenderloop_p.h13
-rw-r--r--src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp570
-rw-r--r--src/quick/scenegraph/qsgrhidistancefieldglyphcache_p.h125
-rw-r--r--src/quick/scenegraph/qsgrhilayer.cpp474
-rw-r--r--src/quick/scenegraph/qsgrhilayer_p.h143
-rw-r--r--src/quick/scenegraph/qsgrhishadereffectnode.cpp886
-rw-r--r--src/quick/scenegraph/qsgrhishadereffectnode_p.h163
-rw-r--r--src/quick/scenegraph/qsgrhisupport.cpp608
-rw-r--r--src/quick/scenegraph/qsgrhisupport_p.h171
-rw-r--r--src/quick/scenegraph/qsgrhitextureglyphcache.cpp272
-rw-r--r--src/quick/scenegraph/qsgrhitextureglyphcache_p.h101
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp555
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop_p.h2
-rw-r--r--src/quick/scenegraph/qsgwindowsrenderloop.cpp28
-rw-r--r--src/quick/scenegraph/scenegraph.pri58
-rw-r--r--src/quick/scenegraph/scenegraph.qrc52
-rw-r--r--src/quick/scenegraph/shaders_ng/24bittextmask.frag19
-rw-r--r--src/quick/scenegraph/shaders_ng/24bittextmask.frag.qsbbin0 -> 1873 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/32bitcolortext.frag18
-rw-r--r--src/quick/scenegraph/shaders_ng/32bitcolortext.frag.qsbbin0 -> 1754 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/8bittextmask.frag18
-rw-r--r--src/quick/scenegraph/shaders_ng/8bittextmask.frag.qsbbin0 -> 1753 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/8bittextmask_a.frag18
-rw-r--r--src/quick/scenegraph/shaders_ng/8bittextmask_a.frag.qsbbin0 -> 1280 bytes
-rwxr-xr-xsrc/quick/scenegraph/shaders_ng/compile.bat86
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag25
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag.qsbbin0 -> 2209 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert26
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert.qsbbin0 -> 2209 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag25
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag.qsbbin0 -> 2210 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag27
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag.qsbbin0 -> 2319 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert27
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert.qsbbin0 -> 2340 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag27
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag.qsbbin0 -> 2334 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldtext.frag20
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldtext.frag.qsbbin0 -> 1883 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldtext.vert22
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldtext.vert.qsbbin0 -> 2048 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag20
-rw-r--r--src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag.qsbbin0 -> 1883 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/flatcolor.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/flatcolor.frag.qsbbin0 -> 1149 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/flatcolor.vert15
-rw-r--r--src/quick/scenegraph/shaders_ng/flatcolor.vert.qsbbin0 -> 1597 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag40
-rw-r--r--src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag.qsbbin0 -> 3314 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert44
-rw-r--r--src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert.qsbbin0 -> 3675 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag40
-rw-r--r--src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag.qsbbin0 -> 3304 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag29
-rw-r--r--src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag.qsbbin0 -> 2411 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert37
-rw-r--r--src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert.qsbbin0 -> 3028 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag29
-rw-r--r--src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag.qsbbin0 -> 2412 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/opaquetexture.frag11
-rw-r--r--src/quick/scenegraph/shaders_ng/opaquetexture.frag.qsbbin0 -> 1173 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/opaquetexture.vert18
-rw-r--r--src/quick/scenegraph/shaders_ng/opaquetexture.vert.qsbbin0 -> 1791 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext.frag33
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext.frag.qsbbin0 -> 2549 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext.vert32
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext.vert.qsbbin0 -> 2728 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext_a.frag33
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext_a.frag.qsbbin0 -> 1859 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/shadereffect.frag16
-rw-r--r--src/quick/scenegraph/shaders_ng/shadereffect.frag.qsbbin0 -> 1622 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/shadereffect.vert19
-rw-r--r--src/quick/scenegraph/shaders_ng/shadereffect.vert.qsbbin0 -> 1860 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothcolor.frag9
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothcolor.frag.qsbbin0 -> 838 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothcolor.vert51
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothcolor.vert.qsbbin0 -> 3682 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothtexture.frag13
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothtexture.frag.qsbbin0 -> 1299 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothtexture.vert61
-rw-r--r--src/quick/scenegraph/shaders_ng/smoothtexture.vert.qsbbin0 -> 4350 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/sprite.frag22
-rw-r--r--src/quick/scenegraph/shaders_ng/sprite.frag.qsbbin0 -> 1908 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/sprite.vert29
-rw-r--r--src/quick/scenegraph/shaders_ng/sprite.vert.qsbbin0 -> 2437 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/stencilclip.frag8
-rw-r--r--src/quick/scenegraph/shaders_ng/stencilclip.frag.qsbbin0 -> 853 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/stencilclip.vert14
-rw-r--r--src/quick/scenegraph/shaders_ng/stencilclip.vert.qsbbin0 -> 1510 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext.frag26
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext.frag.qsbbin0 -> 2210 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext.vert26
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext.vert.qsbbin0 -> 2460 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext_a.frag26
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext_a.frag.qsbbin0 -> 1631 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/textmask.frag19
-rw-r--r--src/quick/scenegraph/shaders_ng/textmask.frag.qsbbin0 -> 1873 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/textmask.vert21
-rw-r--r--src/quick/scenegraph/shaders_ng/textmask.vert.qsbbin0 -> 2142 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/texture.frag16
-rw-r--r--src/quick/scenegraph/shaders_ng/texture.frag.qsbbin0 -> 1618 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/texture.vert19
-rw-r--r--src/quick/scenegraph/shaders_ng/texture.vert.qsbbin0 -> 1865 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/vertexcolor.frag9
-rw-r--r--src/quick/scenegraph/shaders_ng/vertexcolor.frag.qsbbin0 -> 851 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/vertexcolor.vert19
-rw-r--r--src/quick/scenegraph/shaders_ng/vertexcolor.vert.qsbbin0 -> 1837 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/visualization.frag19
-rw-r--r--src/quick/scenegraph/shaders_ng/visualization.frag.qsbbin0 -> 1877 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/visualization.vert30
-rw-r--r--src/quick/scenegraph/shaders_ng/visualization.vert.qsbbin0 -> 2099 bytes
-rw-r--r--src/quick/scenegraph/util/qsgdefaultpainternode.cpp24
-rw-r--r--src/quick/scenegraph/util/qsgdefaultpainternode_p.h11
-rw-r--r--src/quick/scenegraph/util/qsgdepthstencilbuffer.cpp4
-rw-r--r--src/quick/scenegraph/util/qsgengine.cpp25
-rw-r--r--src/quick/scenegraph/util/qsgengine.h2
-rw-r--r--src/quick/scenegraph/util/qsgflatcolormaterial.cpp55
-rw-r--r--src/quick/scenegraph/util/qsgopenglatlastexture.cpp (renamed from src/quick/scenegraph/util/qsgatlastexture.cpp)33
-rw-r--r--src/quick/scenegraph/util/qsgopenglatlastexture_p.h (renamed from src/quick/scenegraph/util/qsgatlastexture_p.h)10
-rw-r--r--src/quick/scenegraph/util/qsgplaintexture.cpp457
-rw-r--r--src/quick/scenegraph/util/qsgplaintexture_p.h (renamed from src/quick/scenegraph/util/qsgtexture_p.h)60
-rw-r--r--src/quick/scenegraph/util/qsgrhiatlastexture.cpp491
-rw-r--r--src/quick/scenegraph/util/qsgrhiatlastexture_p.h217
-rw-r--r--src/quick/scenegraph/util/qsgrhinativetextureimporter.cpp104
-rw-r--r--src/quick/scenegraph/util/qsgrhinativetextureimporter_p.h70
-rw-r--r--src/quick/scenegraph/util/qsgsimplematerial.cpp5
-rw-r--r--src/quick/scenegraph/util/qsgsimplerectnode.cpp8
-rw-r--r--src/quick/scenegraph/util/qsgsimpletexturenode.cpp8
-rw-r--r--src/quick/scenegraph/util/qsgtexturematerial.cpp120
-rw-r--r--src/quick/scenegraph/util/qsgtexturematerial_p.h23
-rw-r--r--src/quick/scenegraph/util/qsgvertexcolormaterial.cpp54
-rw-r--r--src/quick/util/qquickanimation.cpp2
-rw-r--r--src/quick/util/qquickanimatorjob.cpp2
-rw-r--r--src/quick/util/qquickbehavior.cpp2
-rw-r--r--src/quick/util/qquickboundaryrule.cpp574
-rw-r--r--src/quick/util/qquickboundaryrule_p.h145
-rw-r--r--src/quick/util/qquickglobal.cpp7
-rw-r--r--src/quick/util/qquickimageprovider_p.h2
-rw-r--r--src/quick/util/qquickpath.cpp353
-rw-r--r--src/quick/util/qquickpath_p.h58
-rw-r--r--src/quick/util/qquickpath_p_p.h1
-rw-r--r--src/quick/util/qquickpixmapcache.cpp119
-rw-r--r--src/quick/util/qquickpixmapcache_p.h5
-rw-r--r--src/quick/util/qquickpropertychanges.cpp99
-rw-r--r--src/quick/util/qquickpropertychanges_p.h6
-rw-r--r--src/quick/util/qquickshortcut.cpp2
-rw-r--r--src/quick/util/qquickshortcut_p.h4
-rw-r--r--src/quick/util/qquickstate.cpp60
-rw-r--r--src/quick/util/qquickstate_p.h8
-rw-r--r--src/quick/util/qquickstate_p_p.h10
-rw-r--r--src/quick/util/qquickstategroup.cpp7
-rw-r--r--src/quick/util/qquickutilmodule.cpp9
-rw-r--r--src/quick/util/qquickvalidator.cpp46
-rw-r--r--src/quick/util/qquickvalidator_p.h3
-rw-r--r--src/quick/util/qquickvaluetypes.cpp5
-rw-r--r--src/quick/util/qquickvaluetypes_p.h2
-rw-r--r--src/quick/util/util.pri2
-rw-r--r--src/quickshapes/qquickshape.cpp170
-rw-r--r--src/quickshapes/qquickshape_p.h1
-rw-r--r--src/quickshapes/qquickshape_p_p.h65
-rw-r--r--src/quickshapes/qquickshapegenericrenderer.cpp307
-rw-r--r--src/quickshapes/qquickshapegenericrenderer_p.h109
-rw-r--r--src/quickshapes/qquickshapenvprrenderer.cpp14
-rw-r--r--src/quickshapes/qtquickshapes.qrc6
-rwxr-xr-xsrc/quickshapes/shaders_ng/compile.bat45
-rw-r--r--src/quickshapes/shaders_ng/conicalgradient.frag25
-rw-r--r--src/quickshapes/shaders_ng/conicalgradient.frag.qsbbin0 -> 2228 bytes
-rw-r--r--src/quickshapes/shaders_ng/conicalgradient.vert21
-rw-r--r--src/quickshapes/shaders_ng/conicalgradient.vert.qsbbin0 -> 1994 bytes
-rw-r--r--src/quickshapes/shaders_ng/lineargradient.frag18
-rw-r--r--src/quickshapes/shaders_ng/lineargradient.frag.qsbbin0 -> 1762 bytes
-rw-r--r--src/quickshapes/shaders_ng/lineargradient.vert22
-rw-r--r--src/quickshapes/shaders_ng/lineargradient.vert.qsbbin0 -> 2225 bytes
-rw-r--r--src/quickshapes/shaders_ng/radialgradient.frag32
-rw-r--r--src/quickshapes/shaders_ng/radialgradient.frag.qsbbin0 -> 2931 bytes
-rw-r--r--src/quickshapes/shaders_ng/radialgradient.vert23
-rw-r--r--src/quickshapes/shaders_ng/radialgradient.vert.qsbbin0 -> 2115 bytes
-rw-r--r--src/quickwidgets/qquickwidget.cpp26
-rw-r--r--src/quickwidgets/qquickwidget.h2
-rw-r--r--src/quickwidgets/qquickwidget_p.h1
-rw-r--r--src/src.pro13
-rw-r--r--sync.profile4
-rw-r--r--tests/auto/cmake/CMakeLists.txt12
-rw-r--r--tests/auto/cmake/qmlimportscanner/CMakeLists.txt18
-rw-r--r--tests/auto/cmake/qmlimportscanner/main.cpp53
-rw-r--r--tests/auto/cmake/qmlimportscanner/main.qml5
-rw-r--r--tests/auto/cmake/qmlimportscanner/qis_test.qrc6
-rw-r--r--tests/auto/qml/bindingdependencyapi/dummy_imports.qml8
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/data/quit.js4
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/data/quitInJS.qml41
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp52
-rw-r--r--tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp46
-rw-r--r--tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp1
-rw-r--r--tests/auto/qml/ecmascripttests/TestExpectations2
-rw-r--r--tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp2
-rw-r--r--tests/auto/qml/parserstress/dummy_imports.qml8
-rw-r--r--tests/auto/qml/parserstress/tst_parserstress.cpp3
-rw-r--r--tests/auto/qml/qjsengine/dummy_imports.qml8
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp301
-rw-r--r--tests/auto/qml/qjsvalue/tst_qjsvalue.cpp111
-rw-r--r--tests/auto/qml/qjsvalue/tst_qjsvalue.h1
-rw-r--r--tests/auto/qml/qml.pro5
-rw-r--r--tests/auto/qml/qmlcachegen/data/parameterAdjustment.qml7
-rw-r--r--tests/auto/qml/qmlcachegen/qmlcachegen.pro3
-rw-r--r--tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp11
-rw-r--r--tests/auto/qml/qmldiskcache/dummy_imports.qml8
-rw-r--r--tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp64
-rw-r--r--tests/auto/qml/qmllint/data/CatchStatement.qml8
-rw-r--r--tests/auto/qml/qmllint/data/FromRoot.qml17
-rw-r--r--tests/auto/qml/qmllint/data/IdFromOuterSpace.qml9
-rw-r--r--tests/auto/qml/qmllint/data/SignalHandler.qml13
-rw-r--r--tests/auto/qml/qmllint/data/WithStatement.qml13
-rw-r--r--tests/auto/qml/qmllint/data/catchIdentifierNoWarning.qml7
-rw-r--r--tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml8
-rw-r--r--tests/auto/qml/qmllint/data/spuriousParentWarning.qml7
-rw-r--r--tests/auto/qml/qmllint/main.cpp85
-rw-r--r--tests/auto/qml/qmllint/qmllint.pro13
-rw-r--r--tests/auto/qml/qmllint/tst_qmllint.cpp174
-rw-r--r--tests/auto/qml/qmlmin/tst_qmlmin.cpp5
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/Imports/Derived.qml6
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.pro2
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/Imports/plugins.qmltypes10
-rw-r--r--tests/auto/qml/qmlplugindump/data/dumper/Imports/qmldir1
-rw-r--r--tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp24
-rw-r--r--tests/auto/qml/qqmlbinding/data/MyComponent.qml2
-rw-r--r--tests/auto/qml/qqmlbinding/data/bindToQMLComponent.qml12
-rw-r--r--tests/auto/qml/qqmlbinding/data/nanPropertyToInt.qml14
-rw-r--r--tests/auto/qml/qqmlbinding/data/noUnexpectedStringConversion.qml30
-rw-r--r--tests/auto/qml/qqmlbinding/data/restoreBinding2.qml55
-rw-r--r--tests/auto/qml/qqmlbinding/data/restoreBinding3.qml56
-rw-r--r--tests/auto/qml/qqmlbinding/data/restoreBinding4.qml59
-rw-r--r--tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp156
-rw-r--r--tests/auto/qml/qqmlchangeset/qqmlchangeset.pro2
-rw-r--r--tests/auto/qml/qqmlcomponent/data/allJSONTypes.qml9
-rw-r--r--tests/auto/qml/qqmlcomponent/data/variantBasedInitialization.qml21
-rw-r--r--tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp111
-rw-r--r--tests/auto/qml/qqmlconnections/data/bindings/connection-no-signal-name.qml (renamed from tests/auto/qml/qqmlconnections/data/connection-no-signal-name.qml)0
-rw-r--r--tests/auto/qml/qqmlconnections/data/bindings/connection-targetchange.qml (renamed from tests/auto/qml/qqmlconnections/data/connection-targetchange.qml)0
-rw-r--r--tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals-ignored.qml (renamed from tests/auto/qml/qqmlconnections/data/connection-unknownsignals-ignored.qml)0
-rw-r--r--tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals-notarget.qml (renamed from tests/auto/qml/qqmlconnections/data/connection-unknownsignals-notarget.qml)0
-rw-r--r--tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals-parent.qml (renamed from tests/auto/qml/qqmlconnections/data/connection-unknownsignals-parent.qml)0
-rw-r--r--tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals.qml (renamed from tests/auto/qml/qqmlconnections/data/connection-unknownsignals.qml)0
-rw-r--r--tests/auto/qml/qqmlconnections/data/bindings/disabled-at-start.qml (renamed from tests/auto/qml/qqmlconnections/data/disabled-at-start.qml)0
-rw-r--r--tests/auto/qml/qqmlconnections/data/bindings/override-proxy-type.qml (renamed from tests/auto/qml/qqmlconnections/data/override-proxy-type.qml)0
-rw-r--r--tests/auto/qml/qqmlconnections/data/bindings/rewriteError-global.qml (renamed from tests/auto/qml/qqmlconnections/data/rewriteError-global.qml)0
-rw-r--r--tests/auto/qml/qqmlconnections/data/bindings/rewriteError-unnamed.qml (renamed from tests/auto/qml/qqmlconnections/data/rewriteError-unnamed.qml)0
-rw-r--r--tests/auto/qml/qqmlconnections/data/bindings/singletontype-target.qml (renamed from tests/auto/qml/qqmlconnections/data/singletontype-target.qml)0
-rw-r--r--tests/auto/qml/qqmlconnections/data/bindings/test-connection-implicit.qml (renamed from tests/auto/qml/qqmlconnections/data/test-connection-implicit.qml)0
-rw-r--r--tests/auto/qml/qqmlconnections/data/bindings/test-connection.qml (renamed from tests/auto/qml/qqmlconnections/data/test-connection.qml)0
-rw-r--r--tests/auto/qml/qqmlconnections/data/bindings/trimming.qml (renamed from tests/auto/qml/qqmlconnections/data/trimming.qml)0
-rw-r--r--tests/auto/qml/qqmlconnections/data/functions/connection-no-signal-name.qml15
-rw-r--r--tests/auto/qml/qqmlconnections/data/functions/connection-targetchange.qml25
-rw-r--r--tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-ignored.qml17
-rw-r--r--tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-notarget.qml9
-rw-r--r--tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-parent.qml8
-rw-r--r--tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals.qml11
-rw-r--r--tests/auto/qml/qqmlconnections/data/functions/disabled-at-start.qml14
-rw-r--r--tests/auto/qml/qqmlconnections/data/functions/override-proxy-type.qml13
-rw-r--r--tests/auto/qml/qqmlconnections/data/functions/rewriteError-global.qml8
-rw-r--r--tests/auto/qml/qqmlconnections/data/functions/rewriteError-unnamed.qml8
-rw-r--r--tests/auto/qml/qqmlconnections/data/functions/singletontype-target.qml22
-rw-r--r--tests/auto/qml/qqmlconnections/data/functions/test-connection-implicit.qml9
-rw-r--r--tests/auto/qml/qqmlconnections/data/functions/test-connection.qml14
-rw-r--r--tests/auto/qml/qqmlconnections/data/functions/trimming.qml13
-rw-r--r--tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp72
-rw-r--r--tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp26
-rw-r--r--tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp2
-rw-r--r--tests/auto/qml/qqmldirparser/qqmldirparser.pro2
-rw-r--r--tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp16
-rw-r--r--tests/auto/qml/qqmlecmascript/data/generatorFunction.qml22
-rw-r--r--tests/auto/qml/qqmlecmascript/data/regularExpression.2.qml7
-rw-r--r--tests/auto/qml/qqmlecmascript/data/regularExpression.qml7
-rw-r--r--tests/auto/qml/qqmlecmascript/data/semicolonAfterProperty.qml10
-rw-r--r--tests/auto/qml/qqmlecmascript/data/signalParameterTypes.qml1
-rw-r--r--tests/auto/qml/qqmlecmascript/testtypes.h26
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp177
-rw-r--r--tests/auto/qml/qqmlengine/qqmlengine.pro2
-rw-r--r--tests/auto/qml/qqmlengine/tst_qqmlengine.cpp86
-rw-r--r--tests/auto/qml/qqmlerror/tst_qqmlerror.cpp2
-rw-r--r--tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp7
-rw-r--r--tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp26
-rw-r--r--tests/auto/qml/qqmlimport/qqmlimport.pro5
-rw-r--r--tests/auto/qml/qqmlimport/tst_qqmlimport.cpp5
-rw-r--r--tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro2
-rw-r--r--tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp2
-rw-r--r--tests/auto/qml/qqmllanguage/data/alias.17.qml31
-rw-r--r--tests/auto/qml/qqmllanguage/data/bareQmlImport.errors.txt0
-rw-r--r--tests/auto/qml/qqmllanguage/data/bareQmlImport.qml6
-rw-r--r--tests/auto/qml/qqmllanguage/data/fakeDotProperty.errors.txt2
-rw-r--r--tests/auto/qml/qqmllanguage/data/functionParameterTypes.qml7
-rw-r--r--tests/auto/qml/qqmllanguage/data/fuzzed.2.errors.txt1
-rw-r--r--tests/auto/qml/qqmllanguage/data/fuzzed.2.qmlbin404 -> 404 bytes
-rw-r--r--tests/auto/qml/qqmllanguage/data/fuzzed.3.errors.txt2
-rw-r--r--tests/auto/qml/qqmllanguage/data/fuzzed.3.qmlbin0 -> 4777 bytes
-rw-r--r--tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.2.errors.txt2
-rw-r--r--tests/auto/qml/qqmllanguage/data/objectValueTypeProperty.errors.txt2
-rw-r--r--tests/auto/qml/qqmllanguage/data/propertyUnknownType.errors.txt1
-rw-r--r--tests/auto/qml/qqmllanguage/data/propertyUnknownType.qml4
-rw-r--r--tests/auto/qml/qqmllanguage/data/signalParameterTypes.3.qml19
-rw-r--r--tests/auto/qml/qqmllanguage/data/typeAnnotations.2.errors.txt1
-rw-r--r--tests/auto/qml/qqmllanguage/data/typeAnnotations.2.qml8
-rw-r--r--tests/auto/qml/qqmllanguage/qqmllanguage.pro2
-rw-r--r--tests/auto/qml/qqmllanguage/qqmllanguage.qrc5
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.cpp9
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.h31
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp306
-rw-r--r--tests/auto/qml/qqmllistcompositor/qqmllistcompositor.pro2
-rw-r--r--tests/auto/qml/qqmllistmodel/qqmllistmodel.pro2
-rw-r--r--tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp63
-rw-r--r--tests/auto/qml/qqmllistmodelworkerscript/qqmllistmodelworkerscript.pro2
-rw-r--r--tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp4
-rw-r--r--tests/auto/qml/qqmllocale/data/localeAsCppProperty.qml6
-rw-r--r--tests/auto/qml/qqmllocale/tst_qqmllocale.cpp22
-rw-r--r--tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp11
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/data/multiSingleton.qml6
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton.qml16
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton2.qml6
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/internal/InternalType.qml6
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/moduleWithQmlSingleton.pro18
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp50
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/qmldir2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro3
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp17
-rw-r--r--tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp3
-rw-r--r--tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro2
-rw-r--r--tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp4
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/anonfunctionexpr.js3
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classmemberparam.js6
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classreturnvalue.js6
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/function.js4
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionexpr.js3
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionparams.js3
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration.js3
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration2.js5
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/qmlnestedfunction.qml9
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/variables.js2
-rw-r--r--tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_bool.qml4
-rw-r--r--tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_double.qml4
-rw-r--r--tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_int.qml4
-rw-r--r--tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_real.qml4
-rw-r--r--tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_string.qml4
-rw-r--r--tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_url.qml4
-rw-r--r--tests/auto/qml/qqmlparser/data/typeannotations/parametrized.qml5
-rw-r--r--tests/auto/qml/qqmlparser/data/typeannotations/qmlfunction.qml6
-rw-r--r--tests/auto/qml/qqmlparser/qqmlparser.pro4
-rw-r--r--tests/auto/qml/qqmlparser/tst_qqmlparser.cpp143
-rw-r--r--tests/auto/qml/qqmlproperty/data/interfaceBinding.qml27
-rw-r--r--tests/auto/qml/qqmlproperty/data/nullPropertyBinding.qml22
-rw-r--r--tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp113
-rw-r--r--tests/auto/qml/qqmlpropertycache/data/SpecialObject1.qml5
-rw-r--r--tests/auto/qml/qqmlpropertycache/data/SpecialObject2.qml5
-rw-r--r--tests/auto/qml/qqmlpropertycache/data/noDuckType.qml7
-rw-r--r--tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp12
-rw-r--r--tests/auto/qml/qqmlpropertymap/dummy_imports.qml8
-rw-r--r--tests/auto/qml/qqmlpropertymap/qqmlpropertymap.pro2
-rw-r--r--tests/auto/qml/qqmlsqldatabase/dummy_imports.qml8
-rw-r--r--tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp2
-rw-r--r--tests/auto/qml/qqmltablemodel/data/TestModel.qml50
-rw-r--r--tests/auto/qml/qqmltablemodel/data/TestUtils.js45
-rw-r--r--tests/auto/qml/qqmltablemodel/data/common.qml149
-rw-r--r--tests/auto/qml/qqmltablemodel/data/complex.qml68
-rw-r--r--tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml55
-rw-r--r--tests/auto/qml/qqmltablemodel/data/empty.qml82
-rw-r--r--tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml47
-rw-r--r--tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml86
-rw-r--r--tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml84
-rw-r--r--tests/auto/qml/qqmltablemodel/qqmltablemodel.pro10
-rw-r--r--tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp980
-rw-r--r--tests/auto/qml/qqmltimer/dummy_imports.qml9
-rw-r--r--tests/auto/qml/qqmltranslation/data/mylibrary.js5
-rw-r--r--tests/auto/qml/qqmltranslation/data/nested_js_translation.js3
-rw-r--r--tests/auto/qml/qqmltranslation/data/preferjs.qml8
-rw-r--r--tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp30
-rw-r--r--tests/auto/qml/qqmltypeloader/data/Com/Orga/BaseStyle.qml5
-rw-r--r--tests/auto/qml/qqmltypeloader/data/Com/Orga/Handlers/Handler.qml7
-rw-r--r--tests/auto/qml/qqmltypeloader/data/Com/Orga/Handlers/qmldir2
-rw-r--r--tests/auto/qml/qqmltypeloader/data/Com/Orga/Style.qml6
-rw-r--r--tests/auto/qml/qqmltypeloader/data/Com/Orga/qmldir2
-rw-r--r--tests/auto/qml/qqmltypeloader/data/implicitimporttest.qml5
-rw-r--r--tests/auto/qml/qqmltypeloader/data/imports/modulewithimplicitimport/Test.qml3
-rw-r--r--tests/auto/qml/qqmltypeloader/data/imports/modulewithimplicitimport/qmldir2
-rw-r--r--tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp33
-rw-r--r--tests/auto/qml/qqmlvaluetypes/data/color_read.qml3
-rw-r--r--tests/auto/qml/qqmlvaluetypes/testtypes.h3
-rw-r--r--tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp34
-rw-r--r--tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp51
-rw-r--r--tests/auto/qml/qquickworkerscript/qquickworkerscript.pro2
-rw-r--r--tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp46
-rw-r--r--tests/auto/qml/qv4assembler/tst_qv4assembler.cpp14
-rw-r--r--tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp25
-rw-r--r--tests/auto/qml/qv4mm/tst_qv4mm.cpp2
-rw-r--r--tests/auto/qml/v4traced/tst_v4traced.cpp325
-rw-r--r--tests/auto/qml/v4traced/v4traced.pro5
-rw-r--r--tests/auto/qmltest/statemachine/tst_parallelmachine.qml32
-rw-r--r--tests/auto/quick/drawingmodes/tst_drawingmodes.cpp26
-rw-r--r--tests/auto/quick/examples/tst_examples.cpp1
-rw-r--r--tests/auto/quick/nodes/tst_nodestest.cpp7
-rw-r--r--tests/auto/quick/nokeywords/tst_nokeywords.cpp9
-rw-r--r--tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp1
-rw-r--r--tests/auto/quick/pointerhandlers/pointerhandlers.pro1
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml86
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro1
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp74
-rw-r--r--tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml55
-rw-r--r--tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml45
-rw-r--r--tests/auto/quick/pointerhandlers/qquickwheelhandler/qquickwheelhandler.pro14
-rw-r--r--tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp344
-rw-r--r--tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp6
-rw-r--r--tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp2
-rw-r--r--tests/auto/quick/qquickanchors/tst_qquickanchors.cpp4
-rw-r--r--tests/auto/quick/qquickanimatedimage/data/currentframe.qml13
-rw-r--r--tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp46
-rw-r--r--tests/auto/quick/qquickanimations/qquickanimations.pro2
-rw-r--r--tests/auto/quick/qquickanimations/tst_qquickanimations.cpp2
-rw-r--r--tests/auto/quick/qquickbehaviors/data/delete.qml37
-rw-r--r--tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp11
-rw-r--r--tests/auto/quick/qquickborderimage/data/multi.icobin0 -> 27110 bytes
-rw-r--r--tests/auto/quick/qquickborderimage/data/multiframe.qml8
-rw-r--r--tests/auto/quick/qquickborderimage/data/multiframeAsync.qml9
-rw-r--r--tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp65
-rw-r--r--tests/auto/quick/qquickboundaryrule/data/dragHandler.qml23
-rw-r--r--tests/auto/quick/qquickboundaryrule/qquickboundaryrule.pro12
-rw-r--r--tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp99
-rw-r--r--tests/auto/quick/qquickdrag/tst_qquickdrag.cpp8
-rw-r--r--tests/auto/quick/qquickdroparea/data/nested1.qml93
-rw-r--r--tests/auto/quick/qquickdroparea/data/nested2.qml93
-rw-r--r--tests/auto/quick/qquickdroparea/qquickdroparea.pro7
-rw-r--r--tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp88
-rw-r--r--tests/auto/quick/qquickflickable/tst_qquickflickable.cpp15
-rw-r--r--tests/auto/quick/qquickfontloader_static/tst_qquickfontloader_static.cpp4
-rw-r--r--tests/auto/quick/qquickgridview/qquickgridview.pro2
-rw-r--r--tests/auto/quick/qquickgridview/tst_qquickgridview.cpp10
-rw-r--r--tests/auto/quick/qquickimage/data/multi.icobin0 -> 27110 bytes
-rw-r--r--tests/auto/quick/qquickimage/data/multiframe.qml5
-rw-r--r--tests/auto/quick/qquickimage/data/multiframeAsync.qml6
-rw-r--r--tests/auto/quick/qquickimage/tst_qquickimage.cpp90
-rw-r--r--tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp181
-rw-r--r--tests/auto/quick/qquickitem/tst_qquickitem.cpp7
-rw-r--r--tests/auto/quick/qquicklistview/BLACKLIST4
-rw-r--r--tests/auto/quick/qquicklistview/qquicklistview.pro2
-rw-r--r--tests/auto/quick/qquicklistview/tst_qquicklistview.cpp14
-rw-r--r--tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp44
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp37
-rw-r--r--tests/auto/quick/qquickpath/qquickpath.pro1
-rw-r--r--tests/auto/quick/qquickpath/tst_qquickpath.cpp186
-rw-r--r--tests/auto/quick/qquickpathview/qquickpathview.pro2
-rw-r--r--tests/auto/quick/qquickpathview/tst_qquickpathview.cpp20
-rw-r--r--tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp8
-rw-r--r--tests/auto/quick/qquickrectangle/data/gradient-preset.qml18
-rw-r--r--tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp8
-rw-r--r--tests/auto/quick/qquickrepeater/qquickrepeater.pro2
-rw-r--r--tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp14
-rw-r--r--tests/auto/quick/qquickshape/BLACKLIST8
-rw-r--r--tests/auto/quick/qquickshape/data/multiline.pngbin0 -> 1244 bytes
-rw-r--r--tests/auto/quick/qquickshape/data/multiline.qml52
-rw-r--r--tests/auto/quick/qquickshape/data/multilineStronglyTyped.qml59
-rw-r--r--tests/auto/quick/qquickshape/data/pathitem7.qml67
-rw-r--r--tests/auto/quick/qquickshape/data/pathitem8.pngbin0 -> 7323 bytes
-rw-r--r--tests/auto/quick/qquickshape/data/pathitem8.qml88
-rw-r--r--tests/auto/quick/qquickshape/data/polyline.pngbin0 -> 757 bytes
-rw-r--r--tests/auto/quick/qquickshape/data/polyline.qml52
-rw-r--r--tests/auto/quick/qquickshape/qquickshape.pro1
-rw-r--r--tests/auto/quick/qquickshape/tst_qquickshape.cpp402
-rw-r--r--tests/auto/quick/qquickstates/data/trivialWhen.qml5
-rw-r--r--tests/auto/quick/qquickstates/tst_qquickstates.cpp10
-rw-r--r--tests/auto/quick/qquicktableview/data/syncviewsimple.qml122
-rw-r--r--tests/auto/quick/qquicktableview/qquicktableview.pro2
-rw-r--r--tests/auto/quick/qquicktableview/tst_qquicktableview.cpp624
-rw-r--r--tests/auto/quick/qquicktext/BLACKLIST6
-rw-r--r--tests/auto/quick/qquicktextedit/BLACKLIST2
-rw-r--r--tests/auto/quick/qquickview/tst_qquickview.cpp12
-rw-r--r--tests/auto/quick/qquickvisualdatamodel/qquickvisualdatamodel.pro2
-rw-r--r--tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp42
-rw-r--r--tests/auto/quick/qquickwindow/BLACKLIST3
-rw-r--r--tests/auto/quick/qquickwindow/data/shortcut.qml47
-rw-r--r--tests/auto/quick/qquickwindow/tst_qquickwindow.cpp28
-rw-r--r--tests/auto/quick/quick.pro1
-rw-r--r--tests/auto/quick/rendernode/tst_rendernode.cpp35
-rw-r--r--tests/auto/quick/scenegraph/data/render_bug37422.frag9
-rw-r--r--tests/auto/quick/scenegraph/data/render_bug37422.frag.qsbbin0 -> 864 bytes
-rw-r--r--tests/auto/quick/scenegraph/data/render_bug37422.qml6
-rw-r--r--tests/auto/quick/scenegraph/tst_scenegraph.cpp70
-rw-r--r--tests/auto/quick/shared/viewtestutil.cpp3
-rw-r--r--tests/auto/shared/testhttpserver.cpp7
-rw-r--r--tests/auto/shared/util.h7
-rw-r--r--tests/auto/toolsupport/tst_toolsupport.cpp1
-rw-r--r--tests/benchmarks/qml/js/qjsengine/tst_qjsengine.cpp4
-rw-r--r--tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp204
-rw-r--r--tests/benchmarks/qml/painting/paintbenchmark.cpp10
-rw-r--r--tests/libfuzzer/qml/jsapi/evaluate/evaluate.pro6
-rw-r--r--tests/libfuzzer/qml/jsapi/evaluate/main.cpp43
-rw-r--r--tests/manual/nodetypes_ng/AtlasedImages.qml101
-rw-r--r--tests/manual/nodetypes_ng/CompressedImages.qml78
-rw-r--r--tests/manual/nodetypes_ng/DistanceFieldText.qml84
-rw-r--r--tests/manual/nodetypes_ng/Images.qml128
-rw-r--r--tests/manual/nodetypes_ng/Layers.qml174
-rw-r--r--tests/manual/nodetypes_ng/LotsOfNodes.qml70
-rw-r--r--tests/manual/nodetypes_ng/LotsOfRects.qml260
-rw-r--r--tests/manual/nodetypes_ng/MoreWindows.qml105
-rw-r--r--tests/manual/nodetypes_ng/MultiClipRects.qml152
-rw-r--r--tests/manual/nodetypes_ng/Painter.qml94
-rw-r--r--tests/manual/nodetypes_ng/Rects.qml147
-rw-r--r--tests/manual/nodetypes_ng/ShaderEffect.qml174
-rw-r--r--tests/manual/nodetypes_ng/ShaderEffectNoAnim.qml85
-rw-r--r--tests/manual/nodetypes_ng/ShaderEffectSource.qml80
-rw-r--r--tests/manual/nodetypes_ng/SimpleRect.qml68
-rw-r--r--tests/manual/nodetypes_ng/Text.qml84
-rw-r--r--tests/manual/nodetypes_ng/arrow-down.pngbin0 -> 594 bytes
-rw-r--r--tests/manual/nodetypes_ng/arrow-up.pngbin0 -> 692 bytes
-rw-r--r--tests/manual/nodetypes_ng/blacknwhite.pngbin0 -> 156 bytes
-rwxr-xr-xtests/manual/nodetypes_ng/buildshaders.bat4
-rw-r--r--tests/manual/nodetypes_ng/car_etc2_nomips.ktxbin0 -> 11908 bytes
-rw-r--r--tests/manual/nodetypes_ng/face-smile.pngbin0 -> 15408 bytes
-rw-r--r--tests/manual/nodetypes_ng/main.qml102
-rw-r--r--tests/manual/nodetypes_ng/minus-sign.pngbin0 -> 250 bytes
-rw-r--r--tests/manual/nodetypes_ng/nodetypes_ng.cpp325
-rw-r--r--tests/manual/nodetypes_ng/nodetypes_ng.pro11
-rw-r--r--tests/manual/nodetypes_ng/nodetypes_ng.qrc38
-rw-r--r--tests/manual/nodetypes_ng/plus-sign.pngbin0 -> 462 bytes
-rw-r--r--tests/manual/nodetypes_ng/qt.pngbin0 -> 11917 bytes
-rw-r--r--tests/manual/nodetypes_ng/qt_bc1_10mips.ktxbin0 -> 174912 bytes
-rw-r--r--tests/manual/nodetypes_ng/shadow_pass1.frag23
-rw-r--r--tests/manual/nodetypes_ng/shadow_pass1.frag.qsbbin0 -> 2218 bytes
-rw-r--r--tests/manual/nodetypes_ng/shadow_pass1_legacy_gl.frag11
-rw-r--r--tests/manual/nodetypes_ng/shadow_pass2.frag23
-rw-r--r--tests/manual/nodetypes_ng/shadow_pass2.frag.qsbbin0 -> 2131 bytes
-rw-r--r--tests/manual/nodetypes_ng/shadow_pass2_legacy_gl.frag12
-rw-r--r--tests/manual/nodetypes_ng/wobble.frag20
-rw-r--r--tests/manual/nodetypes_ng/wobble.frag.qsbbin0 -> 2013 bytes
-rw-r--r--tests/manual/nodetypes_ng/wobble.vert22
-rw-r--r--tests/manual/nodetypes_ng/wobble.vert.qsbbin0 -> 2001 bytes
-rw-r--r--tests/manual/nodetypes_ng/wobble_legacy_gl.frag10
-rw-r--r--tests/manual/nodetypes_ng/wobble_legacy_gl.vert8
-rw-r--r--tests/manual/pointer/content/FakeFlickable.qml101
-rw-r--r--tests/manual/pointer/content/LeftDrawer.qml101
-rw-r--r--tests/manual/pointer/content/Slider.qml33
-rw-r--r--tests/manual/pointer/fakeFlickable.qml58
-rw-r--r--tests/manual/pointer/map.qml18
-rw-r--r--tests/manual/pointer/pinchAndWheel.qml160
-rw-r--r--tests/manual/scalablepath/ShapeTestScale.qml287
-rw-r--r--tests/manual/scalablepath/main.cpp42
-rw-r--r--tests/manual/scalablepath/main.qml42
-rw-r--r--tests/manual/scalablepath/qml.qrc6
-rw-r--r--tests/manual/scalablepath/scalablepath.pro5
-rw-r--r--tests/manual/scenegraph_lancelot/data/shape/shape_multiline.qml74
-rw-r--r--tests/manual/tableview/abstracttablemodel/Button.qml2
-rw-r--r--tests/manual/tableview/abstracttablemodel/main.qml156
-rw-r--r--tests/manual/tableview/tablemodel/form/RowForm.qml102
-rw-r--r--tests/manual/tableview/tablemodel/form/form.pro10
-rw-r--r--tests/manual/tableview/tablemodel/form/main.cpp52
-rw-r--r--tests/manual/tableview/tablemodel/form/main.qml290
-rw-r--r--tests/manual/tableview/tablemodel/json/JsonData.js186
-rw-r--r--tests/manual/tableview/tablemodel/json/json.pro12
-rw-r--r--tests/manual/tableview/tablemodel/json/main.cpp52
-rw-r--r--tests/manual/tableview/tablemodel/json/main.qml116
-rw-r--r--tests/manual/tableview/tablemodel/tablemodel.pro2
-rw-r--r--tools/qml/conf/content/resizeItemToWindow.qml70
-rw-r--r--tools/qml/conf/content/resizeWindowToItem.qml (renamed from tools/qml/conf/qtquick.qml)6
-rw-r--r--tools/qml/conf/default.qml (renamed from tools/qml/conf/configuration.qml)4
-rw-r--r--tools/qml/conf/resizeToItem.qml57
-rw-r--r--tools/qml/main.cpp389
-rw-r--r--tools/qml/qml.qrc6
-rw-r--r--tools/qmlcachegen/generateloader.cpp8
-rw-r--r--tools/qmlcachegen/qmlcachegen.cpp134
-rw-r--r--tools/qmlcachegen/qmlcachegen.pro8
-rw-r--r--tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in184
-rw-r--r--tools/qmlimportscanner/Qt5QmlImportScannerTemplate.cpp.in5
-rw-r--r--tools/qmlimportscanner/main.cpp69
-rw-r--r--tools/qmlimportscanner/qmlimportscanner.pro46
-rw-r--r--tools/qmljs/qmljs.cpp3
-rw-r--r--tools/qmllint/componentversion.cpp123
-rw-r--r--tools/qmllint/componentversion.h73
-rw-r--r--tools/qmllint/fakemetaobject.cpp600
-rw-r--r--tools/qmllint/fakemetaobject.h236
-rw-r--r--tools/qmllint/findunqualified.cpp783
-rw-r--r--tools/qmllint/findunqualified.h131
-rw-r--r--tools/qmllint/main.cpp44
-rw-r--r--tools/qmllint/qcoloroutput.cpp342
-rw-r--r--tools/qmllint/qcoloroutput_p.h110
-rw-r--r--tools/qmllint/qmljstypedescriptionreader.cpp704
-rw-r--r--tools/qmllint/qmljstypedescriptionreader.h103
-rw-r--r--tools/qmllint/qmllint.pro16
-rw-r--r--tools/qmllint/scopetree.cpp269
-rw-r--r--tools/qmllint/scopetree.h106
-rw-r--r--tools/qmlplugindump/main.cpp254
-rw-r--r--tools/qmlprofiler/qmlprofilerapplication.cpp4
-rw-r--r--tools/qmlprofiler/qmlprofilerdata.cpp1
-rw-r--r--tools/qmlscene/main.cpp51
-rw-r--r--tools/qmltime/qmltime.cpp4
-rw-r--r--tools/shared/resourcefilemapper.cpp (renamed from tools/qmlcachegen/resourcefilemapper.cpp)11
-rw-r--r--tools/shared/resourcefilemapper.h (renamed from tools/qmlcachegen/resourcefilemapper.h)6
-rw-r--r--tools/shared/shared.pri3
-rw-r--r--tools/tools.pro2
1439 files changed, 71725 insertions, 20671 deletions
diff --git a/.qmake.conf b/.qmake.conf
index 451a93ee59..076c5b7874 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,4 +1,7 @@
load(qt_build_config)
CONFIG += warning_clean
-MODULE_VERSION = 5.13.2
+DEFINES += QT_NO_LINKED_LIST
+DEFINES += QT_NO_JAVA_STYLE_ITERATORS
+
+MODULE_VERSION = 5.14.0
diff --git a/configure.json b/configure.json
index fd1f58863c..df68a1765e 100644
--- a/configure.json
+++ b/configure.json
@@ -1,6 +1,7 @@
{
"subconfigs": [
"src/qml",
+ "src/qmlmodels",
"src/quick"
]
}
diff --git a/examples/qml/referenceexamples/adding/main.cpp b/examples/qml/referenceexamples/adding/main.cpp
index 5c3c891130..e312149da1 100644
--- a/examples/qml/referenceexamples/adding/main.cpp
+++ b/examples/qml/referenceexamples/adding/main.cpp
@@ -62,7 +62,7 @@ int main(int argc, char ** argv)
QQmlEngine engine;
QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- Person *person = qobject_cast<Person *>(component.create());
+ auto *person = qobject_cast<Person *>(component.create());
if (person) {
qWarning() << "The person's name is" << person->name();
qWarning() << "They wear a" << person->shoeSize() << "sized shoe";
diff --git a/examples/qml/referenceexamples/adding/person.h b/examples/qml/referenceexamples/adding/person.h
index 44e2ac3b1b..f40c8d8086 100644
--- a/examples/qml/referenceexamples/adding/person.h
+++ b/examples/qml/referenceexamples/adding/person.h
@@ -58,7 +58,7 @@ class Person : public QObject
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize)
public:
- Person(QObject *parent = 0);
+ Person(QObject *parent = nullptr);
QString name() const;
void setName(const QString &);
diff --git a/examples/qml/referenceexamples/attached/birthdayparty.cpp b/examples/qml/referenceexamples/attached/birthdayparty.cpp
index 26aa56e86c..da0cb800fc 100644
--- a/examples/qml/referenceexamples/attached/birthdayparty.cpp
+++ b/examples/qml/referenceexamples/attached/birthdayparty.cpp
@@ -81,7 +81,7 @@ void BirthdayParty::setHost(Person *c)
QQmlListProperty<Person> BirthdayParty::guests()
{
- return QQmlListProperty<Person>(this, m_guests);
+ return {this, m_guests};
}
int BirthdayParty::guestCount() const
diff --git a/examples/qml/referenceexamples/attached/birthdayparty.h b/examples/qml/referenceexamples/attached/birthdayparty.h
index 0684f16255..15375f14d9 100644
--- a/examples/qml/referenceexamples/attached/birthdayparty.h
+++ b/examples/qml/referenceexamples/attached/birthdayparty.h
@@ -76,7 +76,7 @@ class BirthdayParty : public QObject
Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
Q_CLASSINFO("DefaultProperty", "guests")
public:
- BirthdayParty(QObject *parent = 0);
+ BirthdayParty(QObject *parent = nullptr);
Person *host() const;
void setHost(Person *);
diff --git a/examples/qml/referenceexamples/attached/main.cpp b/examples/qml/referenceexamples/attached/main.cpp
index f07f16ae0f..581b033dfc 100644
--- a/examples/qml/referenceexamples/attached/main.cpp
+++ b/examples/qml/referenceexamples/attached/main.cpp
@@ -67,7 +67,7 @@ int main(int argc, char ** argv)
QQmlEngine engine;
QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- BirthdayParty *party = qobject_cast<BirthdayParty *>(component.create());
+ auto *party = qobject_cast<BirthdayParty *>(component.create());
if (party && party->host()) {
qWarning() << party->host()->name() << "is having a birthday!";
diff --git a/examples/qml/referenceexamples/attached/person.h b/examples/qml/referenceexamples/attached/person.h
index 9b63773d49..2398da38bf 100644
--- a/examples/qml/referenceexamples/attached/person.h
+++ b/examples/qml/referenceexamples/attached/person.h
@@ -87,7 +87,7 @@ class Person : public QObject
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(ShoeDescription *shoe READ shoe)
public:
- Person(QObject *parent = 0);
+ Person(QObject *parent = nullptr);
QString name() const;
void setName(const QString &);
@@ -102,14 +102,14 @@ class Boy : public Person
{
Q_OBJECT
public:
- Boy(QObject * parent = 0);
+ Boy(QObject * parent = nullptr);
};
class Girl : public Person
{
Q_OBJECT
public:
- Girl(QObject * parent = 0);
+ Girl(QObject * parent = nullptr);
};
#endif // PERSON_H
diff --git a/examples/qml/referenceexamples/binding/birthdayparty.h b/examples/qml/referenceexamples/binding/birthdayparty.h
index 93dad927d7..15e1908ece 100644
--- a/examples/qml/referenceexamples/binding/birthdayparty.h
+++ b/examples/qml/referenceexamples/binding/birthdayparty.h
@@ -83,7 +83,7 @@ class BirthdayParty : public QObject
Q_PROPERTY(QString announcement READ announcement WRITE setAnnouncement)
Q_CLASSINFO("DefaultProperty", "guests")
public:
- BirthdayParty(QObject *parent = 0);
+ BirthdayParty(QObject *parent = nullptr);
Person *host() const;
void setHost(Person *);
diff --git a/examples/qml/referenceexamples/binding/happybirthdaysong.cpp b/examples/qml/referenceexamples/binding/happybirthdaysong.cpp
index fae016091b..5f8e6d696e 100644
--- a/examples/qml/referenceexamples/binding/happybirthdaysong.cpp
+++ b/examples/qml/referenceexamples/binding/happybirthdaysong.cpp
@@ -54,7 +54,7 @@ HappyBirthdaySong::HappyBirthdaySong(QObject *parent)
: QObject(parent), m_line(-1)
{
setName(QString());
- QTimer *timer = new QTimer(this);
+ auto *timer = new QTimer(this);
QObject::connect(timer, &QTimer::timeout, this, &HappyBirthdaySong::advance);
timer->start(1000);
}
diff --git a/examples/qml/referenceexamples/binding/happybirthdaysong.h b/examples/qml/referenceexamples/binding/happybirthdaysong.h
index ab264b80c7..dcfebc06ba 100644
--- a/examples/qml/referenceexamples/binding/happybirthdaysong.h
+++ b/examples/qml/referenceexamples/binding/happybirthdaysong.h
@@ -61,9 +61,9 @@ class HappyBirthdaySong : public QObject, public QQmlPropertyValueSource
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_INTERFACES(QQmlPropertyValueSource)
public:
- HappyBirthdaySong(QObject *parent = 0);
+ HappyBirthdaySong(QObject *parent = nullptr);
- virtual void setTarget(const QQmlProperty &);
+ void setTarget(const QQmlProperty &) override;
QString name() const;
void setName(const QString &);
diff --git a/examples/qml/referenceexamples/binding/main.cpp b/examples/qml/referenceexamples/binding/main.cpp
index cc9ce8f373..99187eba3e 100644
--- a/examples/qml/referenceexamples/binding/main.cpp
+++ b/examples/qml/referenceexamples/binding/main.cpp
@@ -68,7 +68,7 @@ int main(int argc, char ** argv)
QQmlEngine engine;
QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- BirthdayParty *party = qobject_cast<BirthdayParty *>(component.create());
+ auto *party = qobject_cast<BirthdayParty *>(component.create());
if (party && party->host()) {
qWarning() << party->host()->name() << "is having a birthday!";
diff --git a/examples/qml/referenceexamples/binding/person.h b/examples/qml/referenceexamples/binding/person.h
index 2cff97a6cd..543b24f971 100644
--- a/examples/qml/referenceexamples/binding/person.h
+++ b/examples/qml/referenceexamples/binding/person.h
@@ -61,7 +61,7 @@ class ShoeDescription : public QObject
Q_PROPERTY(QString brand READ brand WRITE setBrand NOTIFY shoeChanged)
Q_PROPERTY(qreal price READ price WRITE setPrice NOTIFY shoeChanged)
public:
- ShoeDescription(QObject *parent = 0);
+ ShoeDescription(QObject *parent = nullptr);
int size() const;
void setSize(int);
@@ -92,7 +92,7 @@ class Person : public QObject
Q_PROPERTY(ShoeDescription *shoe READ shoe CONSTANT)
// ![0]
public:
- Person(QObject *parent = 0);
+ Person(QObject *parent = nullptr);
QString name() const;
void setName(const QString &);
@@ -110,14 +110,14 @@ class Boy : public Person
{
Q_OBJECT
public:
- Boy(QObject * parent = 0);
+ Boy(QObject * parent = nullptr);
};
class Girl : public Person
{
Q_OBJECT
public:
- Girl(QObject * parent = 0);
+ Girl(QObject * parent = nullptr);
};
#endif // PERSON_H
diff --git a/examples/qml/referenceexamples/coercion/birthdayparty.cpp b/examples/qml/referenceexamples/coercion/birthdayparty.cpp
index e729c42bb5..1bae55076c 100644
--- a/examples/qml/referenceexamples/coercion/birthdayparty.cpp
+++ b/examples/qml/referenceexamples/coercion/birthdayparty.cpp
@@ -66,7 +66,7 @@ void BirthdayParty::setHost(Person *c)
QQmlListProperty<Person> BirthdayParty::guests()
{
- return QQmlListProperty<Person>(this, m_guests);
+ return {this, m_guests};
}
int BirthdayParty::guestCount() const
diff --git a/examples/qml/referenceexamples/coercion/birthdayparty.h b/examples/qml/referenceexamples/coercion/birthdayparty.h
index bb20212ac9..554e7ab0da 100644
--- a/examples/qml/referenceexamples/coercion/birthdayparty.h
+++ b/examples/qml/referenceexamples/coercion/birthdayparty.h
@@ -62,7 +62,7 @@ class BirthdayParty : public QObject
Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
// ![0]
public:
- BirthdayParty(QObject *parent = 0);
+ BirthdayParty(QObject *parent = nullptr);
Person *host() const;
void setHost(Person *);
diff --git a/examples/qml/referenceexamples/coercion/main.cpp b/examples/qml/referenceexamples/coercion/main.cpp
index 04a78b05f7..262cdf6320 100644
--- a/examples/qml/referenceexamples/coercion/main.cpp
+++ b/examples/qml/referenceexamples/coercion/main.cpp
@@ -70,7 +70,7 @@ int main(int argc, char ** argv)
QQmlEngine engine;
QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- BirthdayParty *party = qobject_cast<BirthdayParty *>(component.create());
+ auto *party = qobject_cast<BirthdayParty *>(component.create());
if (party && party->host()) {
qWarning() << party->host()->name() << "is having a birthday!";
diff --git a/examples/qml/referenceexamples/coercion/person.h b/examples/qml/referenceexamples/coercion/person.h
index 7169859cce..692cf4eb19 100644
--- a/examples/qml/referenceexamples/coercion/person.h
+++ b/examples/qml/referenceexamples/coercion/person.h
@@ -58,7 +58,7 @@ class Person : public QObject
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize)
public:
- Person(QObject *parent = 0);
+ Person(QObject *parent = nullptr);
QString name() const;
void setName(const QString &);
@@ -76,7 +76,7 @@ class Boy : public Person
{
Q_OBJECT
public:
- Boy(QObject * parent = 0);
+ Boy(QObject * parent = nullptr);
};
//! [girl class]
@@ -84,7 +84,7 @@ class Girl : public Person
{
Q_OBJECT
public:
- Girl(QObject * parent = 0);
+ Girl(QObject * parent = nullptr);
};
//! [girl class]
diff --git a/examples/qml/referenceexamples/default/birthdayparty.cpp b/examples/qml/referenceexamples/default/birthdayparty.cpp
index e729c42bb5..1bae55076c 100644
--- a/examples/qml/referenceexamples/default/birthdayparty.cpp
+++ b/examples/qml/referenceexamples/default/birthdayparty.cpp
@@ -66,7 +66,7 @@ void BirthdayParty::setHost(Person *c)
QQmlListProperty<Person> BirthdayParty::guests()
{
- return QQmlListProperty<Person>(this, m_guests);
+ return {this, m_guests};
}
int BirthdayParty::guestCount() const
diff --git a/examples/qml/referenceexamples/default/birthdayparty.h b/examples/qml/referenceexamples/default/birthdayparty.h
index 6acb395f47..ea63a6a16d 100644
--- a/examples/qml/referenceexamples/default/birthdayparty.h
+++ b/examples/qml/referenceexamples/default/birthdayparty.h
@@ -62,7 +62,7 @@ class BirthdayParty : public QObject
Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
Q_CLASSINFO("DefaultProperty", "guests")
public:
- BirthdayParty(QObject *parent = 0);
+ BirthdayParty(QObject *parent = nullptr);
Person *host() const;
void setHost(Person *);
diff --git a/examples/qml/referenceexamples/default/main.cpp b/examples/qml/referenceexamples/default/main.cpp
index d8c3e466ce..017d6495cd 100644
--- a/examples/qml/referenceexamples/default/main.cpp
+++ b/examples/qml/referenceexamples/default/main.cpp
@@ -65,7 +65,7 @@ int main(int argc, char ** argv)
QQmlEngine engine;
QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- BirthdayParty *party = qobject_cast<BirthdayParty *>(component.create());
+ auto *party = qobject_cast<BirthdayParty *>(component.create());
if (party && party->host()) {
qWarning() << party->host()->name() << "is having a birthday!";
diff --git a/examples/qml/referenceexamples/default/person.h b/examples/qml/referenceexamples/default/person.h
index 878c2953e5..87f69276bf 100644
--- a/examples/qml/referenceexamples/default/person.h
+++ b/examples/qml/referenceexamples/default/person.h
@@ -58,7 +58,7 @@ class Person : public QObject
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize)
public:
- Person(QObject *parent = 0);
+ Person(QObject *parent = nullptr);
QString name() const;
void setName(const QString &);
@@ -74,14 +74,14 @@ class Boy : public Person
{
Q_OBJECT
public:
- Boy(QObject * parent = 0);
+ Boy(QObject * parent = nullptr);
};
class Girl : public Person
{
Q_OBJECT
public:
- Girl(QObject * parent = 0);
+ Girl(QObject * parent = nullptr);
};
#endif // PERSON_H
diff --git a/examples/qml/referenceexamples/extended/lineedit.cpp b/examples/qml/referenceexamples/extended/lineedit.cpp
index f2f5ec0efc..feb1a08585 100644
--- a/examples/qml/referenceexamples/extended/lineedit.cpp
+++ b/examples/qml/referenceexamples/extended/lineedit.cpp
@@ -51,64 +51,56 @@
#include <qqml.h>
LineEditExtension::LineEditExtension(QObject *object)
-: QObject(object), m_lineedit(static_cast<QLineEdit *>(object))
+: QObject(object), m_lineedit(qobject_cast<QLineEdit *>(object))
{
}
int LineEditExtension::leftMargin() const
{
- int l, r, t, b;
- m_lineedit->getTextMargins(&l, &t, &r, &b);
- return l;
+ return m_lineedit->textMargins().left();
}
-void LineEditExtension::setLeftMargin(int m)
+void LineEditExtension::setLeftMargin(int l)
{
- int l, r, t, b;
- m_lineedit->getTextMargins(&l, &t, &r, &b);
- m_lineedit->setTextMargins(m, t, r, b);
+ QMargins m = m_lineedit->textMargins();
+ m.setLeft(l);
+ m_lineedit->setTextMargins(m);
}
int LineEditExtension::rightMargin() const
{
- int l, r, t, b;
- m_lineedit->getTextMargins(&l, &t, &r, &b);
- return r;
+ return m_lineedit->textMargins().right();
}
-void LineEditExtension::setRightMargin(int m)
+void LineEditExtension::setRightMargin(int r)
{
- int l, r, t, b;
- m_lineedit->getTextMargins(&l, &t, &r, &b);
- m_lineedit->setTextMargins(l, t, m, b);
+ QMargins m = m_lineedit->textMargins();
+ m.setRight(r);
+ m_lineedit->setTextMargins(m);
}
int LineEditExtension::topMargin() const
{
- int l, r, t, b;
- m_lineedit->getTextMargins(&l, &t, &r, &b);
- return t;
+ return m_lineedit->textMargins().top();
}
-void LineEditExtension::setTopMargin(int m)
+void LineEditExtension::setTopMargin(int t)
{
- int l, r, t, b;
- m_lineedit->getTextMargins(&l, &t, &r, &b);
- m_lineedit->setTextMargins(l, m, r, b);
+ QMargins m = m_lineedit->textMargins();
+ m.setTop(t);
+ m_lineedit->setTextMargins(m);
}
int LineEditExtension::bottomMargin() const
{
- int l, r, t, b;
- m_lineedit->getTextMargins(&l, &t, &r, &b);
- return b;
+ return m_lineedit->textMargins().bottom();
}
-void LineEditExtension::setBottomMargin(int m)
+void LineEditExtension::setBottomMargin(int b)
{
- int l, r, t, b;
- m_lineedit->getTextMargins(&l, &t, &r, &b);
- m_lineedit->setTextMargins(l, t, r, m);
+ QMargins m = m_lineedit->textMargins();
+ m.setBottom(b);
+ m_lineedit->setTextMargins(m);
}
diff --git a/examples/qml/referenceexamples/extended/main.cpp b/examples/qml/referenceexamples/extended/main.cpp
index c99e052ae5..f91cec76b1 100644
--- a/examples/qml/referenceexamples/extended/main.cpp
+++ b/examples/qml/referenceexamples/extended/main.cpp
@@ -65,7 +65,7 @@ int main(int argc, char ** argv)
// ![1]
QQmlEngine engine;
QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- QLineEdit *edit = qobject_cast<QLineEdit *>(component.create());
+ auto *edit = qobject_cast<QLineEdit *>(component.create());
// ![1]
if (edit) {
diff --git a/examples/qml/referenceexamples/grouped/birthdayparty.cpp b/examples/qml/referenceexamples/grouped/birthdayparty.cpp
index e729c42bb5..1bae55076c 100644
--- a/examples/qml/referenceexamples/grouped/birthdayparty.cpp
+++ b/examples/qml/referenceexamples/grouped/birthdayparty.cpp
@@ -66,7 +66,7 @@ void BirthdayParty::setHost(Person *c)
QQmlListProperty<Person> BirthdayParty::guests()
{
- return QQmlListProperty<Person>(this, m_guests);
+ return {this, m_guests};
}
int BirthdayParty::guestCount() const
diff --git a/examples/qml/referenceexamples/grouped/birthdayparty.h b/examples/qml/referenceexamples/grouped/birthdayparty.h
index 7a9a03dabb..edaa11fa88 100644
--- a/examples/qml/referenceexamples/grouped/birthdayparty.h
+++ b/examples/qml/referenceexamples/grouped/birthdayparty.h
@@ -61,7 +61,7 @@ class BirthdayParty : public QObject
Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
Q_CLASSINFO("DefaultProperty", "guests")
public:
- BirthdayParty(QObject *parent = 0);
+ BirthdayParty(QObject *parent = nullptr);
Person *host() const;
void setHost(Person *);
diff --git a/examples/qml/referenceexamples/grouped/main.cpp b/examples/qml/referenceexamples/grouped/main.cpp
index 17dcd09c34..14cd64fe68 100644
--- a/examples/qml/referenceexamples/grouped/main.cpp
+++ b/examples/qml/referenceexamples/grouped/main.cpp
@@ -66,7 +66,7 @@ int main(int argc, char ** argv)
QQmlEngine engine;
QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- BirthdayParty *party = qobject_cast<BirthdayParty *>(component.create());
+ auto *party = qobject_cast<BirthdayParty *>(component.create());
if (party && party->host()) {
qWarning() << party->host()->name() << "is having a birthday!";
diff --git a/examples/qml/referenceexamples/grouped/person.h b/examples/qml/referenceexamples/grouped/person.h
index b4e6a894cd..6f6caaee7c 100644
--- a/examples/qml/referenceexamples/grouped/person.h
+++ b/examples/qml/referenceexamples/grouped/person.h
@@ -61,7 +61,7 @@ class ShoeDescription : public QObject
Q_PROPERTY(QString brand READ brand WRITE setBrand)
Q_PROPERTY(qreal price READ price WRITE setPrice)
public:
- ShoeDescription(QObject *parent = 0);
+ ShoeDescription(QObject *parent = nullptr);
int size() const;
void setSize(int);
@@ -89,7 +89,7 @@ class Person : public QObject
Q_PROPERTY(ShoeDescription *shoe READ shoe)
// ![1]
public:
- Person(QObject *parent = 0);
+ Person(QObject *parent = nullptr);
QString name() const;
void setName(const QString &);
@@ -104,14 +104,14 @@ class Boy : public Person
{
Q_OBJECT
public:
- Boy(QObject * parent = 0);
+ Boy(QObject * parent = nullptr);
};
class Girl : public Person
{
Q_OBJECT
public:
- Girl(QObject * parent = 0);
+ Girl(QObject * parent = nullptr);
};
#endif // PERSON_H
diff --git a/examples/qml/referenceexamples/methods/birthdayparty.cpp b/examples/qml/referenceexamples/methods/birthdayparty.cpp
index dfb36257eb..7e750e4f4b 100644
--- a/examples/qml/referenceexamples/methods/birthdayparty.cpp
+++ b/examples/qml/referenceexamples/methods/birthdayparty.cpp
@@ -67,7 +67,7 @@ void BirthdayParty::setHost(Person *c)
QQmlListProperty<Person> BirthdayParty::guests()
{
- return QQmlListProperty<Person>(this, m_guests);
+ return {this, m_guests};
}
int BirthdayParty::guestCount() const
@@ -82,7 +82,7 @@ Person *BirthdayParty::guest(int index) const
void BirthdayParty::invite(const QString &name)
{
- Person *person = new Person(this);
+ auto *person = new Person(this);
person->setName(name);
m_guests.append(person);
}
diff --git a/examples/qml/referenceexamples/methods/birthdayparty.h b/examples/qml/referenceexamples/methods/birthdayparty.h
index 27c164728a..0eb968a841 100644
--- a/examples/qml/referenceexamples/methods/birthdayparty.h
+++ b/examples/qml/referenceexamples/methods/birthdayparty.h
@@ -60,7 +60,7 @@ class BirthdayParty : public QObject
Q_PROPERTY(Person *host READ host WRITE setHost)
Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
public:
- BirthdayParty(QObject *parent = 0);
+ BirthdayParty(QObject *parent = nullptr);
Person *host() const;
void setHost(Person *);
diff --git a/examples/qml/referenceexamples/methods/main.cpp b/examples/qml/referenceexamples/methods/main.cpp
index 974cc26338..89404ec822 100644
--- a/examples/qml/referenceexamples/methods/main.cpp
+++ b/examples/qml/referenceexamples/methods/main.cpp
@@ -63,7 +63,7 @@ int main(int argc, char ** argv)
QQmlEngine engine;
QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- BirthdayParty *party = qobject_cast<BirthdayParty *>(component.create());
+ auto *party = qobject_cast<BirthdayParty *>(component.create());
if (party && party->host()) {
qWarning() << party->host()->name() << "is having a birthday!";
diff --git a/examples/qml/referenceexamples/methods/person.h b/examples/qml/referenceexamples/methods/person.h
index 488f8ebac4..749109dc72 100644
--- a/examples/qml/referenceexamples/methods/person.h
+++ b/examples/qml/referenceexamples/methods/person.h
@@ -58,7 +58,7 @@ class Person : public QObject
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize)
public:
- Person(QObject *parent = 0);
+ Person(QObject *parent = nullptr);
QString name() const;
void setName(const QString &);
diff --git a/examples/qml/referenceexamples/properties/birthdayparty.cpp b/examples/qml/referenceexamples/properties/birthdayparty.cpp
index 717c3e0f71..9abb08dbd9 100644
--- a/examples/qml/referenceexamples/properties/birthdayparty.cpp
+++ b/examples/qml/referenceexamples/properties/birthdayparty.cpp
@@ -67,11 +67,11 @@ void BirthdayParty::setHost(Person *c)
QQmlListProperty<Person> BirthdayParty::guests()
{
- return QQmlListProperty<Person>(this, this,
+ return {this, this,
&BirthdayParty::appendGuest,
&BirthdayParty::guestCount,
&BirthdayParty::guest,
- &BirthdayParty::clearGuests);
+ &BirthdayParty::clearGuests};
}
void BirthdayParty::appendGuest(Person* p) {
diff --git a/examples/qml/referenceexamples/properties/birthdayparty.h b/examples/qml/referenceexamples/properties/birthdayparty.h
index 00c5e443b4..8d62c8dcd5 100644
--- a/examples/qml/referenceexamples/properties/birthdayparty.h
+++ b/examples/qml/referenceexamples/properties/birthdayparty.h
@@ -68,7 +68,7 @@ class BirthdayParty : public QObject
// ![2]
// ![3]
public:
- BirthdayParty(QObject *parent = 0);
+ BirthdayParty(QObject *parent = nullptr);
Person *host() const;
void setHost(Person *);
diff --git a/examples/qml/referenceexamples/properties/main.cpp b/examples/qml/referenceexamples/properties/main.cpp
index fbdbd13fd0..a0a2335034 100644
--- a/examples/qml/referenceexamples/properties/main.cpp
+++ b/examples/qml/referenceexamples/properties/main.cpp
@@ -65,7 +65,7 @@ int main(int argc, char ** argv)
QQmlEngine engine;
QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- BirthdayParty *party = qobject_cast<BirthdayParty *>(component.create());
+ auto *party = qobject_cast<BirthdayParty *>(component.create());
if (party && party->host()) {
qWarning() << party->host()->name() << "is having a birthday!";
diff --git a/examples/qml/referenceexamples/properties/person.h b/examples/qml/referenceexamples/properties/person.h
index 488f8ebac4..749109dc72 100644
--- a/examples/qml/referenceexamples/properties/person.h
+++ b/examples/qml/referenceexamples/properties/person.h
@@ -58,7 +58,7 @@ class Person : public QObject
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize)
public:
- Person(QObject *parent = 0);
+ Person(QObject *parent = nullptr);
QString name() const;
void setName(const QString &);
diff --git a/examples/qml/referenceexamples/signal/birthdayparty.cpp b/examples/qml/referenceexamples/signal/birthdayparty.cpp
index a5fbb742a5..9d34cdf146 100644
--- a/examples/qml/referenceexamples/signal/birthdayparty.cpp
+++ b/examples/qml/referenceexamples/signal/birthdayparty.cpp
@@ -82,7 +82,7 @@ void BirthdayParty::setHost(Person *c)
QQmlListProperty<Person> BirthdayParty::guests()
{
- return QQmlListProperty<Person>(this, m_guests);
+ return {this, m_guests};
}
int BirthdayParty::guestCount() const
diff --git a/examples/qml/referenceexamples/signal/birthdayparty.h b/examples/qml/referenceexamples/signal/birthdayparty.h
index 759450691e..9aecc8929c 100644
--- a/examples/qml/referenceexamples/signal/birthdayparty.h
+++ b/examples/qml/referenceexamples/signal/birthdayparty.h
@@ -76,7 +76,7 @@ class BirthdayParty : public QObject
Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
Q_CLASSINFO("DefaultProperty", "guests")
public:
- BirthdayParty(QObject *parent = 0);
+ BirthdayParty(QObject *parent = nullptr);
Person *host() const;
void setHost(Person *);
diff --git a/examples/qml/referenceexamples/signal/main.cpp b/examples/qml/referenceexamples/signal/main.cpp
index 7e096edd78..bb75e02bc2 100644
--- a/examples/qml/referenceexamples/signal/main.cpp
+++ b/examples/qml/referenceexamples/signal/main.cpp
@@ -67,7 +67,7 @@ int main(int argc, char ** argv)
QQmlEngine engine;
QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- BirthdayParty *party = qobject_cast<BirthdayParty *>(component.create());
+ auto *party = qobject_cast<BirthdayParty *>(component.create());
if (party && party->host()) {
qWarning() << party->host()->name() << "is having a birthday!";
diff --git a/examples/qml/referenceexamples/signal/person.h b/examples/qml/referenceexamples/signal/person.h
index 9b63773d49..06d4f2eb27 100644
--- a/examples/qml/referenceexamples/signal/person.h
+++ b/examples/qml/referenceexamples/signal/person.h
@@ -61,7 +61,7 @@ class ShoeDescription : public QObject
Q_PROPERTY(QString brand READ brand WRITE setBrand)
Q_PROPERTY(qreal price READ price WRITE setPrice)
public:
- ShoeDescription(QObject *parent = 0);
+ ShoeDescription(QObject *parent = nullptr);
int size() const;
void setSize(int);
@@ -87,7 +87,7 @@ class Person : public QObject
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(ShoeDescription *shoe READ shoe)
public:
- Person(QObject *parent = 0);
+ Person(QObject *parent = nullptr);
QString name() const;
void setName(const QString &);
@@ -102,14 +102,14 @@ class Boy : public Person
{
Q_OBJECT
public:
- Boy(QObject * parent = 0);
+ Boy(QObject * parent = nullptr);
};
class Girl : public Person
{
Q_OBJECT
public:
- Girl(QObject * parent = 0);
+ Girl(QObject * parent = nullptr);
};
#endif // PERSON_H
diff --git a/examples/qml/referenceexamples/valuesource/birthdayparty.cpp b/examples/qml/referenceexamples/valuesource/birthdayparty.cpp
index b107c61570..68d5767e8d 100644
--- a/examples/qml/referenceexamples/valuesource/birthdayparty.cpp
+++ b/examples/qml/referenceexamples/valuesource/birthdayparty.cpp
@@ -82,7 +82,7 @@ void BirthdayParty::setHost(Person *c)
QQmlListProperty<Person> BirthdayParty::guests()
{
- return QQmlListProperty<Person>(this, m_guests);
+ return {this, m_guests};
}
int BirthdayParty::guestCount() const
diff --git a/examples/qml/referenceexamples/valuesource/birthdayparty.h b/examples/qml/referenceexamples/valuesource/birthdayparty.h
index f965695cf6..18a9b96147 100644
--- a/examples/qml/referenceexamples/valuesource/birthdayparty.h
+++ b/examples/qml/referenceexamples/valuesource/birthdayparty.h
@@ -80,7 +80,7 @@ class BirthdayParty : public QObject
// ![0]
Q_CLASSINFO("DefaultProperty", "guests")
public:
- BirthdayParty(QObject *parent = 0);
+ BirthdayParty(QObject *parent = nullptr);
Person *host() const;
void setHost(Person *);
diff --git a/examples/qml/referenceexamples/valuesource/happybirthdaysong.cpp b/examples/qml/referenceexamples/valuesource/happybirthdaysong.cpp
index d8e4cad963..da09d3d7ba 100644
--- a/examples/qml/referenceexamples/valuesource/happybirthdaysong.cpp
+++ b/examples/qml/referenceexamples/valuesource/happybirthdaysong.cpp
@@ -54,7 +54,7 @@ HappyBirthdaySong::HappyBirthdaySong(QObject *parent)
: QObject(parent), m_line(-1)
{
setName(QString());
- QTimer *timer = new QTimer(this);
+ auto *timer = new QTimer(this);
QObject::connect(timer, &QTimer::timeout, this, &HappyBirthdaySong::advance);
timer->start(1000);
}
diff --git a/examples/qml/referenceexamples/valuesource/happybirthdaysong.h b/examples/qml/referenceexamples/valuesource/happybirthdaysong.h
index 89bd13c295..e2205a4ebb 100644
--- a/examples/qml/referenceexamples/valuesource/happybirthdaysong.h
+++ b/examples/qml/referenceexamples/valuesource/happybirthdaysong.h
@@ -65,9 +65,9 @@ class HappyBirthdaySong : public QObject, public QQmlPropertyValueSource
Q_PROPERTY(QString name READ name WRITE setName)
// ![1]
public:
- HappyBirthdaySong(QObject *parent = 0);
+ HappyBirthdaySong(QObject *parent = nullptr);
- virtual void setTarget(const QQmlProperty &);
+ void setTarget(const QQmlProperty &) override;
// ![1]
QString name() const;
diff --git a/examples/qml/referenceexamples/valuesource/main.cpp b/examples/qml/referenceexamples/valuesource/main.cpp
index 2f3c466935..4bef695fe2 100644
--- a/examples/qml/referenceexamples/valuesource/main.cpp
+++ b/examples/qml/referenceexamples/valuesource/main.cpp
@@ -69,7 +69,7 @@ int main(int argc, char ** argv)
QQmlEngine engine;
QQmlComponent component(&engine, QUrl("qrc:example.qml"));
- BirthdayParty *party = qobject_cast<BirthdayParty *>(component.create());
+ auto *party = qobject_cast<BirthdayParty *>(component.create());
if (party && party->host()) {
qWarning() << party->host()->name() << "is having a birthday!";
diff --git a/examples/qml/referenceexamples/valuesource/person.h b/examples/qml/referenceexamples/valuesource/person.h
index 9b63773d49..06d4f2eb27 100644
--- a/examples/qml/referenceexamples/valuesource/person.h
+++ b/examples/qml/referenceexamples/valuesource/person.h
@@ -61,7 +61,7 @@ class ShoeDescription : public QObject
Q_PROPERTY(QString brand READ brand WRITE setBrand)
Q_PROPERTY(qreal price READ price WRITE setPrice)
public:
- ShoeDescription(QObject *parent = 0);
+ ShoeDescription(QObject *parent = nullptr);
int size() const;
void setSize(int);
@@ -87,7 +87,7 @@ class Person : public QObject
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(ShoeDescription *shoe READ shoe)
public:
- Person(QObject *parent = 0);
+ Person(QObject *parent = nullptr);
QString name() const;
void setName(const QString &);
@@ -102,14 +102,14 @@ class Boy : public Person
{
Q_OBJECT
public:
- Boy(QObject * parent = 0);
+ Boy(QObject * parent = nullptr);
};
class Girl : public Person
{
Q_OBJECT
public:
- Girl(QObject * parent = 0);
+ Girl(QObject * parent = nullptr);
};
#endif // PERSON_H
diff --git a/examples/quick/imageelements/content/multi.ico b/examples/quick/imageelements/content/multi.ico
new file mode 100644
index 0000000000..b748ceaa29
--- /dev/null
+++ b/examples/quick/imageelements/content/multi.ico
Binary files differ
diff --git a/examples/quick/imageelements/framestepping.qml b/examples/quick/imageelements/framestepping.qml
new file mode 100644
index 0000000000..f5bad46e7b
--- /dev/null
+++ b/examples/quick/imageelements/framestepping.qml
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+
+Rectangle {
+ width: 480
+ height: 320
+ Image {
+ id: img
+ anchors.centerIn: parent
+ cache: true
+ source: "content/multi.ico"
+
+ Shortcut {
+ sequence: StandardKey.MoveToNextPage
+ enabled: img.currentFrame < img.frameCount - 1
+ onActivated: img.currentFrame++
+ }
+ Shortcut {
+ sequence: StandardKey.MoveToPreviousPage
+ enabled: img.currentFrame > 0
+ onActivated: img.currentFrame--
+ }
+ }
+
+ Text {
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ anchors.margins: 6
+ horizontalAlignment: Text.AlignHCenter
+ text: "frame " + (img.currentFrame + 1) + " of " + img.frameCount +
+ "\nPress PgUp/PgDn to switch frames"
+ }
+}
diff --git a/examples/quick/imageelements/imageelements.qml b/examples/quick/imageelements/imageelements.qml
index dfb4d24ea6..91f2e034f7 100644
--- a/examples/quick/imageelements/imageelements.qml
+++ b/examples/quick/imageelements/imageelements.qml
@@ -64,6 +64,8 @@ Item {
addExample("AnimatedImage", "An image which plays animated formats", Qt.resolvedUrl("animatedimage.qml"));
addExample("AnimatedSprite", "A simple sprite-based animation", Qt.resolvedUrl("animatedsprite.qml"));
addExample("SpriteSequence", "A sprite-based animation with complex transitions", Qt.resolvedUrl("spritesequence.qml"));
+ addExample("FrameStepping", "A multi-frame non-animated image", Qt.resolvedUrl("framestepping.qml"));
+ addExample("MultiBorderImage", "A multi-frame image with scaled borders", Qt.resolvedUrl("multiframeborderimage.qml"));
}
}
}
diff --git a/examples/quick/imageelements/imageelements.qrc b/examples/quick/imageelements/imageelements.qrc
index 2488fb083b..cedef2204c 100644
--- a/examples/quick/imageelements/imageelements.qrc
+++ b/examples/quick/imageelements/imageelements.qrc
@@ -8,6 +8,7 @@
<file>content/colors-stretch.sci</file>
<file>content/colors.png</file>
<file>content/ImageCell.qml</file>
+ <file>content/multi.ico</file>
<file>content/MyBorderImage.qml</file>
<file>content/qt-logo.png</file>
<file>content/shadow.png</file>
@@ -18,6 +19,8 @@
<file>animatedimage.qml</file>
<file>animatedsprite.qml</file>
<file>borderimage.qml</file>
+ <file>framestepping.qml</file>
+ <file>multiframeborderimage.qml</file>
<file>image.qml</file>
<file>shadows.qml</file>
<file>spritesequence.qml</file>
diff --git a/examples/quick/imageelements/multiframeborderimage.qml b/examples/quick/imageelements/multiframeborderimage.qml
new file mode 100644
index 0000000000..0805ea4243
--- /dev/null
+++ b/examples/quick/imageelements/multiframeborderimage.qml
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+
+Rectangle {
+ width: 480
+ height: 320
+ BorderImage {
+ id: img
+ anchors.fill: parent
+ anchors.margins: 6
+ cache: true
+ source: "content/multi.ico"
+ border { left: 19; top: 19; right: 19; bottom: 19 }
+ horizontalTileMode: BorderImage.Stretch
+
+ Shortcut {
+ sequence: StandardKey.MoveToNextPage
+ enabled: img.currentFrame < img.frameCount - 1
+ onActivated: img.currentFrame++
+ }
+ Shortcut {
+ sequence: StandardKey.MoveToPreviousPage
+ enabled: img.currentFrame > 0
+ onActivated: img.currentFrame--
+ }
+ }
+
+ Text {
+ anchors.centerIn: parent
+ horizontalAlignment: Text.AlignHCenter
+ text: "frame " + (img.currentFrame + 1) + " of " + img.frameCount +
+ "\nPress PgUp/PgDn to switch frames"
+ }
+}
diff --git a/examples/quick/imageresponseprovider/imageresponseprovider.cpp b/examples/quick/imageresponseprovider/imageresponseprovider.cpp
index 4f7c12b1d8..32510dbec8 100644
--- a/examples/quick/imageresponseprovider/imageresponseprovider.cpp
+++ b/examples/quick/imageresponseprovider/imageresponseprovider.cpp
@@ -57,40 +57,60 @@
#include <QImage>
#include <QThreadPool>
-class AsyncImageResponse : public QQuickImageResponse, public QRunnable
+class AsyncImageResponseRunnable : public QObject, public QRunnable
+{
+ Q_OBJECT
+
+signals:
+ void done(QImage image);
+
+public:
+ AsyncImageResponseRunnable(const QString &id, const QSize &requestedSize)
+ : m_id(id), m_requestedSize(requestedSize) {}
+
+ void run() override
+ {
+ auto image = QImage(50, 50, QImage::Format_RGB32);
+ if (m_id == QLatin1String("slow")) {
+ qDebug() << "Slow, red, sleeping for 5 seconds";
+ QThread::sleep(5);
+ image.fill(Qt::red);
+ } else {
+ qDebug() << "Fast, blue, sleeping for 1 second";
+ QThread::sleep(1);
+ image.fill(Qt::blue);
+ }
+ if (m_requestedSize.isValid())
+ image = image.scaled(m_requestedSize);
+
+ emit done(image);
+ }
+
+private:
+ QString m_id;
+ QSize m_requestedSize;
+};
+
+class AsyncImageResponse : public QQuickImageResponse
{
public:
- AsyncImageResponse(const QString &id, const QSize &requestedSize)
- : m_id(id), m_requestedSize(requestedSize)
+ AsyncImageResponse(const QString &id, const QSize &requestedSize, QThreadPool *pool)
{
- setAutoDelete(false);
+ auto runnable = new AsyncImageResponseRunnable(id, requestedSize);
+ connect(runnable, &AsyncImageResponseRunnable::done, this, &AsyncImageResponse::handleDone);
+ pool->start(runnable);
}
- QQuickTextureFactory *textureFactory() const override
- {
- return QQuickTextureFactory::textureFactoryForImage(m_image);
+ void handleDone(QImage image) {
+ m_image = image;
+ emit finished();
}
- void run() override
+ QQuickTextureFactory *textureFactory() const override
{
- m_image = QImage(50, 50, QImage::Format_RGB32);
- if (m_id == "slow") {
- qDebug() << "Slow, red, sleeping for 5 seconds";
- QThread::sleep(5);
- m_image.fill(Qt::red);
- } else {
- qDebug() << "Fast, blue, sleeping for 1 second";
- QThread::sleep(1);
- m_image.fill(Qt::blue);
- }
- if (m_requestedSize.isValid())
- m_image = m_image.scaled(m_requestedSize);
-
- emit finished();
+ return QQuickTextureFactory::textureFactoryForImage(m_image);
}
- QString m_id;
- QSize m_requestedSize;
QImage m_image;
};
@@ -99,8 +119,7 @@ class AsyncImageProvider : public QQuickAsyncImageProvider
public:
QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override
{
- AsyncImageResponse *response = new AsyncImageResponse(id, requestedSize);
- pool.start(response);
+ AsyncImageResponse *response = new AsyncImageResponse(id, requestedSize, &pool);
return response;
}
diff --git a/examples/quick/localstorage/localstorage/Header.qml b/examples/quick/localstorage/localstorage/Header.qml
index 18f51c1b6e..b3807f6e40 100644
--- a/examples/quick/localstorage/localstorage/Header.qml
+++ b/examples/quick/localstorage/localstorage/Header.qml
@@ -48,7 +48,7 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-import QtQuick 2.7
+import QtQuick 2.14
import QtQuick.Window 2.0
import QtQuick.LocalStorage 2.0
import "Database.js" as JS
@@ -143,8 +143,8 @@ Item {
font.pixelSize: 22
activeFocusOnPress: true
activeFocusOnTab: true
- validator: RegExpValidator {
- regExp: /[0-9/,:.]+/
+ validator: RegularExpressionValidator {
+ regularExpression: /[0-9/,:.]+/
}
onEditingFinished: {
if (dateInput.text == "") {
@@ -174,8 +174,8 @@ Item {
font.pixelSize: 22
activeFocusOnPress: true
activeFocusOnTab: true
- validator: RegExpValidator {
- regExp: /\d{1,3}/
+ validator: RegularExpressionValidator {
+ regularExpression: /\d{1,3}/
}
onEditingFinished: {
if (distInput.text == "") {
diff --git a/examples/quick/scenegraph/d3d11underqml/d3d11squircle.cpp b/examples/quick/scenegraph/d3d11underqml/d3d11squircle.cpp
new file mode 100644
index 0000000000..f05bf2f843
--- /dev/null
+++ b/examples/quick/scenegraph/d3d11underqml/d3d11squircle.cpp
@@ -0,0 +1,426 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "d3d11squircle.h"
+#include <QtCore/QRunnable>
+#include <QtQuick/QQuickWindow>
+
+#include <d3d11.h>
+#include <d3dcompiler.h>
+
+class SquircleRenderer : public QObject
+{
+ Q_OBJECT
+public:
+ SquircleRenderer();
+ ~SquircleRenderer();
+
+ void setT(qreal t) { m_t = t; }
+ void setViewportSize(const QSize &size) { m_viewportSize = size; }
+ void setWindow(QQuickWindow *window) { m_window = window; }
+
+public slots:
+ void frameStart();
+ void mainPassRecordingStart();
+
+private:
+ enum Stage {
+ VertexStage,
+ FragmentStage
+ };
+ void prepareShader(Stage stage);
+ QByteArray compileShader(Stage stage,
+ const QByteArray &source,
+ const QByteArray &entryPoint);
+ void init();
+
+ QSize m_viewportSize;
+ qreal m_t;
+ QQuickWindow *m_window;
+
+ ID3D11Device *m_device = nullptr;
+ ID3D11DeviceContext *m_context = nullptr;
+ QByteArray m_vert;
+ QByteArray m_vertEntryPoint;
+ QByteArray m_frag;
+ QByteArray m_fragEntryPoint;
+
+ bool m_initialized = false;
+ ID3D11Buffer *m_vbuf = nullptr;
+ ID3D11Buffer *m_cbuf = nullptr;
+ ID3D11VertexShader *m_vs = nullptr;
+ ID3D11PixelShader *m_ps = nullptr;
+ ID3D11InputLayout *m_inputLayout = nullptr;
+ ID3D11RasterizerState *m_rastState = nullptr;
+ ID3D11DepthStencilState *m_dsState = nullptr;
+ ID3D11BlendState *m_blendState = nullptr;
+};
+
+D3D11Squircle::D3D11Squircle()
+ : m_t(0)
+ , m_renderer(nullptr)
+{
+ connect(this, &QQuickItem::windowChanged, this, &D3D11Squircle::handleWindowChanged);
+}
+
+void D3D11Squircle::setT(qreal t)
+{
+ if (t == m_t)
+ return;
+ m_t = t;
+ emit tChanged();
+ if (window())
+ window()->update();
+}
+
+void D3D11Squircle::handleWindowChanged(QQuickWindow *win)
+{
+ if (win) {
+ connect(win, &QQuickWindow::beforeSynchronizing, this, &D3D11Squircle::sync, Qt::DirectConnection);
+ connect(win, &QQuickWindow::sceneGraphInvalidated, this, &D3D11Squircle::cleanup, Qt::DirectConnection);
+
+ // Ensure we start with cleared to black. The squircle's blend mode relies on this.
+ win->setColor(Qt::black);
+ }
+}
+
+SquircleRenderer::SquircleRenderer()
+ : m_t(0)
+{
+}
+
+// The safe way to release custom graphics resources it to both connect to
+// sceneGraphInvalidated() and implement releaseResources(). To support
+// threaded render loops the latter performs the SquircleRenderer destruction
+// via scheduleRenderJob(). Note that the D3D11Squircle may be gone by the time
+// the QRunnable is invoked.
+
+void D3D11Squircle::cleanup()
+{
+ delete m_renderer;
+ m_renderer = nullptr;
+}
+
+class CleanupJob : public QRunnable
+{
+public:
+ CleanupJob(SquircleRenderer *renderer) : m_renderer(renderer) { }
+ void run() override { delete m_renderer; }
+private:
+ SquircleRenderer *m_renderer;
+};
+
+void D3D11Squircle::releaseResources()
+{
+ window()->scheduleRenderJob(new CleanupJob(m_renderer), QQuickWindow::BeforeSynchronizingStage);
+ m_renderer = nullptr;
+}
+
+SquircleRenderer::~SquircleRenderer()
+{
+ qDebug("cleanup");
+
+ if (m_vs)
+ m_vs->Release();
+
+ if (m_ps)
+ m_ps->Release();
+
+ if (m_vbuf)
+ m_vbuf->Release();
+
+ if (m_cbuf)
+ m_cbuf->Release();
+
+ if (m_inputLayout)
+ m_inputLayout->Release();
+
+ if (m_rastState)
+ m_rastState->Release();
+
+ if (m_dsState)
+ m_dsState->Release();
+
+ if (m_blendState)
+ m_blendState->Release();
+}
+
+void D3D11Squircle::sync()
+{
+ if (!m_renderer) {
+ m_renderer = new SquircleRenderer;
+ connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::frameStart, Qt::DirectConnection);
+ connect(window(), &QQuickWindow::beforeRenderPassRecording, m_renderer, &SquircleRenderer::mainPassRecordingStart, Qt::DirectConnection);
+ }
+ m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio());
+ m_renderer->setT(m_t);
+ m_renderer->setWindow(window());
+}
+
+void SquircleRenderer::frameStart()
+{
+ QSGRendererInterface *rif = m_window->rendererInterface();
+
+ // We are not prepared for anything other than running with the RHI and its D3D11 backend.
+ Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::Direct3D11Rhi);
+
+ m_device = reinterpret_cast<ID3D11Device *>(rif->getResource(m_window, QSGRendererInterface::DeviceResource));
+ Q_ASSERT(m_device);
+ m_context = reinterpret_cast<ID3D11DeviceContext *>(rif->getResource(m_window, QSGRendererInterface::DeviceContextResource));
+ Q_ASSERT(m_context);
+
+ if (m_vert.isEmpty())
+ prepareShader(VertexStage);
+ if (m_frag.isEmpty())
+ prepareShader(FragmentStage);
+
+ if (!m_initialized)
+ init();
+}
+
+static const float vertices[] = {
+ -1, -1,
+ 1, -1,
+ -1, 1,
+ 1, 1
+};
+
+void SquircleRenderer::mainPassRecordingStart()
+{
+ m_window->beginExternalCommands();
+
+ D3D11_MAPPED_SUBRESOURCE mp;
+ // will copy the entire constant buffer every time -> pass WRITE_DISCARD -> prevent pipeline stalls
+ HRESULT hr = m_context->Map(m_cbuf, 0, D3D11_MAP_WRITE_DISCARD, 0, &mp);
+ if (SUCCEEDED(hr)) {
+ float t = m_t;
+ memcpy(mp.pData, &t, 4);
+ m_context->Unmap(m_cbuf, 0);
+ } else {
+ qFatal("Failed to map constant buffer: 0x%x", hr);
+ }
+
+ D3D11_VIEWPORT v;
+ v.TopLeftX = 0;
+ v.TopLeftY = 0;
+ v.Width = m_viewportSize.width();
+ v.Height = m_viewportSize.height();
+ v.MinDepth = 0;
+ v.MaxDepth = 1;
+ m_context->RSSetViewports(1, &v);
+
+ m_context->VSSetShader(m_vs, nullptr, 0);
+ m_context->PSSetShader(m_ps, nullptr, 0);
+ m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
+ m_context->IASetInputLayout(m_inputLayout);
+ m_context->OMSetDepthStencilState(m_dsState, 0);
+ float blendConstants[] = { 1, 1, 1, 1 };
+ m_context->OMSetBlendState(m_blendState, blendConstants, 0xFFFFFFFF);
+ m_context->RSSetState(m_rastState);
+
+ const UINT stride = 2 * sizeof(float); // vec2
+ const UINT offset = 0;
+ m_context->IASetVertexBuffers(0, 1, &m_vbuf, &stride, &offset);
+ m_context->PSSetConstantBuffers(0, 1, &m_cbuf);
+
+ m_context->Draw(4, 0);
+
+ m_window->endExternalCommands();
+}
+
+void SquircleRenderer::prepareShader(Stage stage)
+{
+ QString filename;
+ if (stage == VertexStage) {
+ filename = QLatin1String(":/scenegraph/d3d11underqml/squircle.vert");
+ } else {
+ Q_ASSERT(stage == FragmentStage);
+ filename = QLatin1String(":/scenegraph/d3d11underqml/squircle.frag");
+ }
+ QFile f(filename);
+ if (!f.open(QIODevice::ReadOnly))
+ qFatal("Failed to read shader %s", qPrintable(filename));
+
+ const QByteArray contents = f.readAll();
+
+ if (stage == VertexStage) {
+ m_vert = contents;
+ Q_ASSERT(!m_vert.isEmpty());
+ m_vertEntryPoint = QByteArrayLiteral("main");
+ } else {
+ m_frag = contents;
+ Q_ASSERT(!m_frag.isEmpty());
+ m_fragEntryPoint = QByteArrayLiteral("main");
+ }
+}
+
+QByteArray SquircleRenderer::compileShader(Stage stage,
+ const QByteArray &source,
+ const QByteArray &entryPoint)
+{
+ const char *target;
+ switch (stage) {
+ case VertexStage:
+ target = "vs_5_0";
+ break;
+ case FragmentStage:
+ target = "ps_5_0";
+ break;
+ default:
+ qFatal("Unknown shader stage %d", stage);
+ return QByteArray();
+ }
+
+ ID3DBlob *bytecode = nullptr;
+ ID3DBlob *errors = nullptr;
+ HRESULT hr = D3DCompile(source.constData(), source.size(),
+ nullptr, nullptr, nullptr,
+ entryPoint.constData(), target, 0, 0, &bytecode, &errors);
+ if (FAILED(hr) || !bytecode) {
+ qWarning("HLSL shader compilation failed: 0x%x", uint(hr));
+ if (errors) {
+ const QByteArray msg(static_cast<const char *>(errors->GetBufferPointer()),
+ errors->GetBufferSize());
+ errors->Release();
+ qWarning("%s", msg.constData());
+ }
+ return QByteArray();
+ }
+
+ QByteArray result;
+ result.resize(bytecode->GetBufferSize());
+ memcpy(result.data(), bytecode->GetBufferPointer(), result.size());
+ bytecode->Release();
+
+ return result;
+}
+
+void SquircleRenderer::init()
+{
+ qDebug("init");
+ m_initialized = true;
+
+ const QByteArray vs = compileShader(VertexStage, m_vert, m_vertEntryPoint);
+ const QByteArray fs = compileShader(FragmentStage, m_frag, m_fragEntryPoint);
+
+ HRESULT hr = m_device->CreateVertexShader(vs.constData(), vs.size(), nullptr, &m_vs);
+ if (FAILED(hr))
+ qFatal("Failed to create vertex shader: 0x%x", hr);
+
+ hr = m_device->CreatePixelShader(fs.constData(), fs.size(), nullptr, &m_ps);
+ if (FAILED(hr))
+ qFatal("Failed to create pixel shader: 0x%x", hr);
+
+ D3D11_BUFFER_DESC bufDesc;
+ memset(&bufDesc, 0, sizeof(bufDesc));
+ bufDesc.ByteWidth = sizeof(vertices);
+ bufDesc.Usage = D3D11_USAGE_DEFAULT;
+ bufDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
+ hr = m_device->CreateBuffer(&bufDesc, nullptr, &m_vbuf);
+ if (FAILED(hr))
+ qFatal("Failed to create buffer: 0x%x", hr);
+
+ m_context->UpdateSubresource(m_vbuf, 0, nullptr, vertices, 0, 0);
+
+ bufDesc.ByteWidth = 256;
+ bufDesc.Usage = D3D11_USAGE_DYNAMIC;
+ bufDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+ bufDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+ hr = m_device->CreateBuffer(&bufDesc, nullptr, &m_cbuf);
+ if (FAILED(hr))
+ qFatal("Failed to create buffer: 0x%x", hr);
+
+ D3D11_INPUT_ELEMENT_DESC inputDesc;
+ memset(&inputDesc, 0, sizeof(inputDesc));
+ // the output from SPIRV-Cross uses TEXCOORD<location> as the semantic
+ inputDesc.SemanticName = "TEXCOORD";
+ inputDesc.SemanticIndex = 0;
+ inputDesc.Format = DXGI_FORMAT_R32G32_FLOAT; // vec2
+ inputDesc.InputSlot = 0;
+ inputDesc.AlignedByteOffset = 0;
+ inputDesc.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
+ hr = m_device->CreateInputLayout(&inputDesc, 1, vs.constData(), vs.size(), &m_inputLayout);
+ if (FAILED(hr))
+ qFatal("Failed to create input layout: 0x%x", hr);
+
+ D3D11_RASTERIZER_DESC rastDesc;
+ memset(&rastDesc, 0, sizeof(rastDesc));
+ rastDesc.FillMode = D3D11_FILL_SOLID;
+ rastDesc.CullMode = D3D11_CULL_NONE;
+ hr = m_device->CreateRasterizerState(&rastDesc, &m_rastState);
+ if (FAILED(hr))
+ qFatal("Failed to create rasterizer state: 0x%x", hr);
+
+ D3D11_DEPTH_STENCIL_DESC dsDesc;
+ memset(&dsDesc, 0, sizeof(dsDesc));
+ hr = m_device->CreateDepthStencilState(&dsDesc, &m_dsState);
+ if (FAILED(hr))
+ qFatal("Failed to create depth/stencil state: 0x%x", hr);
+
+ D3D11_BLEND_DESC blendDesc;
+ memset(&blendDesc, 0, sizeof(blendDesc));
+ blendDesc.IndependentBlendEnable = true;
+ D3D11_RENDER_TARGET_BLEND_DESC blend;
+ memset(&blend, 0, sizeof(blend));
+ blend.BlendEnable = true;
+ blend.SrcBlend = D3D11_BLEND_SRC_ALPHA;
+ blend.DestBlend = D3D11_BLEND_ONE;
+ blend.BlendOp = D3D11_BLEND_OP_ADD;
+ blend.SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA;
+ blend.DestBlendAlpha = D3D11_BLEND_ONE;
+ blend.BlendOpAlpha = D3D11_BLEND_OP_ADD;
+ blend.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
+ blendDesc.RenderTarget[0] = blend;
+ hr = m_device->CreateBlendState(&blendDesc, &m_blendState);
+ if (FAILED(hr))
+ qFatal("Failed to create blend state: 0x%x", hr);
+}
+
+#include "d3d11squircle.moc"
diff --git a/examples/quick/scenegraph/d3d11underqml/d3d11squircle.h b/examples/quick/scenegraph/d3d11underqml/d3d11squircle.h
new file mode 100644
index 0000000000..be9aadc43b
--- /dev/null
+++ b/examples/quick/scenegraph/d3d11underqml/d3d11squircle.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef D3D11SQUIRCLE_H
+#define D3D11SQUIRCLE_H
+
+#include <QtQuick/QQuickItem>
+
+class SquircleRenderer;
+
+class D3D11Squircle : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged)
+
+public:
+ D3D11Squircle();
+
+ qreal t() const { return m_t; }
+ void setT(qreal t);
+
+signals:
+ void tChanged();
+
+public slots:
+ void sync();
+ void cleanup();
+
+private slots:
+ void handleWindowChanged(QQuickWindow *win);
+
+private:
+ void releaseResources() override;
+
+ qreal m_t;
+ SquircleRenderer *m_renderer = nullptr;
+};
+
+#endif
diff --git a/examples/quick/scenegraph/d3d11underqml/d3d11underqml.pro b/examples/quick/scenegraph/d3d11underqml/d3d11underqml.pro
new file mode 100644
index 0000000000..7658a9a813
--- /dev/null
+++ b/examples/quick/scenegraph/d3d11underqml/d3d11underqml.pro
@@ -0,0 +1,12 @@
+!win32: error("This example requires Windows")
+
+QT += qml quick
+
+HEADERS += d3d11squircle.h
+SOURCES += d3d11squircle.cpp main.cpp
+RESOURCES += d3d11underqml.qrc
+
+LIBS += -ld3d11 -ld3dcompiler
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/d3d11underqml
+INSTALLS += target
diff --git a/examples/quick/scenegraph/d3d11underqml/d3d11underqml.qrc b/examples/quick/scenegraph/d3d11underqml/d3d11underqml.qrc
new file mode 100644
index 0000000000..fa16b88279
--- /dev/null
+++ b/examples/quick/scenegraph/d3d11underqml/d3d11underqml.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/scenegraph/d3d11underqml">
+ <file>main.qml</file>
+ <file>squircle.vert</file>
+ <file>squircle.frag</file>
+ </qresource>
+</RCC>
diff --git a/examples/quick/scenegraph/d3d11underqml/doc/images/d3d11underqml-example.jpg b/examples/quick/scenegraph/d3d11underqml/doc/images/d3d11underqml-example.jpg
new file mode 100644
index 0000000000..9f1e53ad61
--- /dev/null
+++ b/examples/quick/scenegraph/d3d11underqml/doc/images/d3d11underqml-example.jpg
Binary files differ
diff --git a/examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc b/examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc
new file mode 100644
index 0000000000..1f0c15e1c6
--- /dev/null
+++ b/examples/quick/scenegraph/d3d11underqml/doc/src/d3d11underqml.qdoc
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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$
+**
+****************************************************************************/
+
+/*!
+ \example scenegraph/d3d11underqml
+ \title Scene Graph - Direct3D 11 Under QML
+ \ingroup qtquickexamples
+ \brief Shows how to render directly with Direct3D 11 under a Qt Quick scene.
+
+ \image d3d11underqml-example.jpg
+
+ The Direct3D 11 Under QML example shows how an application can make use
+ of the \l QQuickWindow::beforeRendering() signal to draw custom
+ D3D11 content under a Qt Quick scene. This signal is emitted at
+ the start of every frame, before the scene graph starts its
+ rendering, thus any D3D11 draw calls that are made as a response
+ to this signal, will stack under the Qt Quick items.
+
+ As an alternative, applications that wish to render D3D11 content
+ on top of the Qt Quick scene, can do so by connecting to the \l
+ QQuickWindow::afterRendering() signal.
+
+ In this example, we will also see how it is possible to have
+ values that are exposed to QML which affect the D3D11
+ rendering. We animate the threshold value using a NumberAnimation
+ in the QML file and this value is used by the HLSL shader
+ program that draws the squircles.
+
+ The example is equivalent in most ways to the \l{Scene Graph - OpenGL Under
+ QML}{OpenGL Under QML}, \l{Scene Graph - Metal Under QML}{Metal Under QML},
+ and \l{Scene Graph - Vulkan Under QML}{Vulkan Under QML} examples, they all
+ render the same custom content, just via different native APIs.
+
+ */
diff --git a/examples/quick/scenegraph/d3d11underqml/main.cpp b/examples/quick/scenegraph/d3d11underqml/main.cpp
new file mode 100644
index 0000000000..d26de1144a
--- /dev/null
+++ b/examples/quick/scenegraph/d3d11underqml/main.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QtQuick/QQuickView>
+#include "d3d11squircle.h"
+
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+
+ qmlRegisterType<D3D11Squircle>("D3D11UnderQML", 1, 0, "D3D11Squircle");
+
+ QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Direct3D11Rhi);
+
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl("qrc:///scenegraph/d3d11underqml/main.qml"));
+ view.show();
+
+ return app.exec();
+}
diff --git a/examples/quick/scenegraph/d3d11underqml/main.qml b/examples/quick/scenegraph/d3d11underqml/main.qml
new file mode 100644
index 0000000000..da128bead6
--- /dev/null
+++ b/examples/quick/scenegraph/d3d11underqml/main.qml
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [1]
+import QtQuick 2.0
+import D3D11UnderQML 1.0
+
+Item {
+
+ width: 320
+ height: 480
+
+ D3D11Squircle {
+ SequentialAnimation on t {
+ NumberAnimation { to: 1; duration: 2500; easing.type: Easing.InQuad }
+ NumberAnimation { to: 0; duration: 2500; easing.type: Easing.OutQuad }
+ loops: Animation.Infinite
+ running: true
+ }
+ }
+//! [1] //! [2]
+ Rectangle {
+ color: Qt.rgba(1, 1, 1, 0.7)
+ radius: 10
+ border.width: 1
+ border.color: "white"
+ anchors.fill: label
+ anchors.margins: -10
+ }
+
+ Text {
+ id: label
+ color: "black"
+ wrapMode: Text.WordWrap
+ text: "The background here is a squircle rendered with raw Direct3D 11 using the beforeRendering() and beforeRenderPassRecording() signals in QQuickWindow. This text label and its border is rendered using QML"
+ anchors.right: parent.right
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ anchors.margins: 20
+ }
+}
+//! [2]
diff --git a/examples/quick/scenegraph/d3d11underqml/squircle.frag b/examples/quick/scenegraph/d3d11underqml/squircle.frag
new file mode 100644
index 0000000000..a907c84e58
--- /dev/null
+++ b/examples/quick/scenegraph/d3d11underqml/squircle.frag
@@ -0,0 +1,35 @@
+cbuffer buf : register(b0)
+{
+ float ubuf_t : packoffset(c0);
+};
+
+
+static float2 coords;
+static float4 fragColor;
+
+struct SPIRV_Cross_Input
+{
+ float2 coords : TEXCOORD0;
+};
+
+struct SPIRV_Cross_Output
+{
+ float4 fragColor : SV_Target0;
+};
+
+void frag_main()
+{
+ float i = 1.0f - (pow(abs(coords.x), 4.0f) + pow(abs(coords.y), 4.0f));
+ i = smoothstep(ubuf_t - 0.800000011920928955078125f, ubuf_t + 0.800000011920928955078125f, i);
+ i = floor(i * 20.0f) / 20.0f;
+ fragColor = float4((coords * 0.5f) + 0.5f.xx, i, i);
+}
+
+SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
+{
+ coords = stage_input.coords;
+ frag_main();
+ SPIRV_Cross_Output stage_output;
+ stage_output.fragColor = fragColor;
+ return stage_output;
+}
diff --git a/examples/quick/scenegraph/d3d11underqml/squircle.vert b/examples/quick/scenegraph/d3d11underqml/squircle.vert
new file mode 100644
index 0000000000..077a1ad096
--- /dev/null
+++ b/examples/quick/scenegraph/d3d11underqml/squircle.vert
@@ -0,0 +1,30 @@
+static float4 gl_Position;
+static float4 vertices;
+static float2 coords;
+
+struct SPIRV_Cross_Input
+{
+ float4 vertices : TEXCOORD0;
+};
+
+struct SPIRV_Cross_Output
+{
+ float2 coords : TEXCOORD0;
+ float4 gl_Position : SV_Position;
+};
+
+void vert_main()
+{
+ gl_Position = vertices;
+ coords = vertices.xy;
+}
+
+SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
+{
+ vertices = stage_input.vertices;
+ vert_main();
+ SPIRV_Cross_Output stage_output;
+ stage_output.gl_Position = gl_Position;
+ stage_output.coords = coords;
+ return stage_output;
+}
diff --git a/examples/quick/scenegraph/textureinsgnode/doc/images/textureinsgnode-example.jpg b/examples/quick/scenegraph/fboitem/doc/images/fboitem-example.jpg
index 306b8bab20..306b8bab20 100644
--- a/examples/quick/scenegraph/textureinsgnode/doc/images/textureinsgnode-example.jpg
+++ b/examples/quick/scenegraph/fboitem/doc/images/fboitem-example.jpg
Binary files differ
diff --git a/examples/quick/scenegraph/textureinsgnode/doc/src/textureinsgnode.qdoc b/examples/quick/scenegraph/fboitem/doc/src/fboitem.qdoc
index c1c830338b..b5add02991 100644
--- a/examples/quick/scenegraph/textureinsgnode/doc/src/textureinsgnode.qdoc
+++ b/examples/quick/scenegraph/fboitem/doc/src/fboitem.qdoc
@@ -26,11 +26,11 @@
****************************************************************************/
/*!
- \example scenegraph/textureinsgnode
+ \example scenegraph/fboitem
\title Scene Graph - Rendering FBOs
\ingroup qtquickexamples
\brief Shows how to use FramebufferObjects with Qt Quick.
- \image textureinsgnode-example.jpg
+ \image fboitem-example.jpg
*/
diff --git a/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.cpp b/examples/quick/scenegraph/fboitem/fboinsgrenderer.cpp
index 8ba5bddb2a..8ba5bddb2a 100644
--- a/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.cpp
+++ b/examples/quick/scenegraph/fboitem/fboinsgrenderer.cpp
diff --git a/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.h b/examples/quick/scenegraph/fboitem/fboinsgrenderer.h
index e1a9ce22c8..e1a9ce22c8 100644
--- a/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.h
+++ b/examples/quick/scenegraph/fboitem/fboinsgrenderer.h
diff --git a/examples/quick/scenegraph/textureinsgnode/textureinsgnode.pro b/examples/quick/scenegraph/fboitem/fboitem.pro
index 238e20a553..e40e5f4cf8 100644
--- a/examples/quick/scenegraph/textureinsgnode/textureinsgnode.pro
+++ b/examples/quick/scenegraph/fboitem/fboitem.pro
@@ -7,9 +7,9 @@ INCLUDEPATH += ../shared
HEADERS += ../shared/logorenderer.h
SOURCES += ../shared/logorenderer.cpp
-RESOURCES += textureinsgnode.qrc
+RESOURCES += fboitem.qrc
-target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/textureinsgnode
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/fboitem
INSTALLS += target
OTHER_FILES += \
diff --git a/examples/quick/scenegraph/textureinsgnode/textureinsgnode.qrc b/examples/quick/scenegraph/fboitem/fboitem.qrc
index 9ecf0ada1c..9d9db70654 100644
--- a/examples/quick/scenegraph/textureinsgnode/textureinsgnode.qrc
+++ b/examples/quick/scenegraph/fboitem/fboitem.qrc
@@ -1,5 +1,5 @@
<RCC>
- <qresource prefix="/scenegraph/textureinsgnode">
+ <qresource prefix="/scenegraph/fboitem">
<file>main.qml</file>
</qresource>
</RCC>
diff --git a/examples/quick/scenegraph/textureinsgnode/main.cpp b/examples/quick/scenegraph/fboitem/main.cpp
index 8eececc0aa..429224ba95 100644
--- a/examples/quick/scenegraph/textureinsgnode/main.cpp
+++ b/examples/quick/scenegraph/fboitem/main.cpp
@@ -62,7 +62,7 @@ int main(int argc, char **argv)
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
- view.setSource(QUrl("qrc:///scenegraph/textureinsgnode/main.qml"));
+ view.setSource(QUrl("qrc:///scenegraph/fboitem/main.qml"));
view.show();
return app.exec();
diff --git a/examples/quick/scenegraph/textureinsgnode/main.qml b/examples/quick/scenegraph/fboitem/main.qml
index 92fa99e847..92fa99e847 100644
--- a/examples/quick/scenegraph/textureinsgnode/main.qml
+++ b/examples/quick/scenegraph/fboitem/main.qml
diff --git a/examples/quick/scenegraph/metaltextureimport/doc/images/metaltextureimport-example.jpg b/examples/quick/scenegraph/metaltextureimport/doc/images/metaltextureimport-example.jpg
new file mode 100644
index 0000000000..19ad40cd85
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/doc/images/metaltextureimport-example.jpg
Binary files differ
diff --git a/examples/quick/scenegraph/metaltextureimport/doc/src/metaltextureimport.qdoc b/examples/quick/scenegraph/metaltextureimport/doc/src/metaltextureimport.qdoc
new file mode 100644
index 0000000000..2a584c26cc
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/doc/src/metaltextureimport.qdoc
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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$
+**
+****************************************************************************/
+
+/*!
+ \example scenegraph/metaltextureimport
+ \title Scene Graph - Metal Texture Import
+ \ingroup qtquickexamples
+ \brief Shows how to use a texture created directly with Metal
+
+ \image metaltextureimport-example.jpg
+
+ The Metal Texture Import example shows how an application can import and
+ use a
+ \l{https://developer.apple.com/documentation/metal/mtltexture}{MTLTexture}
+ in the Qt Quick scene. This provides an alternative to the \l{Scene Graph -
+ Metal Under QML}{underlay}, overlay, or \l{Scene Graph - Custom Rendering
+ with QSGRenderNode}{render node} approaches when it comes to integrating
+ native Metal rendering. In many cases going through a texture, and
+ therefore "flattening" the 3D contents first, is the best option to
+ integrate and mix custom 3D contents with the 2D UI elements provided by Qt
+ Quick.
+
+ \snippet scenegraph/metaltextureimport/main.qml 1
+ \snippet scenegraph/metaltextureimport/main.qml 2
+
+ The application exposes a custom QQuickItem subclass under ther name of
+ CustomTextureItem. This is instantiated in QML. The value of the \c t
+ property is animated as well.
+
+ \snippet scenegraph/metaltextureimport/metaltextureimport.h 1
+
+ The implementation of our custom item involves overriding
+ QQuickItem::updatePaintNode(), as well as functions and slots related to
+ geometry changes and cleanup.
+
+ \snippet scenegraph/metaltextureimport/metaltextureimport.mm 1
+
+ We also need a scenegraph node. Instead of deriving directly from QSGNode,
+ we can use QSGSimpleTextureNode which gives us some of the functionality
+ pre-implemented as a convenience.
+
+ \snippet scenegraph/metaltextureimport/metaltextureimport.mm 2
+
+ The updatePaintNode() function of the item is called on the render thread
+ (if there is one), with the main (gui) thread blocked. Here we create a new
+ node if there has not yet been one, and update it. Accessing Qt objects
+ living on the main thread is safe here, so sync() will calculate and copy
+ the values it needs from QQuickItem or QQuickWindow.
+
+ \snippet scenegraph/metaltextureimport/metaltextureimport.mm 3
+
+ The node does not merely rely on the typical QQuickItem - QSGNode
+ update sequence, it connects to QQuickWindow::beforeRendering() as
+ well. That is where the contents of the Metal texture will be updated by
+ encoding a full render pass, targeting the texture, on the Qt Quicks
+ scenegraph's command buffer. beforeRendering() is the right place for this,
+ because the signal is emitted before Qt Quick starts to encode its own
+ rendering commands. Choosing QQuickWindow::beforeRenderPassRecording()
+ instead would be an error in this exanple.
+
+ \snippet scenegraph/metaltextureimport/metaltextureimport.mm 4
+ \snippet scenegraph/metaltextureimport/metaltextureimport.mm 5
+
+ After copying the values we need, sync() also performs some graphics
+ resource initialization. The MTLDevice is queried from the scenegraph. Once
+ a MTLTexture is available, a QSGTexture wrapping (not owning) it is created
+ via QQuickWindow::createTextureFromNativeObject(). This function is a
+ modern equivalent to QQuickWindow::createTextureFromId() that is not tied
+ to OpenGL. Finally, the QSGTexture is associated with the underlying
+ materials by calling the base class' setTexture() function.
+
+ \snippet scenegraph/metaltextureimport/metaltextureimport.mm 6
+
+ render(), the slot connected to beforeRendering(), encodes the rendering
+ commands using the buffers and pipeline state objects created in sync().
+
+ */
diff --git a/examples/quick/scenegraph/metaltextureimport/main.cpp b/examples/quick/scenegraph/metaltextureimport/main.cpp
new file mode 100644
index 0000000000..c969817e8f
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/main.cpp
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QtQuick/QQuickView>
+#include "metaltextureimport.h"
+
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+
+ qmlRegisterType<CustomTextureItem>("MetalTextureImport", 1, 0, "CustomTextureItem");
+
+ QQuickWindow::setSceneGraphBackend(QSGRendererInterface::MetalRhi);
+
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl("qrc:///scenegraph/metaltextureimport/main.qml"));
+ view.resize(400, 400);
+ view.show();
+
+ return app.exec();
+}
diff --git a/examples/quick/scenegraph/metaltextureimport/main.qml b/examples/quick/scenegraph/metaltextureimport/main.qml
new file mode 100644
index 0000000000..facab4e440
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/main.qml
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+//! [1]
+import MetalTextureImport 1.0
+//! [1]
+
+Rectangle {
+ gradient: Gradient {
+ GradientStop { position: 0; color: "steelblue" }
+ GradientStop { position: 1; color: "black" }
+ }
+
+ //! [2]
+ CustomTextureItem {
+ id: renderer
+ anchors.fill: parent
+ anchors.margins: 10
+
+ SequentialAnimation on t {
+ NumberAnimation { to: 1; duration: 2500; easing.type: Easing.InQuad }
+ NumberAnimation { to: 0; duration: 2500; easing.type: Easing.OutQuad }
+ loops: Animation.Infinite
+ running: true
+ }
+ //! [2]
+
+ transform: [
+ Rotation { id: rotation; axis.x: 0; axis.z: 0; axis.y: 1; angle: 0; origin.x: renderer.width / 2; origin.y: renderer.height / 2; },
+ Translate { id: txOut; x: -renderer.width / 2; y: -renderer.height / 2 },
+ Scale { id: scale; },
+ Translate { id: txIn; x: renderer.width / 2; y: renderer.height / 2 }
+ ]
+ }
+
+ SequentialAnimation {
+ PauseAnimation { duration: 5000 }
+ ParallelAnimation {
+ NumberAnimation { target: scale; property: "xScale"; to: 0.6; duration: 1000; easing.type: Easing.InOutBack }
+ NumberAnimation { target: scale; property: "yScale"; to: 0.6; duration: 1000; easing.type: Easing.InOutBack }
+ }
+ NumberAnimation { target: rotation; property: "angle"; to: 80; duration: 1000; easing.type: Easing.InOutCubic }
+ NumberAnimation { target: rotation; property: "angle"; to: -80; duration: 1000; easing.type: Easing.InOutCubic }
+ NumberAnimation { target: rotation; property: "angle"; to: 0; duration: 1000; easing.type: Easing.InOutCubic }
+ NumberAnimation { target: renderer; property: "opacity"; to: 0.1; duration: 1000; easing.type: Easing.InOutCubic }
+ PauseAnimation { duration: 1000 }
+ NumberAnimation { target: renderer; property: "opacity"; to: 1.0; duration: 1000; easing.type: Easing.InOutCubic }
+ ParallelAnimation {
+ NumberAnimation { target: scale; property: "xScale"; to: 1; duration: 1000; easing.type: Easing.InOutBack }
+ NumberAnimation { target: scale; property: "yScale"; to: 1; duration: 1000; easing.type: Easing.InOutBack }
+ }
+ running: true
+ loops: Animation.Infinite
+ }
+
+ Rectangle {
+ id: labelFrame
+ anchors.margins: -10
+ radius: 5
+ color: "white"
+ border.color: "black"
+ opacity: 0.5
+ anchors.fill: label
+ }
+
+ Text {
+ id: label
+ anchors.bottom: renderer.bottom
+ anchors.left: renderer.left
+ anchors.right: renderer.right
+ anchors.margins: 20
+ wrapMode: Text.WordWrap
+ text: "The squircle, using rendering code borrowed from the metalunderqml example, is rendered into a texture directly with Metal. The MTLTexture is then imported and used in a custom Qt Quick item."
+ }
+}
diff --git a/examples/quick/scenegraph/metaltextureimport/metaltextureimport.h b/examples/quick/scenegraph/metaltextureimport/metaltextureimport.h
new file mode 100644
index 0000000000..afc5aced97
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/metaltextureimport.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef METALTEXTUREIMPORT_H
+#define METALTEXTUREIMPORT_H
+
+#include <QtQuick/QQuickItem>
+
+class CustomTextureNode;
+
+//! [1]
+class CustomTextureItem : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged)
+
+public:
+ CustomTextureItem();
+
+ qreal t() const { return m_t; }
+ void setT(qreal t);
+
+signals:
+ void tChanged();
+
+protected:
+ QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override;
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+
+private slots:
+ void invalidateSceneGraph();
+
+private:
+ void releaseResources() override;
+
+ CustomTextureNode *m_node = nullptr;
+ qreal m_t = 0;
+};
+//! [1]
+
+#endif // METALTEXTUREIMPORT_H
diff --git a/examples/quick/scenegraph/metaltextureimport/metaltextureimport.mm b/examples/quick/scenegraph/metaltextureimport/metaltextureimport.mm
new file mode 100644
index 0000000000..66a39083f7
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/metaltextureimport.mm
@@ -0,0 +1,424 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "metaltextureimport.h"
+#include <QtGui/QScreen>
+#include <QtQuick/QQuickWindow>
+#include <QtQuick/QSGTextureProvider>
+#include <QtQuick/QSGSimpleTextureNode>
+
+#include <Metal/Metal.h>
+
+//! [1]
+class CustomTextureNode : public QSGTextureProvider, public QSGSimpleTextureNode
+{
+ Q_OBJECT
+
+public:
+ CustomTextureNode(QQuickItem *item);
+ ~CustomTextureNode();
+
+ QSGTexture *texture() const override;
+
+ void sync();
+//! [1]
+private slots:
+ void render();
+
+private:
+ enum Stage {
+ VertexStage,
+ FragmentStage
+ };
+ void prepareShader(Stage stage);
+ using FuncAndLib = QPair<id<MTLFunction>, id<MTLLibrary> >;
+ FuncAndLib compileShaderFromSource(const QByteArray &src, const QByteArray &entryPoint);
+
+ QQuickItem *m_item;
+ QQuickWindow *m_window;
+ QSize m_size;
+ qreal m_dpr;
+ id<MTLDevice> m_device = nil;
+ id<MTLTexture> m_texture = nil;
+
+ bool m_initialized = false;
+ QByteArray m_vert;
+ QByteArray m_vertEntryPoint;
+ QByteArray m_frag;
+ QByteArray m_fragEntryPoint;
+ FuncAndLib m_vs;
+ FuncAndLib m_fs;
+ id<MTLBuffer> m_vbuf;
+ id<MTLBuffer> m_ubuf[3];
+ id<MTLRenderPipelineState> m_pipeline;
+
+ float m_t;
+};
+
+CustomTextureItem::CustomTextureItem()
+{
+ setFlag(ItemHasContents, true);
+}
+
+// The beauty of using a true QSGNode: no need for complicated cleanup
+// arrangements, unlike in other examples like metalunderqml, because the
+// scenegraph will handle destroying the node at the appropriate time.
+
+void CustomTextureItem::invalidateSceneGraph() // called on the render thread when the scenegraph is invalidated
+{
+ m_node = nullptr;
+}
+
+void CustomTextureItem::releaseResources() // called on the gui thread if the item is removed from scene
+{
+ m_node = nullptr;
+}
+
+//! [2]
+QSGNode *CustomTextureItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
+{
+ CustomTextureNode *n = static_cast<CustomTextureNode *>(node);
+
+ if (!n && (width() <= 0 || height() <= 0))
+ return nullptr;
+
+ if (!n) {
+ m_node = new CustomTextureNode(this);
+ n = m_node;
+ }
+
+ m_node->sync();
+
+ n->setTextureCoordinatesTransform(QSGSimpleTextureNode::NoTransform);
+ n->setFiltering(QSGTexture::Linear);
+ n->setRect(0, 0, width(), height());
+
+ window()->update(); // ensure getting to beforeRendering() at some point
+
+ return n;
+}
+//! [2]
+
+void CustomTextureItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ QQuickItem::geometryChanged(newGeometry, oldGeometry);
+
+ if (newGeometry.size() != oldGeometry.size())
+ update();
+}
+
+void CustomTextureItem::setT(qreal t)
+{
+ if (t == m_t)
+ return;
+
+ m_t = t;
+ emit tChanged();
+
+ update();
+}
+
+//! [3]
+CustomTextureNode::CustomTextureNode(QQuickItem *item)
+ : m_item(item)
+{
+ m_window = m_item->window();
+ connect(m_window, &QQuickWindow::beforeRendering, this, &CustomTextureNode::render);
+ connect(m_window, &QQuickWindow::screenChanged, this, [this]() {
+ if (m_window->effectiveDevicePixelRatio() != m_dpr)
+ m_item->update();
+ });
+//! [3]
+ m_vs.first = nil;
+ m_vs.second = nil;
+
+ m_fs.first = nil;
+ m_fs.second = nil;
+
+ m_vbuf = nil;
+
+ for (int i = 0; i < 3; ++i)
+ m_ubuf[i] = nil;
+
+ m_pipeline = nil;
+
+ qDebug("renderer created");
+}
+
+CustomTextureNode::~CustomTextureNode()
+{
+ [m_pipeline release];
+
+ [m_vbuf release];
+
+ for (int i = 0; i < 3; ++i)
+ [m_ubuf[i] release];
+
+ [m_vs.first release];
+ [m_vs.second release];
+
+ [m_fs.first release];
+ [m_fs.second release];
+
+ delete texture();
+ [m_texture release];
+
+ qDebug("renderer destroyed");
+}
+
+QSGTexture *CustomTextureNode::texture() const
+{
+ return QSGSimpleTextureNode::texture();
+}
+
+static const float vertices[] = {
+ -1, -1,
+ 1, -1,
+ -1, 1,
+ 1, 1
+};
+
+const int UBUF_SIZE = 4;
+
+//! [4]
+void CustomTextureNode::sync()
+{
+ m_dpr = m_window->effectiveDevicePixelRatio();
+ const QSize newSize = m_window->size() * m_dpr;
+ bool needsNew = false;
+
+ if (!texture())
+ needsNew = true;
+
+ if (newSize != m_size) {
+ needsNew = true;
+ m_size = newSize;
+ }
+
+ if (needsNew) {
+ delete texture();
+ [m_texture release];
+
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ m_device = (id<MTLDevice>) rif->getResource(m_window, QSGRendererInterface::DeviceResource);
+ Q_ASSERT(m_device);
+
+ MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
+ desc.textureType = MTLTextureType2D;
+ desc.pixelFormat = MTLPixelFormatRGBA8Unorm;
+ desc.width = m_size.width();
+ desc.height = m_size.height();
+ desc.mipmapLevelCount = 1;
+ desc.resourceOptions = MTLResourceStorageModePrivate;
+ desc.storageMode = MTLStorageModePrivate;
+ desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
+ m_texture = [m_device newTextureWithDescriptor: desc];
+ [desc release];
+
+ QSGTexture *wrapper = m_window->createTextureFromNativeObject(QQuickWindow::NativeObjectTexture,
+ &m_texture,
+ 0,
+ m_size);
+
+ qDebug() << "Got QSGTexture wrapper" << wrapper << "for an MTLTexture of size" << m_size;
+
+ setTexture(wrapper);
+ }
+//! [4]
+ if (!m_initialized && texture()) {
+ m_initialized = true;
+
+ prepareShader(VertexStage);
+ prepareShader(FragmentStage);
+
+ m_vs = compileShaderFromSource(m_vert, m_vertEntryPoint);
+ m_fs = compileShaderFromSource(m_frag, m_fragEntryPoint);
+
+ const int framesInFlight = m_window->graphicsStateInfo().framesInFlight;
+
+ m_vbuf = [m_device newBufferWithLength: sizeof(vertices) options: MTLResourceStorageModeShared];
+ void *p = [m_vbuf contents];
+ memcpy(p, vertices, sizeof(vertices));
+
+ for (int i = 0; i < framesInFlight; ++i)
+ m_ubuf[i] = [m_device newBufferWithLength: UBUF_SIZE options: MTLResourceStorageModeShared];
+
+ MTLVertexDescriptor *inputLayout = [MTLVertexDescriptor vertexDescriptor];
+ inputLayout.attributes[0].format = MTLVertexFormatFloat2;
+ inputLayout.attributes[0].offset = 0;
+ inputLayout.attributes[0].bufferIndex = 1; // ubuf is 0, vbuf is 1
+ inputLayout.layouts[1].stride = 2 * sizeof(float);
+
+ MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
+ rpDesc.vertexDescriptor = inputLayout;
+
+ rpDesc.vertexFunction = m_vs.first;
+ rpDesc.fragmentFunction = m_fs.first;
+
+ rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormatRGBA8Unorm;
+ rpDesc.colorAttachments[0].blendingEnabled = true;
+ rpDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
+ rpDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
+ rpDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOne;
+ rpDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOne;
+
+ NSError *err = nil;
+ m_pipeline = [m_device newRenderPipelineStateWithDescriptor: rpDesc error: &err];
+ if (!m_pipeline) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qFatal("Failed to create render pipeline state: %s", qPrintable(msg));
+ }
+ [rpDesc release];
+
+ qDebug("resources initialized");
+ }
+
+//! [5]
+ m_t = float(static_cast<CustomTextureItem *>(m_item)->t());
+//! [5]
+}
+
+// This is hooked up to beforeRendering() so we can start our own render
+// command encoder. If we instead wanted to use the scenegraph's render command
+// encoder (targeting the window), it should be connected to
+// beforeRenderPassRecording() instead.
+//! [6]
+void CustomTextureNode::render()
+{
+ if (!m_initialized)
+ return;
+
+ // Render to m_texture.
+ MTLRenderPassDescriptor *renderpassdesc = [MTLRenderPassDescriptor renderPassDescriptor];
+ MTLClearColor c = MTLClearColorMake(0, 0, 0, 1);
+ renderpassdesc.colorAttachments[0].loadAction = MTLLoadActionClear;
+ renderpassdesc.colorAttachments[0].storeAction = MTLStoreActionStore;
+ renderpassdesc.colorAttachments[0].clearColor = c;
+ renderpassdesc.colorAttachments[0].texture = m_texture;
+
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ id<MTLCommandBuffer> cb = (id<MTLCommandBuffer>) rif->getResource(m_window, QSGRendererInterface::CommandListResource);
+ Q_ASSERT(cb);
+ id<MTLRenderCommandEncoder> encoder = [cb renderCommandEncoderWithDescriptor: renderpassdesc];
+
+ const QQuickWindow::GraphicsStateInfo &stateInfo(m_window->graphicsStateInfo());
+ void *p = [m_ubuf[stateInfo.currentFrameSlot] contents];
+ memcpy(p, &m_t, 4);
+
+ MTLViewport vp;
+ vp.originX = 0;
+ vp.originY = 0;
+ vp.width = m_size.width();
+ vp.height = m_size.height();
+ vp.znear = 0;
+ vp.zfar = 1;
+ [encoder setViewport: vp];
+
+ [encoder setFragmentBuffer: m_ubuf[stateInfo.currentFrameSlot] offset: 0 atIndex: 0];
+ [encoder setVertexBuffer: m_vbuf offset: 0 atIndex: 1];
+ [encoder setRenderPipelineState: m_pipeline];
+ [encoder drawPrimitives: MTLPrimitiveTypeTriangleStrip vertexStart: 0 vertexCount: 4 instanceCount: 1 baseInstance: 0];
+
+ [encoder endEncoding];
+}
+//! [6]
+
+void CustomTextureNode::prepareShader(Stage stage)
+{
+ QString filename;
+ if (stage == VertexStage) {
+ filename = QLatin1String(":/scenegraph/metaltextureimport/squircle.vert");
+ } else {
+ Q_ASSERT(stage == FragmentStage);
+ filename = QLatin1String(":/scenegraph/metaltextureimport/squircle.frag");
+ }
+ QFile f(filename);
+ if (!f.open(QIODevice::ReadOnly))
+ qFatal("Failed to read shader %s", qPrintable(filename));
+
+ const QByteArray contents = f.readAll();
+
+ if (stage == VertexStage) {
+ m_vert = contents;
+ Q_ASSERT(!m_vert.isEmpty());
+ m_vertEntryPoint = QByteArrayLiteral("main0");
+ } else {
+ m_frag = contents;
+ Q_ASSERT(!m_frag.isEmpty());
+ m_fragEntryPoint = QByteArrayLiteral("main0");
+ }
+}
+
+CustomTextureNode::FuncAndLib CustomTextureNode::compileShaderFromSource(const QByteArray &src, const QByteArray &entryPoint)
+{
+ FuncAndLib fl;
+
+ NSString *srcstr = [NSString stringWithUTF8String: src.constData()];
+ MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
+ opts.languageVersion = MTLLanguageVersion1_2;
+ NSError *err = nil;
+ fl.second = [m_device newLibraryWithSource: srcstr options: opts error: &err];
+ [opts release];
+ // srcstr is autoreleased
+
+ if (err) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qFatal("%s", qPrintable(msg));
+ return fl;
+ }
+
+ NSString *name = [NSString stringWithUTF8String: entryPoint.constData()];
+ fl.first = [fl.second newFunctionWithName: name];
+ [name release];
+
+ return fl;
+}
+
+#include "metaltextureimport.moc"
diff --git a/examples/quick/scenegraph/metaltextureimport/metaltextureimport.pro b/examples/quick/scenegraph/metaltextureimport/metaltextureimport.pro
new file mode 100644
index 0000000000..5b11606946
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/metaltextureimport.pro
@@ -0,0 +1,12 @@
+!macos: error("This example requires macOS")
+
+QT += qml quick
+
+HEADERS += metaltextureimport.h
+SOURCES += metaltextureimport.mm main.cpp
+RESOURCES += metaltextureimport.qrc
+
+LIBS += -framework Metal -framework AppKit
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/metaltextureimport
+INSTALLS += target
diff --git a/examples/quick/scenegraph/metaltextureimport/metaltextureimport.qrc b/examples/quick/scenegraph/metaltextureimport/metaltextureimport.qrc
new file mode 100644
index 0000000000..7a8dd7a860
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/metaltextureimport.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/scenegraph/metaltextureimport">
+ <file>main.qml</file>
+ <file>squircle.vert</file>
+ <file>squircle.frag</file>
+ </qresource>
+</RCC>
diff --git a/examples/quick/scenegraph/metaltextureimport/squircle.frag b/examples/quick/scenegraph/metaltextureimport/squircle.frag
new file mode 100644
index 0000000000..15f34624fe
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/squircle.frag
@@ -0,0 +1,29 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct buf
+{
+ float t;
+};
+
+struct main0_out
+{
+ float4 fragColor [[color(0)]];
+};
+
+struct main0_in
+{
+ float2 coords [[user(locn0)]];
+};
+
+fragment main0_out main0(main0_in in [[stage_in]], constant buf& ubuf [[buffer(0)]])
+{
+ main0_out out = {};
+ float i = 1.0 - (pow(abs(in.coords.x), 4.0) + pow(abs(in.coords.y), 4.0));
+ i = smoothstep(ubuf.t - 0.800000011920928955078125, ubuf.t + 0.800000011920928955078125, i);
+ i = floor(i * 20.0) / 20.0;
+ out.fragColor = float4((in.coords * 0.5) + float2(0.5), i, i);
+ return out;
+}
diff --git a/examples/quick/scenegraph/metaltextureimport/squircle.vert b/examples/quick/scenegraph/metaltextureimport/squircle.vert
new file mode 100644
index 0000000000..a88c59f541
--- /dev/null
+++ b/examples/quick/scenegraph/metaltextureimport/squircle.vert
@@ -0,0 +1,23 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct main0_out
+{
+ float2 coords [[user(locn0)]];
+ float4 gl_Position [[position]];
+};
+
+struct main0_in
+{
+ float4 vertices [[attribute(0)]];
+};
+
+vertex main0_out main0(main0_in in [[stage_in]])
+{
+ main0_out out = {};
+ out.gl_Position = in.vertices;
+ out.coords = in.vertices.xy;
+ return out;
+}
diff --git a/examples/quick/scenegraph/metalunderqml/doc/images/metalunderqml-example.jpg b/examples/quick/scenegraph/metalunderqml/doc/images/metalunderqml-example.jpg
new file mode 100644
index 0000000000..98085773de
--- /dev/null
+++ b/examples/quick/scenegraph/metalunderqml/doc/images/metalunderqml-example.jpg
Binary files differ
diff --git a/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc b/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc
new file mode 100644
index 0000000000..fb28270315
--- /dev/null
+++ b/examples/quick/scenegraph/metalunderqml/doc/src/metalunderqml.qdoc
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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$
+**
+****************************************************************************/
+
+/*!
+ \example scenegraph/metalunderqml
+ \title Scene Graph - Metal Under QML
+ \ingroup qtquickexamples
+ \brief Shows how to render directly with Metal under a Qt Quick scene.
+
+ \image metalunderqml-example.jpg
+
+ The Metal Under QML example shows how an application can make use
+ of the \l QQuickWindow::beforeRendering() and \l
+ QQuickWindow::beforeRenderPassRecording() signals to draw custom
+ Metal content under a Qt Quick scene. This signal is emitted at
+ the start of every frame, before the scene graph starts its
+ rendering, thus any Metal draw calls that are made as a response
+ to this signal, will stack under the Qt Quick items. There are two
+ signals, because the custom Metal commands are recorded onto the
+ same command buffer with the same render command encoder that the
+ scene graph uses. beforeRendering() on its own is not sufficient
+ for this because it gets emitted at the start of the frame, before
+ having an
+ \l{https://developer.apple.com/documentation/metal/mtlrendercommandencoder}{MTLRenderCommandEncoder}
+ available. By also connecting to beforeRenderPassRecording(), the
+ application can gain access to the necessary native objects.
+
+ As an alternative, applications that wish to render Metal content
+ on top of the Qt Quick scene, can do so by connecting to the \l
+ QQuickWindow::afterRendering() and \l
+ QQuickWindow::afterRenderPassRecording() signals.
+
+ In this example, we will also see how it is possible to have
+ values that are exposed to QML which affect the Metal
+ rendering. We animate the threshold value using a NumberAnimation
+ in the QML file and this value is used by the Metal shader
+ program that draws the squircles.
+
+ The example is equivalent in most ways to the \l{Scene Graph - OpenGL Under
+ QML}{OpenGL Under QML}, \l{Scene Graph - Direct3D 11 Under QML}{Direct3D 11
+ Under QML}, and \l{Scene Graph - Vulkan Under QML}{Vulkan Under QML}
+ examples, they all render the same custom content, just via different
+ native APIs.
+
+ */
diff --git a/examples/quick/scenegraph/metalunderqml/main.cpp b/examples/quick/scenegraph/metalunderqml/main.cpp
new file mode 100644
index 0000000000..5ad337abb1
--- /dev/null
+++ b/examples/quick/scenegraph/metalunderqml/main.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QtQuick/QQuickView>
+#include "metalsquircle.h"
+
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+
+ qmlRegisterType<MetalSquircle>("MetalUnderQML", 1, 0, "MetalSquircle");
+
+ QQuickWindow::setSceneGraphBackend(QSGRendererInterface::MetalRhi);
+
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl("qrc:///scenegraph/metalunderqml/main.qml"));
+ view.show();
+
+ return app.exec();
+}
diff --git a/examples/quick/scenegraph/metalunderqml/main.qml b/examples/quick/scenegraph/metalunderqml/main.qml
new file mode 100644
index 0000000000..d41a0b8e81
--- /dev/null
+++ b/examples/quick/scenegraph/metalunderqml/main.qml
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [1]
+import QtQuick 2.0
+import MetalUnderQML 1.0
+
+Item {
+
+ width: 320
+ height: 480
+
+ MetalSquircle {
+ SequentialAnimation on t {
+ NumberAnimation { to: 1; duration: 2500; easing.type: Easing.InQuad }
+ NumberAnimation { to: 0; duration: 2500; easing.type: Easing.OutQuad }
+ loops: Animation.Infinite
+ running: true
+ }
+ }
+//! [1] //! [2]
+ Rectangle {
+ color: Qt.rgba(1, 1, 1, 0.7)
+ radius: 10
+ border.width: 1
+ border.color: "white"
+ anchors.fill: label
+ anchors.margins: -10
+ }
+
+ Text {
+ id: label
+ color: "black"
+ wrapMode: Text.WordWrap
+ text: "The background here is a squircle rendered with raw Metal using the beforeRendering() and beforeRenderPassRecording() signals in QQuickWindow. This text label and its border is rendered using QML"
+ anchors.right: parent.right
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ anchors.margins: 20
+ }
+}
+//! [2]
diff --git a/examples/quick/scenegraph/metalunderqml/metalsquircle.h b/examples/quick/scenegraph/metalunderqml/metalsquircle.h
new file mode 100644
index 0000000000..43c4afad21
--- /dev/null
+++ b/examples/quick/scenegraph/metalunderqml/metalsquircle.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef METALSQUIRCLE_H
+#define METALSQUIRCLE_H
+
+#include <QtQuick/QQuickItem>
+
+class SquircleRenderer;
+
+class MetalSquircle : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged)
+
+public:
+ MetalSquircle();
+
+ qreal t() const { return m_t; }
+ void setT(qreal t);
+
+signals:
+ void tChanged();
+
+public slots:
+ void sync();
+ void cleanup();
+
+private slots:
+ void handleWindowChanged(QQuickWindow *win);
+
+private:
+ void releaseResources() override;
+
+ qreal m_t;
+ SquircleRenderer *m_renderer = nullptr;
+};
+
+#endif
diff --git a/examples/quick/scenegraph/metalunderqml/metalsquircle.mm b/examples/quick/scenegraph/metalunderqml/metalsquircle.mm
new file mode 100644
index 0000000000..5ca6daa01a
--- /dev/null
+++ b/examples/quick/scenegraph/metalunderqml/metalsquircle.mm
@@ -0,0 +1,369 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "metalsquircle.h"
+#include <QtCore/QRunnable>
+#include <QtQuick/QQuickWindow>
+
+#include <Metal/Metal.h>
+
+class SquircleRenderer : public QObject
+{
+ Q_OBJECT
+public:
+ SquircleRenderer();
+ ~SquircleRenderer();
+
+ void setT(qreal t) { m_t = t; }
+ void setViewportSize(const QSize &size) { m_viewportSize = size; }
+ void setWindow(QQuickWindow *window) { m_window = window; }
+
+public slots:
+ void frameStart();
+ void mainPassRecordingStart();
+
+private:
+ enum Stage {
+ VertexStage,
+ FragmentStage
+ };
+ void prepareShader(Stage stage);
+ using FuncAndLib = QPair<id<MTLFunction>, id<MTLLibrary> >;
+ FuncAndLib compileShaderFromSource(const QByteArray &src, const QByteArray &entryPoint);
+ void init(int framesInFlight);
+
+ QSize m_viewportSize;
+ qreal m_t;
+ QQuickWindow *m_window;
+
+ QByteArray m_vert;
+ QByteArray m_vertEntryPoint;
+ QByteArray m_frag;
+ QByteArray m_fragEntryPoint;
+
+ bool m_initialized = false;
+ id<MTLDevice> m_device;
+ id<MTLBuffer> m_vbuf;
+ id<MTLBuffer> m_ubuf[3];
+ FuncAndLib m_vs;
+ FuncAndLib m_fs;
+ id<MTLRenderPipelineState> m_pipeline;
+};
+
+MetalSquircle::MetalSquircle()
+ : m_t(0)
+ , m_renderer(nullptr)
+{
+ connect(this, &QQuickItem::windowChanged, this, &MetalSquircle::handleWindowChanged);
+}
+
+void MetalSquircle::setT(qreal t)
+{
+ if (t == m_t)
+ return;
+ m_t = t;
+ emit tChanged();
+ if (window())
+ window()->update();
+}
+
+void MetalSquircle::handleWindowChanged(QQuickWindow *win)
+{
+ if (win) {
+ connect(win, &QQuickWindow::beforeSynchronizing, this, &MetalSquircle::sync, Qt::DirectConnection);
+ connect(win, &QQuickWindow::sceneGraphInvalidated, this, &MetalSquircle::cleanup, Qt::DirectConnection);
+
+ // Ensure we start with cleared to black. The squircle's blend mode relies on this.
+ win->setColor(Qt::black);
+ }
+}
+
+SquircleRenderer::SquircleRenderer()
+ : m_t(0)
+{
+ m_vbuf = nil;
+
+ for (int i = 0; i < 3; ++i)
+ m_ubuf[i] = nil;
+
+ m_vs.first = nil;
+ m_vs.second = nil;
+
+ m_fs.first = nil;
+ m_fs.second = nil;
+}
+
+// The safe way to release custom graphics resources is to both connect to
+// sceneGraphInvalidated() and implement releaseResources(). To support
+// threaded render loops the latter performs the SquircleRenderer destruction
+// via scheduleRenderJob(). Note that the MetalSquircle may be gone by the time
+// the QRunnable is invoked.
+
+void MetalSquircle::cleanup()
+{
+ delete m_renderer;
+ m_renderer = nullptr;
+}
+
+class CleanupJob : public QRunnable
+{
+public:
+ CleanupJob(SquircleRenderer *renderer) : m_renderer(renderer) { }
+ void run() override { delete m_renderer; }
+private:
+ SquircleRenderer *m_renderer;
+};
+
+void MetalSquircle::releaseResources()
+{
+ window()->scheduleRenderJob(new CleanupJob(m_renderer), QQuickWindow::BeforeSynchronizingStage);
+ m_renderer = nullptr;
+}
+
+SquircleRenderer::~SquircleRenderer()
+{
+ qDebug("cleanup");
+
+ [m_vbuf release];
+ for (int i = 0; i < 3; ++i)
+ [m_ubuf[i] release];
+
+ [m_vs.first release];
+ [m_vs.second release];
+
+ [m_fs.first release];
+ [m_fs.second release];
+}
+
+void MetalSquircle::sync()
+{
+ if (!m_renderer) {
+ m_renderer = new SquircleRenderer;
+ // Initializing resources is done before starting to encode render
+ // commands, regardless of wanting an underlay or overlay.
+ connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::frameStart, Qt::DirectConnection);
+ // Here we want an underlay and therefore connect to
+ // beforeRenderPassRecording. Changing to afterRenderPassRecording
+ // would render the squircle on top (overlay).
+ connect(window(), &QQuickWindow::beforeRenderPassRecording, m_renderer, &SquircleRenderer::mainPassRecordingStart, Qt::DirectConnection);
+ }
+ m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio());
+ m_renderer->setT(m_t);
+ m_renderer->setWindow(window());
+}
+
+void SquircleRenderer::frameStart()
+{
+ QSGRendererInterface *rif = m_window->rendererInterface();
+
+ // We are not prepared for anything other than running with the RHI and its Metal backend.
+ Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::MetalRhi);
+
+ m_device = (id<MTLDevice>) rif->getResource(m_window, QSGRendererInterface::DeviceResource);
+ Q_ASSERT(m_device);
+
+ if (m_vert.isEmpty())
+ prepareShader(VertexStage);
+ if (m_frag.isEmpty())
+ prepareShader(FragmentStage);
+
+ if (!m_initialized)
+ init(m_window->graphicsStateInfo().framesInFlight);
+}
+
+static const float vertices[] = {
+ -1, -1,
+ 1, -1,
+ -1, 1,
+ 1, 1
+};
+
+const int UBUF_SIZE = 4;
+
+void SquircleRenderer::mainPassRecordingStart()
+{
+ // This example demonstrates the simple case: prepending some commands to
+ // the scenegraph's main renderpass. It does not create its own passes,
+ // rendertargets, etc. so no synchronization is needed.
+
+ const QQuickWindow::GraphicsStateInfo &stateInfo(m_window->graphicsStateInfo());
+
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ id<MTLRenderCommandEncoder> encoder = (id<MTLRenderCommandEncoder>) rif->getResource(
+ m_window, QSGRendererInterface::CommandEncoderResource);
+ Q_ASSERT(encoder);
+
+ m_window->beginExternalCommands();
+
+ void *p = [m_ubuf[stateInfo.currentFrameSlot] contents];
+ float t = m_t;
+ memcpy(p, &t, 4);
+
+ MTLViewport vp;
+ vp.originX = 0;
+ vp.originY = 0;
+ vp.width = m_viewportSize.width();
+ vp.height = m_viewportSize.height();
+ vp.znear = 0;
+ vp.zfar = 1;
+ [encoder setViewport: vp];
+
+ [encoder setFragmentBuffer: m_ubuf[stateInfo.currentFrameSlot] offset: 0 atIndex: 0];
+ [encoder setVertexBuffer: m_vbuf offset: 0 atIndex: 1];
+ [encoder setRenderPipelineState: m_pipeline];
+ [encoder drawPrimitives: MTLPrimitiveTypeTriangleStrip vertexStart: 0 vertexCount: 4 instanceCount: 1 baseInstance: 0];
+
+ m_window->endExternalCommands();
+}
+
+void SquircleRenderer::prepareShader(Stage stage)
+{
+ QString filename;
+ if (stage == VertexStage) {
+ filename = QLatin1String(":/scenegraph/metalunderqml/squircle.vert");
+ } else {
+ Q_ASSERT(stage == FragmentStage);
+ filename = QLatin1String(":/scenegraph/metalunderqml/squircle.frag");
+ }
+ QFile f(filename);
+ if (!f.open(QIODevice::ReadOnly))
+ qFatal("Failed to read shader %s", qPrintable(filename));
+
+ const QByteArray contents = f.readAll();
+
+ if (stage == VertexStage) {
+ m_vert = contents;
+ Q_ASSERT(!m_vert.isEmpty());
+ m_vertEntryPoint = QByteArrayLiteral("main0");
+ } else {
+ m_frag = contents;
+ Q_ASSERT(!m_frag.isEmpty());
+ m_fragEntryPoint = QByteArrayLiteral("main0");
+ }
+}
+
+SquircleRenderer::FuncAndLib SquircleRenderer::compileShaderFromSource(const QByteArray &src, const QByteArray &entryPoint)
+{
+ FuncAndLib fl;
+
+ NSString *srcstr = [NSString stringWithUTF8String: src.constData()];
+ MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
+ opts.languageVersion = MTLLanguageVersion1_2;
+ NSError *err = nil;
+ fl.second = [m_device newLibraryWithSource: srcstr options: opts error: &err];
+ [opts release];
+ // srcstr is autoreleased
+
+ if (err) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qFatal("%s", qPrintable(msg));
+ return fl;
+ }
+
+ NSString *name = [NSString stringWithUTF8String: entryPoint.constData()];
+ fl.first = [fl.second newFunctionWithName: name];
+ [name release];
+
+ return fl;
+}
+
+void SquircleRenderer::init(int framesInFlight)
+{
+ qDebug("init");
+
+ Q_ASSERT(framesInFlight <= 3);
+ m_initialized = true;
+
+ m_vbuf = [m_device newBufferWithLength: sizeof(vertices) options: MTLResourceStorageModeShared];
+ void *p = [m_vbuf contents];
+ memcpy(p, vertices, sizeof(vertices));
+
+ for (int i = 0; i < framesInFlight; ++i)
+ m_ubuf[i] = [m_device newBufferWithLength: UBUF_SIZE options: MTLResourceStorageModeShared];
+
+ MTLVertexDescriptor *inputLayout = [MTLVertexDescriptor vertexDescriptor];
+ inputLayout.attributes[0].format = MTLVertexFormatFloat2;
+ inputLayout.attributes[0].offset = 0;
+ inputLayout.attributes[0].bufferIndex = 1; // ubuf is 0, vbuf is 1
+ inputLayout.layouts[1].stride = 2 * sizeof(float);
+
+ MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
+ rpDesc.vertexDescriptor = inputLayout;
+
+ m_vs = compileShaderFromSource(m_vert, m_vertEntryPoint);
+ rpDesc.vertexFunction = m_vs.first;
+ m_fs = compileShaderFromSource(m_frag, m_fragEntryPoint);
+ rpDesc.fragmentFunction = m_fs.first;
+
+ rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
+ rpDesc.colorAttachments[0].blendingEnabled = true;
+ rpDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
+ rpDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
+ rpDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOne;
+ rpDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOne;
+
+ if (m_device.depth24Stencil8PixelFormatSupported) {
+ rpDesc.depthAttachmentPixelFormat = MTLPixelFormatDepth24Unorm_Stencil8;
+ rpDesc.stencilAttachmentPixelFormat = MTLPixelFormatDepth24Unorm_Stencil8;
+ } else {
+ rpDesc.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
+ rpDesc.stencilAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
+ }
+
+ NSError *err = nil;
+ m_pipeline = [m_device newRenderPipelineStateWithDescriptor: rpDesc error: &err];
+ if (!m_pipeline) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qFatal("Failed to create render pipeline state: %s", qPrintable(msg));
+ }
+ [rpDesc release];
+}
+
+#include "metalsquircle.moc"
diff --git a/examples/quick/scenegraph/metalunderqml/metalunderqml.pro b/examples/quick/scenegraph/metalunderqml/metalunderqml.pro
new file mode 100644
index 0000000000..9fd131fe1b
--- /dev/null
+++ b/examples/quick/scenegraph/metalunderqml/metalunderqml.pro
@@ -0,0 +1,12 @@
+!macos: error("This example requires macOS")
+
+QT += qml quick
+
+HEADERS += metalsquircle.h
+SOURCES += metalsquircle.mm main.cpp
+RESOURCES += metalunderqml.qrc
+
+LIBS += -framework Metal -framework AppKit
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/metalunderqml
+INSTALLS += target
diff --git a/examples/quick/scenegraph/metalunderqml/metalunderqml.qrc b/examples/quick/scenegraph/metalunderqml/metalunderqml.qrc
new file mode 100644
index 0000000000..7172f1fcb7
--- /dev/null
+++ b/examples/quick/scenegraph/metalunderqml/metalunderqml.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/scenegraph/metalunderqml">
+ <file>main.qml</file>
+ <file>squircle.vert</file>
+ <file>squircle.frag</file>
+ </qresource>
+</RCC>
diff --git a/examples/quick/scenegraph/metalunderqml/squircle.frag b/examples/quick/scenegraph/metalunderqml/squircle.frag
new file mode 100644
index 0000000000..15f34624fe
--- /dev/null
+++ b/examples/quick/scenegraph/metalunderqml/squircle.frag
@@ -0,0 +1,29 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct buf
+{
+ float t;
+};
+
+struct main0_out
+{
+ float4 fragColor [[color(0)]];
+};
+
+struct main0_in
+{
+ float2 coords [[user(locn0)]];
+};
+
+fragment main0_out main0(main0_in in [[stage_in]], constant buf& ubuf [[buffer(0)]])
+{
+ main0_out out = {};
+ float i = 1.0 - (pow(abs(in.coords.x), 4.0) + pow(abs(in.coords.y), 4.0));
+ i = smoothstep(ubuf.t - 0.800000011920928955078125, ubuf.t + 0.800000011920928955078125, i);
+ i = floor(i * 20.0) / 20.0;
+ out.fragColor = float4((in.coords * 0.5) + float2(0.5), i, i);
+ return out;
+}
diff --git a/examples/quick/scenegraph/metalunderqml/squircle.vert b/examples/quick/scenegraph/metalunderqml/squircle.vert
new file mode 100644
index 0000000000..a88c59f541
--- /dev/null
+++ b/examples/quick/scenegraph/metalunderqml/squircle.vert
@@ -0,0 +1,23 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct main0_out
+{
+ float2 coords [[user(locn0)]];
+ float4 gl_Position [[position]];
+};
+
+struct main0_in
+{
+ float4 vertices [[attribute(0)]];
+};
+
+vertex main0_out main0(main0_in in [[stage_in]])
+{
+ main0_out out = {};
+ out.gl_Position = in.vertices;
+ out.coords = in.vertices.xy;
+ return out;
+}
diff --git a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc
index 3d4f4443e9..9676815c44 100644
--- a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc
+++ b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc
@@ -50,6 +50,12 @@
in the QML file and this value is used by the OpenGL shader
program that draws the squircles.
+ The example is equivalent in most ways to the \l{Scene Graph - Direct3D 11
+ Under QML}{Direct3D 11 Under QML}, \l{Scene Graph - Metal Under QML}{Metal
+ Under QML}, and \l{Scene Graph - Vulkan Under QML}{Vulkan Under QML}
+ examples, they all render the same custom content, just via different
+ native APIs.
+
\snippet scenegraph/openglunderqml/squircle.h 2
First of all, we need an object we can expose to QML. This is a
@@ -94,17 +100,18 @@
\snippet scenegraph/openglunderqml/squircle.cpp 3
- The default behavior of the scene graph is to clear the
- framebuffer before rendering. Since we render before the scene
- graph, we need to turn this clearing off. This means that we need
- to clear ourselves in the \c paint() function.
+ The default behavior of the scene graph is to clear the framebuffer before
+ rendering. This is fine since we will insert our own rendering code after
+ this clear is enqueued. Make sure however that we clear to the desired
+ color (black).
\snippet scenegraph/openglunderqml/squircle.cpp 9
- We use the \c sync() function to initialize the renderer and to
- copy the state in our item into the renderer. When the renderer is
- created, we also connect the \l QQuickWindow::beforeRendering() to
- the renderer's \c paint() slot.
+ We use the \c sync() function to initialize the renderer and to copy the
+ state in our item into the renderer. When the renderer is created, we also
+ connect the \l QQuickWindow::beforeRendering() and \l
+ QQuickWindow::beforeRenderPassRecording() to the renderer's \c init() and
+ \c paint() slots.
\note The \l QQuickWindow::beforeSynchronizing() signal is emitted
on the rendering thread while the GUI thread is blocked, so it is
@@ -112,8 +119,11 @@
\snippet scenegraph/openglunderqml/squircle.cpp 6
- In the \c cleanup() function we delete the renderer which in turn
- cleans up its own resources.
+ In the \c cleanup() function we delete the renderer which in turn cleans up
+ its own resources. This is complemented by reimplementing \l
+ QQuickWindow::releaseResources() since just connecting to the
+ sceneGraphInvalidated() signal is not sufficient on its own to handle all
+ cases.
\snippet scenegraph/openglunderqml/squircle.cpp 8
@@ -124,22 +134,13 @@
\snippet scenegraph/openglunderqml/squircle.cpp 4
- In the SquircleRenderer's \c paint() function we start by
- initializing the shader program. By initializing the shader
- program here, we make sure that the OpenGL context is bound and
- that we are on the correct thread.
+ In the SquircleRenderer's \c init() function we start by initializing the
+ shader program if not yet done. The OpenGL context is current on the thread
+ when the slot is invoked.
\snippet scenegraph/openglunderqml/squircle.cpp 5
- We use the shader program to draw the squircle. At the end of the
- \c paint function we release the program and disable the
- attributes we used so that the OpenGL context is in a "clean"
- state for the scene graph to pick it up.
-
- \note If tracking the changes in the OpenGL context's state is not
- feasible, one can use the function \l
- QQuickWindow::resetOpenGLState() which will reset all state that
- the scene graph relies on.
+ We use the shader program to draw the squircle in \c paint().
\snippet scenegraph/openglunderqml/main.cpp 1
diff --git a/examples/quick/scenegraph/openglunderqml/squircle.cpp b/examples/quick/scenegraph/openglunderqml/squircle.cpp
index d6f6b327f2..828857fe24 100644
--- a/examples/quick/scenegraph/openglunderqml/squircle.cpp
+++ b/examples/quick/scenegraph/openglunderqml/squircle.cpp
@@ -53,6 +53,7 @@
#include <QtQuick/qquickwindow.h>
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLContext>
+#include <QtCore/QRunnable>
//! [7]
Squircle::Squircle()
@@ -82,10 +83,9 @@ void Squircle::handleWindowChanged(QQuickWindow *win)
connect(win, &QQuickWindow::beforeSynchronizing, this, &Squircle::sync, Qt::DirectConnection);
connect(win, &QQuickWindow::sceneGraphInvalidated, this, &Squircle::cleanup, Qt::DirectConnection);
//! [1]
- // If we allow QML to do the clearing, they would clear what we paint
- // and nothing would show.
//! [3]
- win->setClearBeforeRendering(false);
+ // Ensure we start with cleared to black. The squircle's blend mode relies on this.
+ win->setColor(Qt::black);
}
}
//! [3]
@@ -93,10 +93,23 @@ void Squircle::handleWindowChanged(QQuickWindow *win)
//! [6]
void Squircle::cleanup()
{
- if (m_renderer) {
- delete m_renderer;
- m_renderer = nullptr;
- }
+ delete m_renderer;
+ m_renderer = nullptr;
+}
+
+class CleanupJob : public QRunnable
+{
+public:
+ CleanupJob(SquircleRenderer *renderer) : m_renderer(renderer) { }
+ void run() override { delete m_renderer; }
+private:
+ SquircleRenderer *m_renderer;
+};
+
+void Squircle::releaseResources()
+{
+ window()->scheduleRenderJob(new CleanupJob(m_renderer), QQuickWindow::BeforeSynchronizingStage);
+ m_renderer = nullptr;
}
SquircleRenderer::~SquircleRenderer()
@@ -110,7 +123,8 @@ void Squircle::sync()
{
if (!m_renderer) {
m_renderer = new SquircleRenderer();
- connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::paint, Qt::DirectConnection);
+ connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::init, Qt::DirectConnection);
+ connect(window(), &QQuickWindow::beforeRenderPassRecording, m_renderer, &SquircleRenderer::paint, Qt::DirectConnection);
}
m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio());
m_renderer->setT(m_t);
@@ -119,9 +133,12 @@ void Squircle::sync()
//! [9]
//! [4]
-void SquircleRenderer::paint()
+void SquircleRenderer::init()
{
if (!m_program) {
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::OpenGL || rif->graphicsApi() == QSGRendererInterface::OpenGLRhi);
+
initializeOpenGLFunctions();
m_program = new QOpenGLShaderProgram();
@@ -146,7 +163,15 @@ void SquircleRenderer::paint()
m_program->link();
}
+}
+
//! [4] //! [5]
+void SquircleRenderer::paint()
+{
+ // Play nice with the RHI. Not strictly needed when the scenegraph uses
+ // OpenGL directly.
+ m_window->beginExternalCommands();
+
m_program->bind();
m_program->enableAttributeArray(0);
@@ -157,6 +182,11 @@ void SquircleRenderer::paint()
-1, 1,
1, 1
};
+
+ // This example relies on (deprecated) client-side pointers for the vertex
+ // input. Therefore, we have to make sure no vertex buffer is bound.
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
m_program->setAttributeArray(0, GL_FLOAT, values, 2);
m_program->setUniformValue("t", (float) m_t);
@@ -164,9 +194,6 @@ void SquircleRenderer::paint()
glDisable(GL_DEPTH_TEST);
- glClearColor(0, 0, 0, 1);
- glClear(GL_COLOR_BUFFER_BIT);
-
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
@@ -178,5 +205,7 @@ void SquircleRenderer::paint()
// Not strictly needed for this example, but generally useful for when
// mixing with raw OpenGL.
m_window->resetOpenGLState();
+
+ m_window->endExternalCommands();
}
//! [5]
diff --git a/examples/quick/scenegraph/openglunderqml/squircle.h b/examples/quick/scenegraph/openglunderqml/squircle.h
index 652e679f81..1b9995bc1e 100644
--- a/examples/quick/scenegraph/openglunderqml/squircle.h
+++ b/examples/quick/scenegraph/openglunderqml/squircle.h
@@ -70,6 +70,7 @@ public:
void setWindow(QQuickWindow *window) { m_window = window; }
public slots:
+ void init();
void paint();
private:
@@ -103,6 +104,8 @@ private slots:
void handleWindowChanged(QQuickWindow *win);
private:
+ void releaseResources() override;
+
qreal m_t;
SquircleRenderer *m_renderer;
};
diff --git a/examples/quick/scenegraph/rendernode/customrenderitem.cpp b/examples/quick/scenegraph/rendernode/customrenderitem.cpp
index 8f248e2ecb..e55cf0a2f4 100644
--- a/examples/quick/scenegraph/rendernode/customrenderitem.cpp
+++ b/examples/quick/scenegraph/rendernode/customrenderitem.cpp
@@ -53,42 +53,78 @@
#include <QSGRendererInterface>
#include "openglrenderer.h"
+#include "metalrenderer.h"
#include "d3d12renderer.h"
#include "softwarerenderer.h"
+//! [1]
CustomRenderItem::CustomRenderItem(QQuickItem *parent)
: QQuickItem(parent)
{
// Our item shows something so set the flag.
setFlag(ItemHasContents);
}
+//! [1]
+//! [2]
QSGNode *CustomRenderItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
{
QSGRenderNode *n = static_cast<QSGRenderNode *>(node);
- if (!n) {
- QSGRendererInterface *ri = window()->rendererInterface();
- if (!ri)
- return nullptr;
- switch (ri->graphicsApi()) {
- case QSGRendererInterface::OpenGL:
+
+ QSGRendererInterface *ri = window()->rendererInterface();
+ if (!ri)
+ return nullptr;
+
+ switch (ri->graphicsApi()) {
+ case QSGRendererInterface::OpenGL:
+ Q_FALLTHROUGH();
+ case QSGRendererInterface::OpenGLRhi:
#if QT_CONFIG(opengl)
- n = new OpenGLRenderNode(this);
- break;
+ if (!n)
+ n = new OpenGLRenderNode;
+ static_cast<OpenGLRenderNode *>(n)->sync(this);
+#endif
+ break;
+
+ case QSGRendererInterface::MetalRhi:
+// Restore when QTBUG-78580 is done and the .pro is updated accordingly
+//#ifdef Q_OS_DARWIN
+#ifdef Q_OS_MACOS
+ if (!n) {
+ MetalRenderNode *metalNode = new MetalRenderNode;
+ n = metalNode;
+ metalNode->resourceBuilder()->setWindow(window());
+ QObject::connect(window(), &QQuickWindow::beforeRendering,
+ metalNode->resourceBuilder(), &MetalRenderNodeResourceBuilder::build);
+ }
+ static_cast<MetalRenderNode *>(n)->sync(this);
#endif
- case QSGRendererInterface::Direct3D12:
+ break;
+
+ case QSGRendererInterface::Direct3D12: // ### Qt 6: remove
#if QT_CONFIG(d3d12)
- n = new D3D12RenderNode(this);
- break;
+ if (!n)
+ n = new D3D12RenderNode;
+ static_cast<D3D12RenderNode *>(n)->sync(this);
#endif
- case QSGRendererInterface::Software:
- n = new SoftwareRenderNode(this);
- break;
+ break;
- default:
- return nullptr;
- }
+ case QSGRendererInterface::Software:
+ if (!n)
+ n = new SoftwareRenderNode;
+ static_cast<SoftwareRenderNode *>(n)->sync(this);
+ break;
+
+ default:
+ break;
}
+ if (!n)
+ qWarning("QSGRendererInterface reports unknown graphics API %d", ri->graphicsApi());
+
return n;
}
+//! [2]
+
+// This item does not support being moved between windows. If that is desired,
+// itemChange() should be reimplemented as well.
diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp
index df0e29eb67..e85811c089 100644
--- a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp
+++ b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp
@@ -54,12 +54,9 @@
#include <QSGRendererInterface>
#include <QFile>
-#if QT_CONFIG(d3d12)
+// ### Qt 6: remove
-D3D12RenderNode::D3D12RenderNode(QQuickItem *item)
- : m_item(item)
-{
-}
+#if QT_CONFIG(d3d12)
D3D12RenderNode::~D3D12RenderNode()
{
@@ -85,8 +82,8 @@ void D3D12RenderNode::releaseResources()
void D3D12RenderNode::init()
{
- QSGRendererInterface *rif = m_item->window()->rendererInterface();
- m_device = static_cast<ID3D12Device *>(rif->getResource(m_item->window(), QSGRendererInterface::DeviceResource));
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ m_device = static_cast<ID3D12Device *>(rif->getResource(m_window, QSGRendererInterface::DeviceResource));
Q_ASSERT(m_device);
D3D12_ROOT_PARAMETER rootParameter;
@@ -166,8 +163,8 @@ void D3D12RenderNode::init()
psoDesc.RasterizerState = rastDesc;
psoDesc.BlendState = blendDesc;
// No depth. The correct stacking of the item is ensured by the projection matrix.
- // Do not bother with stencil since we do not apply clipping in the
- // example. If clipping is desired, render() needs to set a different PSO
+ // Note that this does not support clipping.
+ // If clipping is desired, render() needs to set a different PSO
// with stencil enabled whenever the RenderState indicates so.
psoDesc.SampleMask = UINT_MAX;
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
@@ -176,7 +173,7 @@ void D3D12RenderNode::init()
psoDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; // not in use due to !DepthEnable, but this would be the correct format otherwise
// We are rendering on the default render target so if the QuickWindow/View
// has requested samples > 0 then we have to follow suit.
- const uint samples = qMax(1, m_item->window()->format().samples());
+ const uint samples = qMax(1, m_window->format().samples());
psoDesc.SampleDesc.Count = samples;
if (samples > 1) {
D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msaaInfo = {};
@@ -255,9 +252,9 @@ void D3D12RenderNode::render(const RenderState *state)
if (!m_device)
init();
- QSGRendererInterface *rif = m_item->window()->rendererInterface();
+ QSGRendererInterface *rif = m_window->rendererInterface();
ID3D12GraphicsCommandList *commandList = static_cast<ID3D12GraphicsCommandList *>(
- rif->getResource(m_item->window(), QSGRendererInterface::CommandListResource));
+ rif->getResource(m_window, QSGRendererInterface::CommandListResource));
Q_ASSERT(commandList);
const int msize = 16 * sizeof(float);
@@ -266,9 +263,9 @@ void D3D12RenderNode::render(const RenderState *state)
const float opacity = inheritedOpacity();
memcpy(cbPtr + 2 * msize, &opacity, sizeof(float));
- const QPointF p0(m_item->width() - 1, m_item->height() - 1);
+ const QPointF p0(m_width - 1, m_height - 1);
const QPointF p1(0, 0);
- const QPointF p2(0, m_item->height() - 1);
+ const QPointF p2(0, m_height - 1);
float *vp = reinterpret_cast<float *>(vbPtr);
*vp++ = p0.x();
@@ -299,7 +296,14 @@ QSGRenderNode::RenderingFlags D3D12RenderNode::flags() const
QRectF D3D12RenderNode::rect() const
{
- return QRect(0, 0, m_item->width(), m_item->height());
+ return QRect(0, 0, m_width, m_height);
+}
+
+void D3D12RenderNode::sync(QQuickItem *item)
+{
+ m_window = item->window();
+ m_width = item->width();
+ m_height = item->height();
}
#endif // d3d12
diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.h b/examples/quick/scenegraph/rendernode/d3d12renderer.h
index ec4b5f85e8..7186b72c04 100644
--- a/examples/quick/scenegraph/rendernode/d3d12renderer.h
+++ b/examples/quick/scenegraph/rendernode/d3d12renderer.h
@@ -52,11 +52,10 @@
#define D3D12RENDERER_H
#include <qsgrendernode.h>
+#include <QQuickItem>
#if QT_CONFIG(d3d12)
-QT_FORWARD_DECLARE_CLASS(QQuickItem)
-
#include <d3d12.h>
#include <wrl/client.h>
@@ -65,7 +64,6 @@ using namespace Microsoft::WRL;
class D3D12RenderNode : public QSGRenderNode
{
public:
- D3D12RenderNode(QQuickItem *item);
~D3D12RenderNode();
void render(const RenderState *state) override;
@@ -73,10 +71,15 @@ public:
RenderingFlags flags() const override;
QRectF rect() const override;
+ void sync(QQuickItem *item);
+
private:
void init();
- QQuickItem *m_item;
+ QQuickWindow *m_window = nullptr;
+ int m_width = 0;
+ int m_height = 0;
+
ID3D12Device *m_device = nullptr;
ComPtr<ID3D12PipelineState> pipelineState;
ComPtr<ID3D12RootSignature> rootSignature;
diff --git a/examples/quick/scenegraph/rendernode/doc/images/rendernode-example.jpg b/examples/quick/scenegraph/rendernode/doc/images/rendernode-example.jpg
new file mode 100644
index 0000000000..cbb59b950d
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/doc/images/rendernode-example.jpg
Binary files differ
diff --git a/examples/quick/scenegraph/rendernode/doc/src/rendernode.qdoc b/examples/quick/scenegraph/rendernode/doc/src/rendernode.qdoc
new file mode 100644
index 0000000000..ba6551fddf
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/doc/src/rendernode.qdoc
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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$
+**
+****************************************************************************/
+
+/*!
+ \example scenegraph/rendernode
+ \title Scene Graph - Custom Rendering with QSGRenderNode
+ \ingroup qtquickexamples
+ \brief Shows how to integrate drawing via the native graphics API with the Qt Quick scene graph.
+
+ \image rendernode-example.jpg
+
+ \l QSGRenderNode allows integrating draw and other calls made directly via
+ the Qt Quick scene graph's underlying native graphics API (such as, Vulkan,
+ Metal, Direct 3D, or OpenGL). This example demonstrates implementing a
+ custom QQuickItem backed by a QSGRenderNode implementation, where the node
+ renders a triangle directly via the graphics API. The rest of the scene
+ (background, text, rectangles) are standard Qt Quick items. The example has
+ full support for OpenGL and Metal, as well as the software backend of Qt
+ Quick.
+
+ The custom item behaves like any other Qt Quick item, meaning it
+ participates and stacking and clipping as usual, which is a big difference
+ to the alternative approaches like having the custom rendering as an
+ overlay (connecting to \l QQuickWindow::afterRendering()) and underlay
+ (connecting to \l QQuickWindow::beforeRendering()) because those do not
+ offer the possibility of proper mixing of the custom content with the Qt
+ Quick scene.
+
+ Another important feature is that QSGRenderNode can be helpful to preserve
+ performance, when compared to some of the alternatives. Going through \l
+ QQuickFramebufferObject allows creating a custom item similarly to what
+ this example does, but it does it by rendering the custom content in a
+ texture, and then drawing a textured quad with that texture. This can be
+ expensive on some systems due to the cost of texturing and blending.
+ QSGRenderNode avoids this since the native graphics calls are issued in
+ line with the draw calls for the scene graph's batches.
+
+ All this comes at the cost of being more complex, and not necessarily being
+ suitable for all types of 3D content, in particular where vertices and
+ different depth would clash with the 2D content in the Qt Quick scene
+ graph's batches (those are better served by "flattening" into a 2D texture
+ via approaches like QQuickFramebufferObject). Therefore QSGRenderNode is
+ not always the right choice. It can however a good and powerful choice in
+ many cases. This is what the example demonstrates.
+
+ Let's go through the most important parts of the code:
+
+ \snippet scenegraph/rendernode/main.cpp 1
+
+ Our custom QML type is implemented in the class CustomRenderItem.
+
+ \snippet scenegraph/rendernode/main.qml 2
+
+ The corresponding import in the QML document.
+
+ \snippet scenegraph/rendernode/main.qml 3
+
+ The CustomRenderItem object. It is positioned to fill a big part of the
+ scene, covering its parent (the yellow rectangle; this will be used to
+ demonstrate clipping). The item will have its scale and rotation animated.
+
+ \snippet scenegraph/rendernode/main.qml 4
+
+ Text items are used to show some helpful information, such as, the
+ active graphics API Qt Quick uses.
+
+ \snippet scenegraph/rendernode/main.qml 5
+
+ Clicking the left mouse button is used to toggle clipping on the custom
+ item's parent item. By default this is done using scissoring (GL_SCISSOR_TEST
+ with OpenGL). A well-written QSGRenderNode implementation is expected to be
+ able to take this into account and enable scissor testing when the scene graph
+ indicates that it is necessary.
+
+ The right mouse button is used to toggle an animation on the rotation of
+ the parent item. With clipping enabled, this demonstrates clipping via the
+ stencil buffer since a rectangular scissor is not appropriate when we need
+ to clip to a rotated rectangle shape. The scene graph fills up the stencil
+ buffer as necessary, the QSGRenderNode implementation just has to enable
+ stencil testing using the provided reference value.
+
+ \snippet scenegraph/rendernode/customrenderitem.cpp 1
+
+ Moving on to the CustomRenderItem implementation. This is a visual item.
+
+ \snippet scenegraph/rendernode/customrenderitem.cpp 2
+
+ The implementation of \l QQuickItem::updatePaintNode() creates (if not yet
+ done) and returns an instance of a suitable QSGRenderNode subclass. The
+ example supports multiple graphics APIs, and also the \c software backend.
+
+ Let's look at the the render node for OpenGL (supporting both the
+ traditional, direct OpenGL-based scene graph, and also the modern,
+ abstracted variant using the RHI). For other graphics APIs, the concepts
+ and the outline of a QSGRenderNode implementation are the same. It is worth
+ noting that in some cases it will also be necessary to connect to a signal
+ like \l QQuickWindow::beforeRendering() to perform copy type of operations
+ (such as, vertex buffer uploads). This is not necessary for OpenGL, but it
+ is essential for Vulkan or Metal since there such operations cannot be
+ issued in render() as there is a renderpass being recorded when render() is
+ called.
+
+ \snippet scenegraph/rendernode/openglrenderer.h 1
+
+ The main job is to provide implementations of the virtual QSGRenderNode functions.
+
+ \snippet scenegraph/rendernode/openglrenderer.cpp 1
+
+ The pattern for safe graphics resource management is to do any cleanup in
+ \l{QSGRenderNode::releaseResources()}{releaseResources()}, while also
+ calling this from the destructor.
+
+ \snippet scenegraph/rendernode/openglrenderer.cpp 2
+
+ The render() function initializes graphics resources (in this case, an
+ OpenGL shader program and a vertex buffer), if not yet done. It then
+ makes sure the necessary resources are bound and updates uniforms.
+ The transformation matrix and the opacity are provided by the scene graph
+ either via the \c state argument or base class functions.
+
+ \snippet scenegraph/rendernode/openglrenderer.cpp 5
+
+ This render node is well-behaving since it basically renders in 2D,
+ respecting the item's geometry. This is not mandatory, but then flags() has
+ to return (or not return) the appropriate flags.
+
+ \snippet scenegraph/rendernode/openglrenderer.cpp 3
+
+ After setting up vertex inputs, but before recording a draw call for our
+ triangle, it is important to set some state in order to integrate with the
+ rest of the scene correctly. Setting scissor and stencil as instructed by
+ \c state allows our item to render correctly even when there are one or
+ more clips in the parent chain.
+
+ \snippet scenegraph/rendernode/openglrenderer.cpp 4
+
+ As shown above, we only really render in 2D (no depth), within the item's
+ geometry. changedStates() returns the flags corresponding to the OpenGL
+ states render() touches.
+
+*/
diff --git a/examples/quick/scenegraph/rendernode/main.cpp b/examples/quick/scenegraph/rendernode/main.cpp
index 21419abfc9..146d787e50 100644
--- a/examples/quick/scenegraph/rendernode/main.cpp
+++ b/examples/quick/scenegraph/rendernode/main.cpp
@@ -58,7 +58,9 @@ int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
+//! [1]
qmlRegisterType<CustomRenderItem>("SceneGraphRendering", 2, 0, "CustomRenderItem");
+//! [1]
QQuickView view;
diff --git a/examples/quick/scenegraph/rendernode/main.qml b/examples/quick/scenegraph/rendernode/main.qml
index d0ba4a4669..153a71e097 100644
--- a/examples/quick/scenegraph/rendernode/main.qml
+++ b/examples/quick/scenegraph/rendernode/main.qml
@@ -49,27 +49,65 @@
****************************************************************************/
import QtQuick 2.8
+//! [2]
import SceneGraphRendering 2.0
+//! [2]
Item {
Rectangle {
+ id: bg
anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0; color: "steelblue" }
GradientStop { position: 1; color: "black" }
}
- CustomRenderItem {
- id: renderer
+ //! [5]
+ MouseArea {
anchors.fill: parent
- anchors.margins: 10
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ onClicked: {
+ if (mouse.button === Qt.LeftButton) {
+ clipper.clip = !clipper.clip
+ } else if (mouse.button === Qt.RightButton) {
+ nonRectClipAnim.running = !nonRectClipAnim.running
+ if (!nonRectClipAnim.running)
+ clipper.rotation = 0;
+ }
+ }
+ }
+ // ![5]
+
+ Rectangle {
+ id: clipper
+ width: parent.width / 2
+ height: parent.height / 2
+ anchors.centerIn: parent
+ border.color: "yellow"
+ border.width: 2
+ color: "transparent"
+ NumberAnimation on rotation {
+ id: nonRectClipAnim
+ from: 0; to: 360; duration: 5000; loops: Animation.Infinite
+ running: false
+ }
+
+ //! [3]
+ CustomRenderItem {
+ id: renderer
+ width: bg.width - 20
+ height: bg.height - 20
+ x: -clipper.x + 10
+ y: -clipper.y + 10
- transform: [
- Rotation { id: rotation; axis.x: 0; axis.z: 0; axis.y: 1; angle: 0; origin.x: renderer.width / 2; origin.y: renderer.height / 2; },
- Translate { id: txOut; x: -renderer.width / 2; y: -renderer.height / 2 },
- Scale { id: scale; },
- Translate { id: txIn; x: renderer.width / 2; y: renderer.height / 2 }
- ]
+ transform: [
+ Rotation { id: rotation; axis.x: 0; axis.z: 0; axis.y: 1; angle: 0; origin.x: renderer.width / 2; origin.y: renderer.height / 2; },
+ Translate { id: txOut; x: -renderer.width / 2; y: -renderer.height / 2 },
+ Scale { id: scale; },
+ Translate { id: txIn; x: renderer.width / 2; y: renderer.height / 2 }
+ ]
+ }
+ //! [3]
}
SequentialAnimation {
@@ -92,19 +130,39 @@ Item {
loops: Animation.Infinite
}
+ //! [4]
Text {
id: label
- anchors.bottom: renderer.bottom
- anchors.left: renderer.left
- anchors.right: renderer.right
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
anchors.margins: 20
+ color: "yellow"
wrapMode: Text.WordWrap
property int api: GraphicsInfo.api
- text: "Custom rendering via the graphics API "
- + (api === GraphicsInfo.OpenGL ? "OpenGL"
- : api === GraphicsInfo.Direct3D12 ? "Direct3D 12"
- : api === GraphicsInfo.Software ? "Software" : "")
+ text: {
+ var apiStr;
+ switch (api) {
+ case GraphicsInfo.OpenGL: apiStr = "OpenGL (direct)"; break;
+ case GraphicsInfo.Direct3D12: apiStr = "Direct3D 12 (direct)"; break;
+ case GraphicsInfo.Software: apiStr = "Software (QPainter)"; break;
+ case GraphicsInfo.OpenGLRhi: apiStr = "OpenGL (RHI)"; break;
+ case GraphicsInfo.MetalRhi: apiStr = "Metal (RHI)"; break;
+ // the example has no other QSGRenderNode subclasses
+ default: apiStr = "<UNSUPPORTED>"; break;
+ }
+ "Custom rendering via the graphics API " + apiStr
+ + "\nLeft click to toggle clipping to yellow rect"
+ + "\nRight click to rotate (can be used to exercise stencil clip instead of scissor)"
+ }
+ // ![4]
+ }
+
+ Text {
+ id: label2
+ anchors.top: parent.top
+ anchors.right: parent.right
color: "yellow"
+ text: "Clip: " + (clipper.clip ? "ON" : "OFF") + " Rotation: " + (nonRectClipAnim.running ? "ON" : "OFF")
}
}
}
diff --git a/examples/quick/scenegraph/rendernode/metalrenderer.h b/examples/quick/scenegraph/rendernode/metalrenderer.h
new file mode 100644
index 0000000000..cf7fccb930
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/metalrenderer.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef METALRENDERER_H
+#define METALRENDERER_H
+
+#include <qsgrendernode.h>
+#include <QQuickItem>
+
+//#ifdef Q_OS_DARWIN
+#ifdef Q_OS_MACOS
+
+QT_BEGIN_NAMESPACE
+
+QT_END_NAMESPACE
+
+class MetalRenderNodeResourceBuilder : public QObject
+{
+ Q_OBJECT
+
+public:
+ void setWindow(QQuickWindow *w) { m_window = w; }
+
+public slots:
+ void build();
+
+private:
+ QQuickWindow *m_window = nullptr;
+};
+
+class MetalRenderNode : public QSGRenderNode
+{
+public:
+ MetalRenderNode();
+ ~MetalRenderNode();
+
+ void render(const RenderState *state) override;
+ void releaseResources() override;
+ StateFlags changedStates() const override;
+ RenderingFlags flags() const override;
+ QRectF rect() const override;
+
+ MetalRenderNodeResourceBuilder *resourceBuilder() { return &m_resourceBuilder; }
+
+ void sync(QQuickItem *item);
+
+private:
+ MetalRenderNodeResourceBuilder m_resourceBuilder;
+ QQuickWindow *m_window = nullptr;
+ int m_width = 0;
+ int m_height = 0;
+ int m_outputHeight = 0;
+};
+
+#endif // Q_OS_DARWIN
+
+#endif
diff --git a/examples/quick/scenegraph/rendernode/metalrenderer.mm b/examples/quick/scenegraph/rendernode/metalrenderer.mm
new file mode 100644
index 0000000000..b83dc62c48
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/metalrenderer.mm
@@ -0,0 +1,333 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "metalrenderer.h"
+#include <QQuickItem>
+#include <QQuickWindow>
+
+#include <Metal/Metal.h>
+
+using FuncAndLib = QPair<id<MTLFunction>, id<MTLLibrary> >;
+
+const int MAX_FRAMES_IN_FLIGHT = 3;
+
+struct {
+ id<MTLDevice> dev = nil;
+ QByteArray vsSource;
+ FuncAndLib vs;
+ QByteArray fsSource;
+ FuncAndLib fs;
+ id<MTLBuffer> vbuf[MAX_FRAMES_IN_FLIGHT];
+ id<MTLBuffer> ubuf[MAX_FRAMES_IN_FLIGHT];
+ id<MTLDepthStencilState> stencilEnabledDsState = nil;
+ id<MTLRenderPipelineState> pipeline = nil;
+} g;
+
+static FuncAndLib compileShaderFromSource(const QByteArray &src, const QByteArray &entryPoint)
+{
+ FuncAndLib fl;
+
+ NSString *srcstr = [NSString stringWithUTF8String: src.constData()];
+ MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
+ opts.languageVersion = MTLLanguageVersion1_2;
+ NSError *err = nil;
+ fl.second = [g.dev newLibraryWithSource: srcstr options: opts error: &err];
+ [opts release];
+ // srcstr is autoreleased
+
+ if (err) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qFatal("%s", qPrintable(msg));
+ return fl;
+ }
+
+ NSString *name = [NSString stringWithUTF8String: entryPoint.constData()];
+ fl.first = [fl.second newFunctionWithName: name];
+ [name release];
+
+ return fl;
+}
+
+const int VERTEX_SIZE = 6 * sizeof(float);
+
+static float colors[] = {
+ 1.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f
+};
+
+void MetalRenderNodeResourceBuilder::build()
+{
+ if (!g.dev) {
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::MetalRhi);
+
+ g.dev = (id<MTLDevice>) rif->getResource(m_window, QSGRendererInterface::DeviceResource);
+ Q_ASSERT(g.dev);
+ }
+
+ if (g.vsSource.isEmpty()) {
+ const QString filename = QLatin1String(":/scenegraph/rendernode/metalshader.vert");
+ QFile f(filename);
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
+ qFatal("Failed to read shader %s", qPrintable(filename));
+ g.vsSource = f.readAll();
+ g.vs = compileShaderFromSource(g.vsSource, QByteArrayLiteral("main0"));
+ }
+
+ if (g.fsSource.isEmpty()) {
+ const QString filename = QLatin1String(":/scenegraph/rendernode/metalshader.frag");
+ QFile f(filename);
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
+ qFatal("Failed to read shader %s", qPrintable(filename));
+ g.fsSource = f.readAll();
+ g.fs = compileShaderFromSource(g.fsSource, QByteArrayLiteral("main0"));
+ }
+
+ const int framesInFlight = m_window->graphicsStateInfo().framesInFlight;
+
+ // For simplicity's sake we use shared mode (something like host visible +
+ // host coherent) for everything.
+
+ for (int i = 0; i < framesInFlight; ++i) {
+ // Have multiple versions for vertex too since we'll just memcpy new
+ // vertices based on item width and height on every render(). This could
+ // be optimized further however.
+ if (!g.vbuf[i]) {
+ g.vbuf[i] = [g.dev newBufferWithLength: VERTEX_SIZE + sizeof(colors) options: MTLResourceStorageModeShared];
+ char *p = (char *) [g.vbuf[i] contents];
+ memcpy(p + VERTEX_SIZE, colors, sizeof(colors));
+ }
+
+ if (!g.ubuf[i])
+ g.ubuf[i] = [g.dev newBufferWithLength: 256 options: MTLResourceStorageModeShared];
+ }
+
+ if (!g.stencilEnabledDsState) {
+ MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
+ dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
+ dsDesc.frontFaceStencil.stencilFailureOperation = MTLStencilOperationKeep;
+ dsDesc.frontFaceStencil.depthFailureOperation = MTLStencilOperationKeep;
+ dsDesc.frontFaceStencil.depthStencilPassOperation = MTLStencilOperationKeep;
+ dsDesc.frontFaceStencil.stencilCompareFunction = MTLCompareFunctionEqual;
+ dsDesc.frontFaceStencil.readMask = 0xFF;
+ dsDesc.frontFaceStencil.writeMask = 0xFF;
+
+ dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
+ dsDesc.backFaceStencil.stencilFailureOperation = MTLStencilOperationKeep;
+ dsDesc.backFaceStencil.depthFailureOperation = MTLStencilOperationKeep;
+ dsDesc.backFaceStencil.depthStencilPassOperation = MTLStencilOperationKeep;
+ dsDesc.backFaceStencil.stencilCompareFunction = MTLCompareFunctionEqual;
+ dsDesc.backFaceStencil.readMask = 0xFF;
+ dsDesc.backFaceStencil.writeMask = 0xFF;
+
+ g.stencilEnabledDsState = [g.dev newDepthStencilStateWithDescriptor: dsDesc];
+ [dsDesc release];
+ }
+
+ if (!g.pipeline) {
+ MTLVertexDescriptor *inputLayout = [MTLVertexDescriptor vertexDescriptor];
+ inputLayout.attributes[0].format = MTLVertexFormatFloat2;
+ inputLayout.attributes[0].offset = 0;
+ inputLayout.attributes[0].bufferIndex = 1; // ubuf is 0, vbuf is 1 and 2
+ inputLayout.attributes[1].format = MTLVertexFormatFloat3;
+ inputLayout.attributes[1].offset = 0;
+ inputLayout.attributes[1].bufferIndex = 2;
+ inputLayout.layouts[1].stride = 2 * sizeof(float);
+ inputLayout.layouts[2].stride = 3 * sizeof(float);
+
+ MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
+ rpDesc.vertexDescriptor = inputLayout;
+
+ rpDesc.vertexFunction = g.vs.first;
+ rpDesc.fragmentFunction = g.fs.first;
+
+ rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
+ rpDesc.colorAttachments[0].blendingEnabled = true;
+ rpDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorOne;
+ rpDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne;
+ rpDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
+ rpDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
+
+ if (g.dev.depth24Stencil8PixelFormatSupported) {
+ rpDesc.depthAttachmentPixelFormat = MTLPixelFormatDepth24Unorm_Stencil8;
+ rpDesc.stencilAttachmentPixelFormat = MTLPixelFormatDepth24Unorm_Stencil8;
+ } else {
+ rpDesc.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
+ rpDesc.stencilAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
+ }
+
+ NSError *err = nil;
+ g.pipeline = [g.dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
+ if (!g.pipeline) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qFatal("Failed to create render pipeline state: %s", qPrintable(msg));
+ }
+ [rpDesc release];
+ }
+}
+
+MetalRenderNode::MetalRenderNode()
+{
+ g.vs.first = g.fs.first = nil;
+ g.vs.second = g.fs.second = nil;
+
+ for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
+ g.vbuf[i] = nil;
+ g.ubuf[i] = nil;
+ }
+}
+
+MetalRenderNode::~MetalRenderNode()
+{
+ releaseResources();
+}
+
+void MetalRenderNode::releaseResources()
+{
+ [g.stencilEnabledDsState release];
+ g.stencilEnabledDsState = nil;
+
+ [g.pipeline release];
+ g.pipeline = nil;
+
+ [g.vs.first release];
+ [g.vs.second release];
+
+ [g.fs.first release];
+ [g.fs.second release];
+
+ g.vs.first = g.fs.first = nil;
+ g.vs.second = g.fs.second = nil;
+
+ for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
+ [g.vbuf[i] release];
+ g.vbuf[i] = nil;
+ [g.ubuf[i] release];
+ g.ubuf[i] = nil;
+ }
+}
+
+void MetalRenderNode::render(const RenderState *state)
+{
+ Q_ASSERT(m_window);
+ const QQuickWindow::GraphicsStateInfo &stateInfo(m_window->graphicsStateInfo());
+ id<MTLBuffer> vbuf = g.vbuf[stateInfo.currentFrameSlot];
+ id<MTLBuffer> ubuf = g.ubuf[stateInfo.currentFrameSlot];
+
+ QPointF p0(m_width - 1, m_height - 1);
+ QPointF p1(0, 0);
+ QPointF p2(0, m_height - 1);
+
+ float vertices[6] = { float(p0.x()), float(p0.y()),
+ float(p1.x()), float(p1.y()),
+ float(p2.x()), float(p2.y()) };
+ char *p = (char *) [vbuf contents];
+ memcpy(p, vertices, VERTEX_SIZE);
+
+ const QMatrix4x4 mvp = *state->projectionMatrix() * *matrix();
+ const float opacity = inheritedOpacity();
+
+ p = (char *) [ubuf contents];
+ memcpy(p, mvp.constData(), 64);
+ memcpy(p + 64, &opacity, 4);
+
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ id<MTLRenderCommandEncoder> encoder = (id<MTLRenderCommandEncoder>) rif->getResource(
+ m_window, QSGRendererInterface::CommandEncoderResource);
+ Q_ASSERT(encoder);
+
+ [encoder setVertexBuffer: vbuf offset: 0 atIndex: 1];
+ [encoder setVertexBuffer: vbuf offset: VERTEX_SIZE atIndex: 2];
+
+ [encoder setVertexBuffer: ubuf offset: 0 atIndex: 0];
+ [encoder setFragmentBuffer: ubuf offset: 0 atIndex: 0];
+
+ // Clip support.
+ if (state->scissorEnabled()) {
+ const QRect r = state->scissorRect(); // bottom-up
+ MTLScissorRect s;
+ s.x = r.x();
+ s.y = m_outputHeight - (r.y() + r.height());
+ s.width = r.width();
+ s.height = r.height();
+ [encoder setScissorRect: s];
+ }
+ if (state->stencilEnabled()) {
+ [encoder setDepthStencilState: g.stencilEnabledDsState];
+ [encoder setStencilReferenceValue: state->stencilValue()];
+ }
+
+ [encoder setRenderPipelineState: g.pipeline];
+ [encoder drawPrimitives: MTLPrimitiveTypeTriangle vertexStart: 0 vertexCount: 3 instanceCount: 1 baseInstance: 0];
+}
+
+QSGRenderNode::StateFlags MetalRenderNode::changedStates() const
+{
+ return BlendState | ScissorState | StencilState;
+}
+
+QSGRenderNode::RenderingFlags MetalRenderNode::flags() const
+{
+ return BoundedRectRendering | DepthAwareRendering;
+}
+
+QRectF MetalRenderNode::rect() const
+{
+ return QRect(0, 0, m_width, m_height);
+}
+
+void MetalRenderNode::sync(QQuickItem *item)
+{
+ m_window = item->window();
+ m_width = item->width();
+ m_height = item->height();
+ m_outputHeight = m_window->height() * m_window->effectiveDevicePixelRatio();
+}
diff --git a/examples/quick/scenegraph/rendernode/metalshader.frag b/examples/quick/scenegraph/rendernode/metalshader.frag
new file mode 100644
index 0000000000..907faa537f
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/metalshader.frag
@@ -0,0 +1,28 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct buf
+{
+ float4x4 matrix;
+ float opacity;
+};
+
+struct main0_out
+{
+ float4 fragColor [[color(0)]];
+};
+
+struct main0_in
+{
+ float4 v_color [[user(locn0)]];
+};
+
+fragment main0_out main0(main0_in in [[stage_in]], constant buf& ubuf [[buffer(0)]])
+{
+ main0_out out = {};
+ out.fragColor = in.v_color * ubuf.opacity;
+ return out;
+}
+
diff --git a/examples/quick/scenegraph/rendernode/metalshader.vert b/examples/quick/scenegraph/rendernode/metalshader.vert
new file mode 100644
index 0000000000..12b721f524
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/metalshader.vert
@@ -0,0 +1,31 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct buf
+{
+ float4x4 matrix;
+ float opacity;
+};
+
+struct main0_out
+{
+ float4 v_color [[user(locn0)]];
+ float4 gl_Position [[position]];
+};
+
+struct main0_in
+{
+ float4 pos [[attribute(0)]];
+ float4 color [[attribute(1)]];
+};
+
+vertex main0_out main0(main0_in in [[stage_in]], constant buf& ubuf [[buffer(0)]])
+{
+ main0_out out = {};
+ out.v_color = in.color;
+ out.gl_Position = ubuf.matrix * in.pos;
+ return out;
+}
+
diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.cpp b/examples/quick/scenegraph/rendernode/openglrenderer.cpp
index 3c68830db6..a4e619bea9 100644
--- a/examples/quick/scenegraph/rendernode/openglrenderer.cpp
+++ b/examples/quick/scenegraph/rendernode/openglrenderer.cpp
@@ -57,11 +57,7 @@
#include <QOpenGLBuffer>
#include <QOpenGLFunctions>
-OpenGLRenderNode::OpenGLRenderNode(QQuickItem *item)
- : m_item(item)
-{
-}
-
+//! [1]
OpenGLRenderNode::~OpenGLRenderNode()
{
releaseResources();
@@ -74,6 +70,7 @@ void OpenGLRenderNode::releaseResources()
delete m_vbo;
m_vbo = nullptr;
}
+//! [1]
void OpenGLRenderNode::init()
{
@@ -121,44 +118,66 @@ void OpenGLRenderNode::init()
m_vbo->release();
}
+//! [2]
void OpenGLRenderNode::render(const RenderState *state)
{
if (!m_program)
init();
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
-
m_program->bind();
m_program->setUniformValue(m_matrixUniform, *state->projectionMatrix() * *matrix());
m_program->setUniformValue(m_opacityUniform, float(inheritedOpacity()));
+//! [2]
m_vbo->bind();
- QPointF p0(m_item->width() - 1, m_item->height() - 1);
+//! [5]
+ QPointF p0(m_width - 1, m_height - 1);
QPointF p1(0, 0);
- QPointF p2(0, m_item->height() - 1);
+ QPointF p2(0, m_height - 1);
GLfloat vertices[6] = { GLfloat(p0.x()), GLfloat(p0.y()),
GLfloat(p1.x()), GLfloat(p1.y()),
GLfloat(p2.x()), GLfloat(p2.y()) };
m_vbo->write(0, vertices, sizeof(vertices));
+//! [5]
m_program->setAttributeBuffer(0, GL_FLOAT, 0, 2);
m_program->setAttributeBuffer(1, GL_FLOAT, sizeof(vertices), 3);
m_program->enableAttributeArray(0);
m_program->enableAttributeArray(1);
- // Note that clipping (scissor or stencil) is ignored in this example.
+ // We are prepared both for the legacy (direct OpenGL) and the modern
+ // (abstracted by RHI) OpenGL scenegraph. So set all the states that are
+ // important to us.
+
+ //! [3]
+ f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
f->glEnable(GL_BLEND);
f->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ // Clip support.
+ if (state->scissorEnabled()) {
+ f->glEnable(GL_SCISSOR_TEST);
+ const QRect r = state->scissorRect(); // already bottom-up
+ f->glScissor(r.x(), r.y(), r.width(), r.height());
+ }
+ if (state->stencilEnabled()) {
+ f->glEnable(GL_STENCIL_TEST);
+ f->glStencilFunc(GL_EQUAL, state->stencilValue(), 0xFF);
+ f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ }
+
f->glDrawArrays(GL_TRIANGLES, 0, 3);
+ //! [3]
}
+//! [4]
QSGRenderNode::StateFlags OpenGLRenderNode::changedStates() const
{
- return BlendState;
+ return BlendState | ScissorState | StencilState;
}
QSGRenderNode::RenderingFlags OpenGLRenderNode::flags() const
@@ -168,7 +187,14 @@ QSGRenderNode::RenderingFlags OpenGLRenderNode::flags() const
QRectF OpenGLRenderNode::rect() const
{
- return QRect(0, 0, m_item->width(), m_item->height());
+ return QRect(0, 0, m_width, m_height);
+}
+//! [4]
+
+void OpenGLRenderNode::sync(QQuickItem *item)
+{
+ m_width = item->width();
+ m_height = item->height();
}
#endif // opengl
diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.h b/examples/quick/scenegraph/rendernode/openglrenderer.h
index ac640405c5..1e7977481a 100644
--- a/examples/quick/scenegraph/rendernode/openglrenderer.h
+++ b/examples/quick/scenegraph/rendernode/openglrenderer.h
@@ -52,21 +52,21 @@
#define OPENGLRENDERER_H
#include <qsgrendernode.h>
+#include <QQuickItem>
#if QT_CONFIG(opengl)
QT_BEGIN_NAMESPACE
-class QQuickItem;
class QOpenGLShaderProgram;
class QOpenGLBuffer;
QT_END_NAMESPACE
+//! [1]
class OpenGLRenderNode : public QSGRenderNode
{
public:
- OpenGLRenderNode(QQuickItem *item);
~OpenGLRenderNode();
void render(const RenderState *state) override;
@@ -74,11 +74,15 @@ public:
StateFlags changedStates() const override;
RenderingFlags flags() const override;
QRectF rect() const override;
+//! [1]
+
+ void sync(QQuickItem *item);
private:
void init();
- QQuickItem *m_item;
+ int m_width = 0;
+ int m_height = 0;
QOpenGLShaderProgram *m_program = nullptr;
int m_matrixUniform;
int m_opacityUniform;
diff --git a/examples/quick/scenegraph/rendernode/rendernode.pro b/examples/quick/scenegraph/rendernode/rendernode.pro
index 76e498042b..897b0b1f08 100644
--- a/examples/quick/scenegraph/rendernode/rendernode.pro
+++ b/examples/quick/scenegraph/rendernode/rendernode.pro
@@ -22,3 +22,9 @@ qtConfig(d3d12) {
SOURCES += d3d12renderer.cpp
LIBS += -ld3d12
}
+
+macos {
+ HEADERS += metalrenderer.h
+ SOURCES += metalrenderer.mm
+ LIBS += -framework Metal -framework AppKit
+}
diff --git a/examples/quick/scenegraph/rendernode/rendernode.qrc b/examples/quick/scenegraph/rendernode/rendernode.qrc
index 049adcf8a6..5907eab62c 100644
--- a/examples/quick/scenegraph/rendernode/rendernode.qrc
+++ b/examples/quick/scenegraph/rendernode/rendernode.qrc
@@ -3,5 +3,7 @@
<file>main.qml</file>
<file>shader_vert.cso</file>
<file>shader_frag.cso</file>
+ <file>metalshader.vert</file>
+ <file>metalshader.frag</file>
</qresource>
</RCC>
diff --git a/examples/quick/scenegraph/rendernode/softwarerenderer.cpp b/examples/quick/scenegraph/rendernode/softwarerenderer.cpp
index 0a0ec4b485..bba364ac97 100644
--- a/examples/quick/scenegraph/rendernode/softwarerenderer.cpp
+++ b/examples/quick/scenegraph/rendernode/softwarerenderer.cpp
@@ -54,11 +54,6 @@
#include <QSGRendererInterface>
#include <QPainter>
-SoftwareRenderNode::SoftwareRenderNode(QQuickItem *item)
- : m_item(item)
-{
-}
-
SoftwareRenderNode::~SoftwareRenderNode()
{
releaseResources();
@@ -70,8 +65,10 @@ void SoftwareRenderNode::releaseResources()
void SoftwareRenderNode::render(const RenderState *renderState)
{
- QSGRendererInterface *rif = m_item->window()->rendererInterface();
- QPainter *p = static_cast<QPainter *>(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource));
+ Q_ASSERT(m_window);
+
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ QPainter *p = static_cast<QPainter *>(rif->getResource(m_window, QSGRendererInterface::PainterResource));
Q_ASSERT(p);
const QRegion *clipRegion = renderState->clipRegion();
@@ -81,15 +78,15 @@ void SoftwareRenderNode::render(const RenderState *renderState)
p->setTransform(matrix()->toTransform());
p->setOpacity(inheritedOpacity());
- const QPointF p0(m_item->width() - 1, m_item->height() - 1);
+ const QPointF p0(m_width - 1, m_height - 1);
const QPointF p1(0, 0);
- const QPointF p2(0, m_item->height() - 1);
+ const QPointF p2(0, m_height - 1);
QPainterPath path(p0);
path.lineTo(p1);
path.lineTo(p2);
path.closeSubpath();
- QLinearGradient gradient(QPointF(0, 0), QPointF(m_item->width(), m_item->height()));
+ QLinearGradient gradient(QPointF(0, 0), QPointF(m_width, m_height));
gradient.setColorAt(0, Qt::green);
gradient.setColorAt(1, Qt::red);
@@ -108,5 +105,12 @@ QSGRenderNode::RenderingFlags SoftwareRenderNode::flags() const
QRectF SoftwareRenderNode::rect() const
{
- return QRect(0, 0, m_item->width(), m_item->height());
+ return QRect(0, 0, m_width, m_height);
+}
+
+void SoftwareRenderNode::sync(QQuickItem *item)
+{
+ m_window = item->window();
+ m_width = item->width();
+ m_height = item->height();
}
diff --git a/examples/quick/scenegraph/rendernode/softwarerenderer.h b/examples/quick/scenegraph/rendernode/softwarerenderer.h
index cc2aaf7ed3..6091a13ca3 100644
--- a/examples/quick/scenegraph/rendernode/softwarerenderer.h
+++ b/examples/quick/scenegraph/rendernode/softwarerenderer.h
@@ -57,7 +57,6 @@
class SoftwareRenderNode : public QSGRenderNode
{
public:
- SoftwareRenderNode(QQuickItem *item);
~SoftwareRenderNode();
void render(const RenderState *state) override;
@@ -66,8 +65,12 @@ public:
RenderingFlags flags() const override;
QRectF rect() const override;
+ void sync(QQuickItem *item);
+
private:
- QQuickItem *m_item;
+ QQuickWindow *m_window = nullptr;
+ int m_width = 0;
+ int m_height = 0;
};
#endif
diff --git a/examples/quick/scenegraph/scenegraph.pro b/examples/quick/scenegraph/scenegraph.pro
index 2efeb5ed83..5fea3b974a 100644
--- a/examples/quick/scenegraph/scenegraph.pro
+++ b/examples/quick/scenegraph/scenegraph.pro
@@ -5,7 +5,7 @@ qtConfig(opengl(es1|es2)?) {
graph \
simplematerial \
sgengine \
- textureinsgnode \
+ fboitem \
openglunderqml \
textureinthread \
twotextureproviders
@@ -16,5 +16,19 @@ SUBDIRS += \
rendernode \
threadedanimation
+macos {
+ SUBDIRS += \
+ metalunderqml \
+ metaltextureimport
+}
+
+win32 {
+ SUBDIRS += d3d11underqml
+}
+
+qtConfig(vulkan) {
+ SUBDIRS += vulkanunderqml
+}
+
EXAMPLE_FILES += \
shared
diff --git a/examples/quick/scenegraph/shared/squircle_rhi.frag b/examples/quick/scenegraph/shared/squircle_rhi.frag
new file mode 100644
index 0000000000..8da62b93e6
--- /dev/null
+++ b/examples/quick/scenegraph/shared/squircle_rhi.frag
@@ -0,0 +1,16 @@
+#version 440
+
+layout(location = 0) in vec2 coords;
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ float t;
+} ubuf;
+
+void main()
+{
+ float i = 1. - (pow(abs(coords.x), 4.) + pow(abs(coords.y), 4.));
+ i = smoothstep(ubuf.t - 0.8, ubuf.t + 0.8, i);
+ i = floor(i * 20.) / 20.;
+ fragColor = vec4(coords * .5 + .5, i, i);
+}
diff --git a/examples/quick/scenegraph/shared/squircle_rhi.vert b/examples/quick/scenegraph/shared/squircle_rhi.vert
new file mode 100644
index 0000000000..b57dfdfe10
--- /dev/null
+++ b/examples/quick/scenegraph/shared/squircle_rhi.vert
@@ -0,0 +1,13 @@
+#version 440
+
+layout(location = 0) in vec4 vertices;
+
+layout(location = 0) out vec2 coords;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ gl_Position = vertices;
+ coords = vertices.xy;
+}
diff --git a/examples/quick/scenegraph/vulkanunderqml/doc/images/vulkanunderqml-example.jpg b/examples/quick/scenegraph/vulkanunderqml/doc/images/vulkanunderqml-example.jpg
new file mode 100644
index 0000000000..c3f51b6194
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/doc/images/vulkanunderqml-example.jpg
Binary files differ
diff --git a/examples/quick/scenegraph/vulkanunderqml/doc/src/vulkanunderqml.qdoc b/examples/quick/scenegraph/vulkanunderqml/doc/src/vulkanunderqml.qdoc
new file mode 100644
index 0000000000..d11982c074
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/doc/src/vulkanunderqml.qdoc
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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$
+**
+****************************************************************************/
+
+/*!
+ \example scenegraph/vulkanunderqml
+ \title Scene Graph - Vulkan Under QML
+ \ingroup qtquickexamples
+ \brief Shows how to render directly with vulkan under a Qt Quick scene.
+
+ \image vulkanunderqml-example.jpg
+
+ The Vulkan Under QML example shows how an application can make use of the
+ \l QQuickWindow::beforeRendering() and \l
+ QQuickWindow::beforeRenderPassRecording() signals to draw custom Vulkan
+ content under a Qt Quick scene. This signal is emitted at the start of
+ every frame, before the scene graph starts its rendering, thus any Vulkan
+ draw calls that are made as a response to this signal, will stack under the
+ Qt Quick items. There are two signals, because the custom Vulkan commands
+ are recorded onto the same command buffer that is used by the scene graph.
+ beforeRendering() on its own is not sufficient for this because it gets
+ emitted at the start of the frame, before recording the start of a
+ renderpass instance via
+ \l{https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCmdBeginRenderPass.html}{vkCmdBeginRenderPass}.
+ By also connecting to beforeRenderPassRecording(), the application's own
+ commands and the scene graph's scaffolding will end up in the right order.
+
+ As an alternative, applications that wish to render Vulkan content
+ on top of the Qt Quick scene, can do so by connecting to the \l
+ QQuickWindow::afterRendering() and \l
+ QQuickWindow::afterRenderPassRecording() signals.
+
+ In this example, we will also see how it is possible to have
+ values that are exposed to QML which affect the Vulkan
+ rendering. We animate the threshold value using a NumberAnimation
+ in the QML file and this value is used by the SPIR-V shader
+ program that draws the squircles.
+
+ The example is equivalent in most ways to the \l{Scene Graph - OpenGL Under
+ QML}{OpenGL Under QML}, \l{Scene Graph - Direct3D 11 Under QML}{Direct3D 11
+ Under QML}, and \l{Scene Graph - Metal Under QML}{Metal Under QML}
+ examples, they all render the same custom content, just via different
+ native APIs.
+
+ */
diff --git a/examples/quick/scenegraph/vulkanunderqml/main.cpp b/examples/quick/scenegraph/vulkanunderqml/main.cpp
new file mode 100644
index 0000000000..a04497b1d6
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/main.cpp
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QtQuick/QQuickView>
+#include "vulkansquircle.h"
+
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+
+ qmlRegisterType<VulkanSquircle>("VulkanUnderQML", 1, 0, "VulkanSquircle");
+
+ // This example needs Vulkan. It will not run otherwise.
+ QQuickWindow::setSceneGraphBackend(QSGRendererInterface::VulkanRhi);
+
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl("qrc:///scenegraph/vulkanunderqml/main.qml"));
+ view.show();
+
+ return app.exec();
+}
diff --git a/examples/quick/scenegraph/vulkanunderqml/main.qml b/examples/quick/scenegraph/vulkanunderqml/main.qml
new file mode 100644
index 0000000000..c364ca7636
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/main.qml
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [1]
+import QtQuick 2.0
+import VulkanUnderQML 1.0
+
+Item {
+
+ width: 320
+ height: 480
+
+ VulkanSquircle {
+ SequentialAnimation on t {
+ NumberAnimation { to: 1; duration: 2500; easing.type: Easing.InQuad }
+ NumberAnimation { to: 0; duration: 2500; easing.type: Easing.OutQuad }
+ loops: Animation.Infinite
+ running: true
+ }
+ }
+//! [1] //! [2]
+ Rectangle {
+ color: Qt.rgba(1, 1, 1, 0.7)
+ radius: 10
+ border.width: 1
+ border.color: "white"
+ anchors.fill: label
+ anchors.margins: -10
+ }
+
+ Text {
+ id: label
+ color: "black"
+ wrapMode: Text.WordWrap
+ text: "The background here is a squircle rendered with raw Vulkan using the beforeRendering() and beforeRenderPassRecording() signals in QQuickWindow. This text label and its border is rendered using QML"
+ anchors.right: parent.right
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ anchors.margins: 20
+ }
+}
+//! [2]
diff --git a/examples/quick/scenegraph/vulkanunderqml/squircle.frag.spv b/examples/quick/scenegraph/vulkanunderqml/squircle.frag.spv
new file mode 100644
index 0000000000..e4d13a871d
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/squircle.frag.spv
Binary files differ
diff --git a/examples/quick/scenegraph/vulkanunderqml/squircle.vert.spv b/examples/quick/scenegraph/vulkanunderqml/squircle.vert.spv
new file mode 100644
index 0000000000..5df94a47e4
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/squircle.vert.spv
Binary files differ
diff --git a/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp b/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp
new file mode 100644
index 0000000000..21f46a25c1
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp
@@ -0,0 +1,623 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "vulkansquircle.h"
+#include <QtCore/QRunnable>
+#include <QtQuick/QQuickWindow>
+
+#include <QVulkanInstance>
+#include <QVulkanFunctions>
+
+class SquircleRenderer : public QObject
+{
+ Q_OBJECT
+public:
+ ~SquircleRenderer();
+
+ void setT(qreal t) { m_t = t; }
+ void setViewportSize(const QSize &size) { m_viewportSize = size; }
+ void setWindow(QQuickWindow *window) { m_window = window; }
+
+public slots:
+ void frameStart();
+ void mainPassRecordingStart();
+
+private:
+ enum Stage {
+ VertexStage,
+ FragmentStage
+ };
+ void prepareShader(Stage stage);
+ void init(int framesInFlight);
+
+ QSize m_viewportSize;
+ qreal m_t = 0;
+ QQuickWindow *m_window;
+
+ QByteArray m_vert;
+ QByteArray m_frag;
+
+ bool m_initialized = false;
+ VkPhysicalDevice m_physDev = VK_NULL_HANDLE;
+ VkDevice m_dev = VK_NULL_HANDLE;
+ QVulkanDeviceFunctions *m_devFuncs = nullptr;
+ QVulkanFunctions *m_funcs = nullptr;
+
+ VkBuffer m_vbuf = VK_NULL_HANDLE;
+ VkDeviceMemory m_vbufMem = VK_NULL_HANDLE;
+ VkBuffer m_ubuf = VK_NULL_HANDLE;
+ VkDeviceMemory m_ubufMem = VK_NULL_HANDLE;
+ VkDeviceSize m_allocPerUbuf = 0;
+
+ VkPipelineCache m_pipelineCache = VK_NULL_HANDLE;
+
+ VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
+ VkDescriptorSetLayout m_resLayout = VK_NULL_HANDLE;
+ VkPipeline m_pipeline = VK_NULL_HANDLE;
+
+ VkDescriptorPool m_descriptorPool = VK_NULL_HANDLE;
+ VkDescriptorSet m_ubufDescriptor = VK_NULL_HANDLE;
+};
+
+VulkanSquircle::VulkanSquircle()
+{
+ connect(this, &QQuickItem::windowChanged, this, &VulkanSquircle::handleWindowChanged);
+}
+
+void VulkanSquircle::setT(qreal t)
+{
+ if (t == m_t)
+ return;
+ m_t = t;
+ emit tChanged();
+ if (window())
+ window()->update();
+}
+
+void VulkanSquircle::handleWindowChanged(QQuickWindow *win)
+{
+ if (win) {
+ connect(win, &QQuickWindow::beforeSynchronizing, this, &VulkanSquircle::sync, Qt::DirectConnection);
+ connect(win, &QQuickWindow::sceneGraphInvalidated, this, &VulkanSquircle::cleanup, Qt::DirectConnection);
+
+ // Ensure we start with cleared to black. The squircle's blend mode relies on this.
+ win->setColor(Qt::black);
+ }
+}
+
+// The safe way to release custom graphics resources is to both connect to
+// sceneGraphInvalidated() and implement releaseResources(). To support
+// threaded render loops the latter performs the SquircleRenderer destruction
+// via scheduleRenderJob(). Note that the VulkanSquircle may be gone by the time
+// the QRunnable is invoked.
+
+void VulkanSquircle::cleanup()
+{
+ delete m_renderer;
+ m_renderer = nullptr;
+}
+
+class CleanupJob : public QRunnable
+{
+public:
+ CleanupJob(SquircleRenderer *renderer) : m_renderer(renderer) { }
+ void run() override { delete m_renderer; }
+private:
+ SquircleRenderer *m_renderer;
+};
+
+void VulkanSquircle::releaseResources()
+{
+ window()->scheduleRenderJob(new CleanupJob(m_renderer), QQuickWindow::BeforeSynchronizingStage);
+ m_renderer = nullptr;
+}
+
+SquircleRenderer::~SquircleRenderer()
+{
+ qDebug("cleanup");
+ if (!m_devFuncs)
+ return;
+
+ m_devFuncs->vkDestroyPipeline(m_dev, m_pipeline, nullptr);
+ m_devFuncs->vkDestroyPipelineLayout(m_dev, m_pipelineLayout, nullptr);
+ m_devFuncs->vkDestroyDescriptorSetLayout(m_dev, m_resLayout, nullptr);
+
+ m_devFuncs->vkDestroyDescriptorPool(m_dev, m_descriptorPool, nullptr);
+
+ m_devFuncs->vkDestroyPipelineCache(m_dev, m_pipelineCache, nullptr);
+
+ m_devFuncs->vkDestroyBuffer(m_dev, m_vbuf, nullptr);
+ m_devFuncs->vkFreeMemory(m_dev, m_vbufMem, nullptr);
+
+ m_devFuncs->vkDestroyBuffer(m_dev, m_ubuf, nullptr);
+ m_devFuncs->vkFreeMemory(m_dev, m_ubufMem, nullptr);
+
+ qDebug("released");
+}
+
+void VulkanSquircle::sync()
+{
+ if (!m_renderer) {
+ m_renderer = new SquircleRenderer;
+ // Initializing resources is done before starting to record the
+ // renderpass, regardless of wanting an underlay or overlay.
+ connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::frameStart, Qt::DirectConnection);
+ // Here we want an underlay and therefore connect to
+ // beforeRenderPassRecording. Changing to afterRenderPassRecording
+ // would render the squircle on top (overlay).
+ connect(window(), &QQuickWindow::beforeRenderPassRecording, m_renderer, &SquircleRenderer::mainPassRecordingStart, Qt::DirectConnection);
+ }
+ m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio());
+ m_renderer->setT(m_t);
+ m_renderer->setWindow(window());
+}
+
+void SquircleRenderer::frameStart()
+{
+ QSGRendererInterface *rif = m_window->rendererInterface();
+
+ // We are not prepared for anything other than running with the RHI and its Vulkan backend.
+ Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::VulkanRhi);
+
+ if (m_vert.isEmpty())
+ prepareShader(VertexStage);
+ if (m_frag.isEmpty())
+ prepareShader(FragmentStage);
+
+ if (!m_initialized)
+ init(m_window->graphicsStateInfo().framesInFlight);
+}
+
+static const float vertices[] = {
+ -1, -1,
+ 1, -1,
+ -1, 1,
+ 1, 1
+};
+
+const int UBUF_SIZE = 4;
+
+void SquircleRenderer::mainPassRecordingStart()
+{
+ // This example demonstrates the simple case: prepending some commands to
+ // the scenegraph's main renderpass. It does not create its own passes,
+ // rendertargets, etc. so no synchronization is needed.
+
+ const QQuickWindow::GraphicsStateInfo &stateInfo(m_window->graphicsStateInfo());
+ QSGRendererInterface *rif = m_window->rendererInterface();
+
+ VkDeviceSize ubufOffset = stateInfo.currentFrameSlot * m_allocPerUbuf;
+ void *p = nullptr;
+ VkResult err = m_devFuncs->vkMapMemory(m_dev, m_ubufMem, ubufOffset, m_allocPerUbuf, 0, &p);
+ if (err != VK_SUCCESS || !p)
+ qFatal("Failed to map uniform buffer memory: %d", err);
+ float t = m_t;
+ memcpy(p, &t, 4);
+ m_devFuncs->vkUnmapMemory(m_dev, m_ubufMem);
+
+ m_window->beginExternalCommands();
+
+ // Must query the command buffer _after_ beginExternalCommands(), this is
+ // actually important when running on Vulkan because what we get here is a
+ // new secondary command buffer, not the primary one.
+ VkCommandBuffer cb = *reinterpret_cast<VkCommandBuffer *>(
+ rif->getResource(m_window, QSGRendererInterface::CommandListResource));
+ Q_ASSERT(cb);
+
+ // Do not assume any state persists on the command buffer. (it may be a
+ // brand new one that just started recording)
+
+ m_devFuncs->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline);
+
+ VkDeviceSize vbufOffset = 0;
+ m_devFuncs->vkCmdBindVertexBuffers(cb, 0, 1, &m_vbuf, &vbufOffset);
+
+ uint32_t dynamicOffset = m_allocPerUbuf * stateInfo.currentFrameSlot;
+ m_devFuncs->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1,
+ &m_ubufDescriptor, 1, &dynamicOffset);
+
+ VkViewport vp = { 0, 0, float(m_viewportSize.width()), float(m_viewportSize.height()), 0.0f, 1.0f };
+ m_devFuncs->vkCmdSetViewport(cb, 0, 1, &vp);
+ VkRect2D scissor = { { 0, 0 }, { uint32_t(m_viewportSize.width()), uint32_t(m_viewportSize.height()) } };
+ m_devFuncs->vkCmdSetScissor(cb, 0, 1, &scissor);
+
+ m_devFuncs->vkCmdDraw(cb, 4, 1, 0, 0);
+
+ m_window->endExternalCommands();
+}
+
+void SquircleRenderer::prepareShader(Stage stage)
+{
+ QString filename;
+ if (stage == VertexStage) {
+ filename = QLatin1String(":/scenegraph/vulkanunderqml/squircle.vert.spv");
+ } else {
+ Q_ASSERT(stage == FragmentStage);
+ filename = QLatin1String(":/scenegraph/vulkanunderqml/squircle.frag.spv");
+ }
+ QFile f(filename);
+ if (!f.open(QIODevice::ReadOnly))
+ qFatal("Failed to read shader %s", qPrintable(filename));
+
+ const QByteArray contents = f.readAll();
+
+ if (stage == VertexStage) {
+ m_vert = contents;
+ Q_ASSERT(!m_vert.isEmpty());
+ } else {
+ m_frag = contents;
+ Q_ASSERT(!m_frag.isEmpty());
+ }
+}
+
+static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
+{
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+void SquircleRenderer::init(int framesInFlight)
+{
+ qDebug("init");
+
+ Q_ASSERT(framesInFlight <= 3);
+ m_initialized = true;
+
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ QVulkanInstance *inst = reinterpret_cast<QVulkanInstance *>(
+ rif->getResource(m_window, QSGRendererInterface::VulkanInstanceResource));
+ Q_ASSERT(inst && inst->isValid());
+
+ m_physDev = *reinterpret_cast<VkPhysicalDevice *>(rif->getResource(m_window, QSGRendererInterface::PhysicalDeviceResource));
+ m_dev = *reinterpret_cast<VkDevice *>(rif->getResource(m_window, QSGRendererInterface::DeviceResource));
+ Q_ASSERT(m_physDev && m_dev);
+
+ m_devFuncs = inst->deviceFunctions(m_dev);
+ m_funcs = inst->functions();
+ Q_ASSERT(m_devFuncs && m_funcs);
+
+ VkRenderPass rp = *reinterpret_cast<VkRenderPass *>(
+ rif->getResource(m_window, QSGRendererInterface::RenderPassResource));
+ Q_ASSERT(rp);
+
+ // For simplicity we just use host visible buffers instead of device local + staging.
+
+ VkPhysicalDeviceProperties physDevProps;
+ m_funcs->vkGetPhysicalDeviceProperties(m_physDev, &physDevProps);
+
+ VkPhysicalDeviceMemoryProperties physDevMemProps;
+ m_funcs->vkGetPhysicalDeviceMemoryProperties(m_physDev, &physDevMemProps);
+
+ VkBufferCreateInfo bufferInfo;
+ memset(&bufferInfo, 0, sizeof(bufferInfo));
+ bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ bufferInfo.size = sizeof(vertices);
+ bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+ VkResult err = m_devFuncs->vkCreateBuffer(m_dev, &bufferInfo, nullptr, &m_vbuf);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create vertex buffer: %d", err);
+
+ VkMemoryRequirements memReq;
+ m_devFuncs->vkGetBufferMemoryRequirements(m_dev, m_vbuf, &memReq);
+ VkMemoryAllocateInfo allocInfo;
+ memset(&allocInfo, 0, sizeof(allocInfo));
+ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ allocInfo.allocationSize = memReq.size;
+
+ uint32_t memTypeIndex = uint32_t(-1);
+ const VkMemoryType *memType = physDevMemProps.memoryTypes;
+ for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) {
+ if (memReq.memoryTypeBits & (1 << i)) {
+ if ((memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
+ && (memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
+ {
+ memTypeIndex = i;
+ break;
+ }
+ }
+ }
+ if (memTypeIndex == uint32_t(-1))
+ qFatal("Failed to find host visible and coherent memory type");
+
+ allocInfo.memoryTypeIndex = memTypeIndex;
+ err = m_devFuncs->vkAllocateMemory(m_dev, &allocInfo, nullptr, &m_vbufMem);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to allocate vertex buffer memory of size %u: %d", uint(allocInfo.allocationSize), err);
+
+ void *p = nullptr;
+ err = m_devFuncs->vkMapMemory(m_dev, m_vbufMem, 0, allocInfo.allocationSize, 0, &p);
+ if (err != VK_SUCCESS || !p)
+ qFatal("Failed to map vertex buffer memory: %d", err);
+ memcpy(p, vertices, sizeof(vertices));
+ m_devFuncs->vkUnmapMemory(m_dev, m_vbufMem);
+ err = m_devFuncs->vkBindBufferMemory(m_dev, m_vbuf, m_vbufMem, 0);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to bind vertex buffer memory: %d", err);
+
+ // Now have a uniform buffer with enough space for the buffer data for each
+ // (potentially) in-flight frame. (as we will write the contents every
+ // frame, and so would need to wait for command buffer completion if there
+ // was only one, and that would not be nice)
+
+ // Could have three buffers and three descriptor sets, or one buffer and
+ // one descriptor set and dynamic offset. We chose the latter in this
+ // example.
+
+ // We use one memory allocation for all uniform buffers, but then have to
+ // watch out for the buffer offset aligment requirement, which may be as
+ // large as 256 bytes.
+
+ m_allocPerUbuf = aligned(UBUF_SIZE, physDevProps.limits.minUniformBufferOffsetAlignment);
+
+ bufferInfo.size = framesInFlight * m_allocPerUbuf;
+ bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
+ err = m_devFuncs->vkCreateBuffer(m_dev, &bufferInfo, nullptr, &m_ubuf);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create uniform buffer: %d", err);
+ m_devFuncs->vkGetBufferMemoryRequirements(m_dev, m_ubuf, &memReq);
+ memTypeIndex = -1;
+ for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) {
+ if (memReq.memoryTypeBits & (1 << i)) {
+ if ((memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
+ && (memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
+ {
+ memTypeIndex = i;
+ break;
+ }
+ }
+ }
+ if (memTypeIndex == uint32_t(-1))
+ qFatal("Failed to find host visible and coherent memory type");
+
+ allocInfo.allocationSize = framesInFlight * m_allocPerUbuf;
+ allocInfo.memoryTypeIndex = memTypeIndex;
+ err = m_devFuncs->vkAllocateMemory(m_dev, &allocInfo, nullptr, &m_ubufMem);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to allocate uniform buffer memory of size %u: %d", uint(allocInfo.allocationSize), err);
+
+ err = m_devFuncs->vkBindBufferMemory(m_dev, m_ubuf, m_ubufMem, 0);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to bind uniform buffer memory: %d", err);
+
+ // Now onto the pipeline.
+
+ VkPipelineCacheCreateInfo pipelineCacheInfo;
+ memset(&pipelineCacheInfo, 0, sizeof(pipelineCacheInfo));
+ pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
+ err = m_devFuncs->vkCreatePipelineCache(m_dev, &pipelineCacheInfo, nullptr, &m_pipelineCache);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create pipeline cache: %d", err);
+
+ VkDescriptorSetLayoutBinding descLayoutBinding;
+ memset(&descLayoutBinding, 0, sizeof(descLayoutBinding));
+ descLayoutBinding.binding = 0;
+ descLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
+ descLayoutBinding.descriptorCount = 1;
+ descLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
+ VkDescriptorSetLayoutCreateInfo layoutInfo;
+ memset(&layoutInfo, 0, sizeof(layoutInfo));
+ layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ layoutInfo.bindingCount = 1;
+ layoutInfo.pBindings = &descLayoutBinding;
+ err = m_devFuncs->vkCreateDescriptorSetLayout(m_dev, &layoutInfo, nullptr, &m_resLayout);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create descriptor set layout: %d", err);
+
+ VkPipelineLayoutCreateInfo pipelineLayoutInfo;
+ memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo));
+ pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ pipelineLayoutInfo.setLayoutCount = 1;
+ pipelineLayoutInfo.pSetLayouts = &m_resLayout;
+ err = m_devFuncs->vkCreatePipelineLayout(m_dev, &pipelineLayoutInfo, nullptr, &m_pipelineLayout);
+ if (err != VK_SUCCESS)
+ qWarning("Failed to create pipeline layout: %d", err);
+
+ VkGraphicsPipelineCreateInfo pipelineInfo;
+ memset(&pipelineInfo, 0, sizeof(pipelineInfo));
+ pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+
+ VkShaderModuleCreateInfo shaderInfo;
+ memset(&shaderInfo, 0, sizeof(shaderInfo));
+ shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+ shaderInfo.codeSize = m_vert.size();
+ shaderInfo.pCode = reinterpret_cast<const quint32 *>(m_vert.constData());
+ VkShaderModule vertShaderModule;
+ err = m_devFuncs->vkCreateShaderModule(m_dev, &shaderInfo, nullptr, &vertShaderModule);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create vertex shader module: %d", err);
+
+ shaderInfo.codeSize = m_frag.size();
+ shaderInfo.pCode = reinterpret_cast<const quint32 *>(m_frag.constData());
+ VkShaderModule fragShaderModule;
+ err = m_devFuncs->vkCreateShaderModule(m_dev, &shaderInfo, nullptr, &fragShaderModule);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create fragment shader module: %d", err);
+
+ VkPipelineShaderStageCreateInfo stageInfo[2];
+ memset(&stageInfo, 0, sizeof(stageInfo));
+ stageInfo[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ stageInfo[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
+ stageInfo[0].module = vertShaderModule;
+ stageInfo[0].pName = "main";
+ stageInfo[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ stageInfo[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
+ stageInfo[1].module = fragShaderModule;
+ stageInfo[1].pName = "main";
+ pipelineInfo.stageCount = 2;
+ pipelineInfo.pStages = stageInfo;
+
+ VkVertexInputBindingDescription vertexBinding = {
+ 0, // binding
+ 2 * sizeof(float), // stride
+ VK_VERTEX_INPUT_RATE_VERTEX
+ };
+ VkVertexInputAttributeDescription vertexAttr = {
+ 0, // location
+ 0, // binding
+ VK_FORMAT_R32G32_SFLOAT, // 'vertices' only has 2 floats per vertex
+ 0 // offset
+ };
+ VkPipelineVertexInputStateCreateInfo vertexInputInfo;
+ memset(&vertexInputInfo, 0, sizeof(vertexInputInfo));
+ vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+ vertexInputInfo.vertexBindingDescriptionCount = 1;
+ vertexInputInfo.pVertexBindingDescriptions = &vertexBinding;
+ vertexInputInfo.vertexAttributeDescriptionCount = 1;
+ vertexInputInfo.pVertexAttributeDescriptions = &vertexAttr;
+ pipelineInfo.pVertexInputState = &vertexInputInfo;
+
+ VkDynamicState dynStates[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
+ VkPipelineDynamicStateCreateInfo dynamicInfo;
+ memset(&dynamicInfo, 0, sizeof(dynamicInfo));
+ dynamicInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
+ dynamicInfo.dynamicStateCount = 2;
+ dynamicInfo.pDynamicStates = dynStates;
+ pipelineInfo.pDynamicState = &dynamicInfo;
+
+ VkPipelineViewportStateCreateInfo viewportInfo;
+ memset(&viewportInfo, 0, sizeof(viewportInfo));
+ viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+ viewportInfo.viewportCount = viewportInfo.scissorCount = 1;
+ pipelineInfo.pViewportState = &viewportInfo;
+
+ VkPipelineInputAssemblyStateCreateInfo iaInfo;
+ memset(&iaInfo, 0, sizeof(iaInfo));
+ iaInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+ iaInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
+ pipelineInfo.pInputAssemblyState = &iaInfo;
+
+ VkPipelineRasterizationStateCreateInfo rsInfo;
+ memset(&rsInfo, 0, sizeof(rsInfo));
+ rsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+ rsInfo.lineWidth = 1.0f;
+ pipelineInfo.pRasterizationState = &rsInfo;
+
+ VkPipelineMultisampleStateCreateInfo msInfo;
+ memset(&msInfo, 0, sizeof(msInfo));
+ msInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+ msInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+ pipelineInfo.pMultisampleState = &msInfo;
+
+ VkPipelineDepthStencilStateCreateInfo dsInfo;
+ memset(&dsInfo, 0, sizeof(dsInfo));
+ dsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
+ pipelineInfo.pDepthStencilState = &dsInfo;
+
+ // SrcAlpha, One
+ VkPipelineColorBlendStateCreateInfo blendInfo;
+ memset(&blendInfo, 0, sizeof(blendInfo));
+ blendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+ VkPipelineColorBlendAttachmentState blend;
+ memset(&blend, 0, sizeof(blend));
+ blend.blendEnable = true;
+ blend.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
+ blend.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
+ blend.colorBlendOp = VK_BLEND_OP_ADD;
+ blend.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
+ blend.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
+ blend.alphaBlendOp = VK_BLEND_OP_ADD;
+ blend.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT
+ | VK_COLOR_COMPONENT_A_BIT;
+ blendInfo.attachmentCount = 1;
+ blendInfo.pAttachments = &blend;
+ pipelineInfo.pColorBlendState = &blendInfo;
+
+ pipelineInfo.layout = m_pipelineLayout;
+
+ pipelineInfo.renderPass = rp;
+
+ err = m_devFuncs->vkCreateGraphicsPipelines(m_dev, m_pipelineCache, 1, &pipelineInfo, nullptr, &m_pipeline);
+
+ m_devFuncs->vkDestroyShaderModule(m_dev, vertShaderModule, nullptr);
+ m_devFuncs->vkDestroyShaderModule(m_dev, fragShaderModule, nullptr);
+
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create graphics pipeline: %d", err);
+
+ // Now just need some descriptors.
+ VkDescriptorPoolSize descPoolSizes[] = {
+ { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1 }
+ };
+ VkDescriptorPoolCreateInfo descPoolInfo;
+ memset(&descPoolInfo, 0, sizeof(descPoolInfo));
+ descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+ descPoolInfo.flags = 0; // won't use vkFreeDescriptorSets
+ descPoolInfo.maxSets = 1;
+ descPoolInfo.poolSizeCount = sizeof(descPoolSizes) / sizeof(descPoolSizes[0]);
+ descPoolInfo.pPoolSizes = descPoolSizes;
+ err = m_devFuncs->vkCreateDescriptorPool(m_dev, &descPoolInfo, nullptr, &m_descriptorPool);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create descriptor pool: %d", err);
+
+ VkDescriptorSetAllocateInfo descAllocInfo;
+ memset(&descAllocInfo, 0, sizeof(descAllocInfo));
+ descAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ descAllocInfo.descriptorPool = m_descriptorPool;
+ descAllocInfo.descriptorSetCount = 1;
+ descAllocInfo.pSetLayouts = &m_resLayout;
+ err = m_devFuncs->vkAllocateDescriptorSets(m_dev, &descAllocInfo, &m_ubufDescriptor);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to allocate descriptor set");
+
+ VkWriteDescriptorSet writeInfo;
+ memset(&writeInfo, 0, sizeof(writeInfo));
+ writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ writeInfo.dstSet = m_ubufDescriptor;
+ writeInfo.dstBinding = 0;
+ writeInfo.descriptorCount = 1;
+ writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
+ VkDescriptorBufferInfo bufInfo;
+ bufInfo.buffer = m_ubuf;
+ bufInfo.offset = 0; // dynamic offset is used so this is ignored
+ bufInfo.range = UBUF_SIZE;
+ writeInfo.pBufferInfo = &bufInfo;
+ m_devFuncs->vkUpdateDescriptorSets(m_dev, 1, &writeInfo, 0, nullptr);
+}
+
+#include "vulkansquircle.moc"
diff --git a/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.h b/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.h
new file mode 100644
index 0000000000..7e65d01a15
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef VULKANSQUIRCLE_H
+#define VULKANSQUIRCLE_H
+
+#include <QtQuick/QQuickItem>
+
+class SquircleRenderer;
+
+class VulkanSquircle : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged)
+
+public:
+ VulkanSquircle();
+
+ qreal t() const { return m_t; }
+ void setT(qreal t);
+
+signals:
+ void tChanged();
+
+public slots:
+ void sync();
+ void cleanup();
+
+private slots:
+ void handleWindowChanged(QQuickWindow *win);
+
+private:
+ void releaseResources() override;
+
+ qreal m_t = 0;
+ SquircleRenderer *m_renderer = nullptr;
+};
+
+#endif
diff --git a/examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.pro b/examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.pro
new file mode 100644
index 0000000000..9ea57b91c3
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.pro
@@ -0,0 +1,10 @@
+!qtConfig(vulkan): error("This example requires Qt built with Vulkan support")
+
+QT += qml quick
+
+HEADERS += vulkansquircle.h
+SOURCES += vulkansquircle.cpp main.cpp
+RESOURCES += vulkanunderqml.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/vulkanunderqml
+INSTALLS += target
diff --git a/examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.qrc b/examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.qrc
new file mode 100644
index 0000000000..c85be0f238
--- /dev/null
+++ b/examples/quick/scenegraph/vulkanunderqml/vulkanunderqml.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/scenegraph/vulkanunderqml">
+ <file>main.qml</file>
+ <file>squircle.vert.spv</file>
+ <file>squircle.frag.spv</file>
+ </qresource>
+</RCC>
diff --git a/examples/quick/shadereffects/content/shaders/+qsb/blur.frag b/examples/quick/shadereffects/content/shaders/+qsb/blur.frag
new file mode 100644
index 0000000000..1c79359297
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/+qsb/blur.frag
Binary files differ
diff --git a/examples/quick/shadereffects/content/shaders/+qsb/colorize.frag b/examples/quick/shadereffects/content/shaders/+qsb/colorize.frag
new file mode 100644
index 0000000000..45c5301f31
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/+qsb/colorize.frag
Binary files differ
diff --git a/examples/quick/shadereffects/content/shaders/+qsb/genie.vert b/examples/quick/shadereffects/content/shaders/+qsb/genie.vert
new file mode 100644
index 0000000000..dd94129cf7
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/+qsb/genie.vert
Binary files differ
diff --git a/examples/quick/shadereffects/content/shaders/+qsb/outline.frag b/examples/quick/shadereffects/content/shaders/+qsb/outline.frag
new file mode 100644
index 0000000000..470e2bd6e6
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/+qsb/outline.frag
Binary files differ
diff --git a/examples/quick/shadereffects/content/shaders/+qsb/shadow.frag b/examples/quick/shadereffects/content/shaders/+qsb/shadow.frag
new file mode 100644
index 0000000000..128af21daa
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/+qsb/shadow.frag
Binary files differ
diff --git a/examples/quick/shadereffects/content/shaders/+qsb/wobble.frag b/examples/quick/shadereffects/content/shaders/+qsb/wobble.frag
new file mode 100644
index 0000000000..9b27ae87cb
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/+qsb/wobble.frag
Binary files differ
diff --git a/examples/quick/shadereffects/content/shaders/rhi/blur.frag b/examples/quick/shadereffects/content/shaders/rhi/blur.frag
new file mode 100644
index 0000000000..0c914d4244
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/rhi/blur.frag
@@ -0,0 +1,21 @@
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord0;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D source;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float qt_Opacity;
+ vec2 delta;
+} ubuf;
+
+void main()
+{
+ fragColor =(0.0538 * texture(source, qt_TexCoord0 - 3.182 * ubuf.delta)
+ + 0.3229 * texture(source, qt_TexCoord0 - 1.364 * ubuf.delta)
+ + 0.2466 * texture(source, qt_TexCoord0)
+ + 0.3229 * texture(source, qt_TexCoord0 + 1.364 * ubuf.delta)
+ + 0.0538 * texture(source, qt_TexCoord0 + 3.182 * ubuf.delta)) * ubuf.qt_Opacity;
+}
diff --git a/examples/quick/shadereffects/content/shaders/rhi/colorize.frag b/examples/quick/shadereffects/content/shaders/rhi/colorize.frag
new file mode 100644
index 0000000000..bc8067cc8c
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/rhi/colorize.frag
@@ -0,0 +1,20 @@
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord0;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D source;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float qt_Opacity;
+ vec4 tint;
+} ubuf;
+
+void main()
+{
+ vec4 c = texture(source, qt_TexCoord0);
+ float lo = min(min(c.x, c.y), c.z);
+ float hi = max(max(c.x, c.y), c.z);
+ fragColor = ubuf.qt_Opacity * vec4(mix(vec3(lo), vec3(hi), ubuf.tint.xyz), c.w);
+}
diff --git a/examples/quick/shadereffects/content/shaders/rhi/compile.bat b/examples/quick/shadereffects/content/shaders/rhi/compile.bat
new file mode 100755
index 0000000000..93dfb7b2a9
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/rhi/compile.bat
@@ -0,0 +1,56 @@
+:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::
+:: Copyright (C) 2019 The Qt Company Ltd.
+:: Contact: https://www.qt.io/licensing/
+::
+:: This file is part of the examples of the Qt Toolkit.
+::
+:: $QT_BEGIN_LICENSE:BSD$
+:: 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.
+::
+:: BSD License Usage
+:: Alternatively, you may use this file under the terms of the BSD license
+:: as follows:
+::
+:: "Redistribution and use in source and binary forms, with or without
+:: modification, are permitted provided that the following conditions are
+:: met:
+:: * Redistributions of source code must retain the above copyright
+:: notice, this list of conditions and the following disclaimer.
+:: * Redistributions in binary form must reproduce the above copyright
+:: notice, this list of conditions and the following disclaimer in
+:: the documentation and/or other materials provided with the
+:: distribution.
+:: * Neither the name of The Qt Company Ltd nor the names of its
+:: contributors may be used to endorse or promote products derived
+:: from this software without specific prior written permission.
+::
+::
+:: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+:: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+:: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+:: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+:: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+:: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+:: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+:: DATA, OR 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."
+::
+:: $QT_END_LICENSE$
+::
+:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o ../+qsb/blur.frag blur.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o ../+qsb/colorize.frag colorize.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o ../+qsb/outline.frag outline.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o ../+qsb/shadow.frag shadow.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o ../+qsb/wobble.frag wobble.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o ../+qsb/genie.vert genie.vert
diff --git a/examples/quick/shadereffects/content/shaders/rhi/genie.vert b/examples/quick/shadereffects/content/shaders/rhi/genie.vert
new file mode 100644
index 0000000000..2cb1e71046
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/rhi/genie.vert
@@ -0,0 +1,29 @@
+#version 440
+
+layout(location = 0) in vec4 qt_Vertex;
+layout(location = 1) in vec2 qt_MultiTexCoord0;
+
+layout(location = 0) out vec2 qt_TexCoord0;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float qt_Opacity;
+ float bend;
+ float minimize;
+ float side;
+ float width;
+ float height;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ qt_TexCoord0 = qt_MultiTexCoord0;
+ vec4 pos = qt_Vertex;
+ pos.y = mix(qt_Vertex.y, ubuf.height, ubuf.minimize);
+ float t = pos.y / ubuf.height;
+ t = (3. - 2. * t) * t * t;
+ pos.x = mix(qt_Vertex.x, ubuf.side * ubuf.width, t * ubuf.bend);
+ gl_Position = ubuf.qt_Matrix * pos;
+}
diff --git a/examples/quick/shadereffects/content/shaders/rhi/outline.frag b/examples/quick/shadereffects/content/shaders/rhi/outline.frag
new file mode 100644
index 0000000000..26df69c5fe
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/rhi/outline.frag
@@ -0,0 +1,24 @@
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord0;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D source;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float qt_Opacity;
+ vec2 delta;
+} ubuf;
+
+void main()
+{
+ vec4 tl = texture(source, qt_TexCoord0 - ubuf.delta);
+ vec4 tr = texture(source, qt_TexCoord0 + vec2(ubuf.delta.x, -ubuf.delta.y));
+ vec4 bl = texture(source, qt_TexCoord0 - vec2(ubuf.delta.x, -ubuf.delta.y));
+ vec4 br = texture(source, qt_TexCoord0 + ubuf.delta);
+ vec4 gx = (tl + bl) - (tr + br);
+ vec4 gy = (tl + tr) - (bl + br);
+ fragColor.xyz = vec3(0.);
+ fragColor.w = clamp(dot(sqrt(gx * gx + gy * gy), vec4(1.)), 0., 1.) * ubuf.qt_Opacity;
+}
diff --git a/examples/quick/shadereffects/content/shaders/rhi/shadow.frag b/examples/quick/shadereffects/content/shaders/rhi/shadow.frag
new file mode 100644
index 0000000000..8247517b6d
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/rhi/shadow.frag
@@ -0,0 +1,21 @@
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord0;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D source;
+layout(binding = 2) uniform sampler2D shadow;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float qt_Opacity;
+ float darkness;
+ vec2 delta;
+} ubuf;
+
+void main()
+{
+ vec4 fg = texture(source, qt_TexCoord0);
+ vec4 bg = texture(shadow, qt_TexCoord0 + ubuf.delta);
+ fragColor = (fg + vec4(0., 0., 0., ubuf.darkness * bg.a) * (1. - fg.a)) * ubuf.qt_Opacity;
+}
diff --git a/examples/quick/shadereffects/content/shaders/rhi/wobble.frag b/examples/quick/shadereffects/content/shaders/rhi/wobble.frag
new file mode 100644
index 0000000000..a34481c2f2
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/rhi/wobble.frag
@@ -0,0 +1,20 @@
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord0;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D source;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float qt_Opacity;
+ float amplitude;
+ float frequency;
+ float time;
+} ubuf;
+
+void main()
+{
+ vec2 p = sin(ubuf.time + ubuf.frequency * qt_TexCoord0);
+ fragColor = texture(source, qt_TexCoord0 + ubuf.amplitude * vec2(p.y, -p.x)) * ubuf.qt_Opacity;
+}
diff --git a/examples/quick/shadereffects/doc/src/shadereffects.qdoc b/examples/quick/shadereffects/doc/src/shadereffects.qdoc
index 8cb4024da2..d35989c262 100644
--- a/examples/quick/shadereffects/doc/src/shadereffects.qdoc
+++ b/examples/quick/shadereffects/doc/src/shadereffects.qdoc
@@ -52,10 +52,18 @@
\snippet shadereffects/shadereffects.qml fragment
In order to support multiple graphics APIs, not just OpenGL, the shader
- source is not embedded into QML. Instead, file selectors are used to select
- the correct variant at runtime. Based on the Qt Quick backend in use, Qt
- will automatically select either \c{shaders/wobble.frag} with the GLSL
- source code or \c{shaders/+hlsl/wobble.frag} with the HLSL source code.
+ source is not embedded into QML. When running with the graphics API
+ independent scene graph, the actual file in use is a pre-generated shader
+ pack containing multiple variants of the shader code. The appropriate
+ shader is then chosen by Qt Quick, regardless of running on Vulkan, Metal,
+ Direct 3D, or OpenGL. Qt automatically selects the file under the \c qsb
+ selector, for example \c{shaders/+qsb/wobble.frag}, when present.
+
+ On the traditional code path, which can mean using OpenGL or Direct3D 12,
+ file selectors are used to select the correct variant at runtime. Based on
+ the Qt Quick backend in use, Qt will automatically select either
+ \c{shaders/wobble.frag} with the GLSL source code or
+ \c{shaders/+hlsl/wobble.frag} with the HLSL source code.
\note For simplicity shader source code is used in all variants of the
files. However, with the Direct3D backend of Qt Quick pre-compiled shaders
diff --git a/examples/quick/shadereffects/shadereffects.qrc b/examples/quick/shadereffects/shadereffects.qrc
index e66b98a6df..762ad0d037 100644
--- a/examples/quick/shadereffects/shadereffects.qrc
+++ b/examples/quick/shadereffects/shadereffects.qrc
@@ -6,15 +6,21 @@
<file>content/Slider.qml</file>
<file>content/shaders/wobble.frag</file>
<file>content/shaders/+hlsl/wobble.frag</file>
+ <file>content/shaders/+qsb/wobble.frag</file>
<file>content/shaders/blur.frag</file>
<file>content/shaders/+hlsl/blur.frag</file>
+ <file>content/shaders/+qsb/blur.frag</file>
<file>content/shaders/shadow.frag</file>
<file>content/shaders/+hlsl/shadow.frag</file>
+ <file>content/shaders/+qsb/shadow.frag</file>
<file>content/shaders/outline.frag</file>
<file>content/shaders/+hlsl/outline.frag</file>
+ <file>content/shaders/+qsb/outline.frag</file>
<file>content/shaders/colorize.frag</file>
<file>content/shaders/+hlsl/colorize.frag</file>
+ <file>content/shaders/+qsb/colorize.frag</file>
<file>content/shaders/genie.vert</file>
<file>content/shaders/+hlsl/genie.vert</file>
+ <file>content/shaders/+qsb/genie.vert</file>
</qresource>
</RCC>
diff --git a/examples/quick/shapes/content/interactive.qml b/examples/quick/shapes/content/interactive.qml
index 55a1d16299..78413db3f9 100644
--- a/examples/quick/shapes/content/interactive.qml
+++ b/examples/quick/shapes/content/interactive.qml
@@ -170,14 +170,45 @@ Rectangle {
property variant resizers: []
property variant funcs
+ property Component mouseArea: Component {
+ Rectangle {
+ id: rr
+
+ property variant obj
+ property string xprop
+ property string yprop
+
+ width: 20
+ height: 20
+
+ MouseArea {
+ property bool a: false
+
+ anchors.fill: parent
+ hoverEnabled: true
+ onEntered: color = "yellow"
+ onExited: color = rr.color
+ onPressed: a = true
+ onReleased: a = false
+ onPositionChanged: {
+ if (a) {
+ var pt = mapToItem(rr.parent, mouse.x, mouse.y);
+ rr.obj[rr.xprop] = pt.x
+ rr.obj[rr.yprop] = pt.y
+ rr.x = pt.x - 10
+ rr.y = pt.y - 10
+ }
+ }
+ }
+ }
+ }
+
function genResizer(obj, x, y, xprop, yprop, color) {
- var ma = Qt.createQmlObject('import QtQuick 2.9; import QtQuick.Shapes 1.0; Rectangle { id: rr; property variant obj; color: "' + color + '"; width: 20; height: 20;'+
- 'MouseArea { anchors.fill: parent; hoverEnabled: true;' +
- 'onEntered: color = "yellow"; onExited: color = "' + color + '";' +
- 'property bool a: false; onPressed: a = true; onReleased: a = false; ' +
- 'onPositionChanged: if (a) { var pt = mapToItem(rr.parent, mouse.x, mouse.y);' +
- 'obj.' + xprop + ' = pt.x; obj.' + yprop + ' = pt.y; rr.x = pt.x - 10; rr.y = pt.y - 10; } } }',
- canvas, "resizer_item");
+ var ma = mouseArea.createObject(canvas, {
+ color: color,
+ xprop: xprop,
+ yprop: yprop
+ });
ma.visible = root.showResizers;
ma.obj = obj;
ma.x = x - 10;
@@ -186,15 +217,55 @@ Rectangle {
return ma;
}
+ property Component linePath: Component {
+ ShapePath {
+ id: lineShapePath
+ strokeColor: "black"
+ strokeWidth: widthSlider.value
+ fillColor: "transparent"
+ PathLine {
+ x: lineShapePath.startX + 1
+ y: lineShapePath.startY + 1
+ }
+ }
+ }
+
+ property Component cubicPath: Component {
+ ShapePath {
+ id: cubicShapePath
+ strokeColor: "black"
+ strokeWidth: widthSlider.value
+ fillColor: root.fill ? 'green' : 'transparent'
+ PathCubic {
+ x: cubicShapePath.startX + 1
+ y: cubicShapePath.startY + 1
+ control1X: cubicShapePath.startX + 50;
+ control1Y: cubicShapePath.startY + 50;
+ control2X: cubicShapePath.startX + 150;
+ control2Y: cubicShapePath.startY + 50;
+ }
+ }
+ }
+
+ property Component quadPath: Component {
+ ShapePath {
+ id: quadShapePath
+ strokeColor: "black"
+ strokeWidth: widthSlider.value
+ fillColor: root.fill ? 'green' : 'transparent'
+ PathQuad {
+ x: quadShapePath.startx + 1
+ y: quadShapePath.startY + 1
+ controlX: quadShapePath.startX + 50
+ controlY: quadShapePath.startY + 50
+ }
+ }
+ }
+
Component.onCompleted: {
funcs = [
{ "start": function(x, y) {
- var p = Qt.createQmlObject('import QtQuick 2.9; import QtQuick.Shapes 1.0; ShapePath {' +
- 'strokeColor: "black"; fillColor: "transparent";'+
- 'strokeWidth: ' + widthSlider.value + ';' +
- 'startX: ' + x + '; startY: ' + y + ';' +
- 'PathLine { x: ' + x + ' + 1; y: ' + y + ' + 1 } }',
- root, "dynamic_visual_path");
+ var p = linePath.createObject(root, { startX: x, startY: y });
shape.data.push(p);
activePath = p;
}, "move": function(x, y) {
@@ -211,13 +282,7 @@ Rectangle {
}
},
{ "start": function(x, y) {
- var p = Qt.createQmlObject('import QtQuick 2.9; import QtQuick.Shapes 1.0; ShapePath {' +
- 'strokeColor: "black"; fillColor: "' + (root.fill ? 'green' : 'transparent') + '";'+
- 'strokeWidth: ' + widthSlider.value + ';' +
- 'startX: ' + x + '; startY: ' + y + ';' +
- 'PathCubic { x: ' + x + ' + 1; y: ' + y + ' + 1;' +
- 'control1X: ' + x + ' + 50; control1Y: ' + y + ' + 50; control2X: ' + x + ' + 150; control2Y: ' + y + ' + 50; } }',
- root, "dynamic_visual_path");
+ var p = cubicPath.createObject(root, { startX: x, startY: y });
shape.data.push(p);
activePath = p;
}, "move": function(x, y) {
@@ -236,13 +301,7 @@ Rectangle {
}
},
{ "start": function(x, y) {
- var p = Qt.createQmlObject('import QtQuick 2.9; import QtQuick.Shapes 1.0; ShapePath {' +
- 'strokeColor: "black"; fillColor: "' + (root.fill ? 'green' : 'transparent') + '";'+
- 'strokeWidth: ' + widthSlider.value + ';' +
- 'startX: ' + x + '; startY: ' + y + ';' +
- 'PathQuad { x: ' + x + ' + 1; y: ' + y + ' + 1;' +
- 'controlX: ' + x + ' + 50; controlY: ' + y + ' + 50 } }',
- root, "dynamic_visual_path");
+ var p = quadPath.createObject(root, { startX: x, startY: y });
shape.data.push(p);
activePath = p;
}, "move": function(x, y) {
diff --git a/examples/quick/shared/CheckBox.qml b/examples/quick/shared/CheckBox.qml
index 7b1588d2d9..51b6aabc09 100644
--- a/examples/quick/shared/CheckBox.qml
+++ b/examples/quick/shared/CheckBox.qml
@@ -87,7 +87,7 @@ Item {
anchors.margins: frame.width / 5
fillMode: Image.PreserveAspectFit
smooth: true
- visible: checked
+ visible: root.checked
}
}
Text {
diff --git a/examples/quick/shared/LauncherList.qml b/examples/quick/shared/LauncherList.qml
index fe44cf3634..9859b5b635 100644
--- a/examples/quick/shared/LauncherList.qml
+++ b/examples/quick/shared/LauncherList.qml
@@ -51,6 +51,7 @@
import QtQuick 2.12
Rectangle {
+ id: root
property int activePageCount: 0
//model is a list of {"name":"somename", "url":"file:///some/url/mainfile.qml"}
@@ -74,7 +75,7 @@ Rectangle {
id: launcherList
clip: true
delegate: SimpleLauncherDelegate{
- onClicked: showExample(url)
+ onClicked: root.showExample(url)
}
model: ListModel {id:myModel}
anchors.fill: parent
@@ -116,7 +117,7 @@ Rectangle {
ParallelAnimation {
id: showAnim
ScriptAction {
- script: activePageCount++
+ script: root.activePageCount++
}
NumberAnimation {
target: launcherList
@@ -144,7 +145,7 @@ Rectangle {
id: exitAnim
ScriptAction {
- script: activePageCount--
+ script: root.activePageCount--
}
ParallelAnimation {
@@ -182,7 +183,7 @@ Rectangle {
visible: height > 0
anchors.bottom: parent.bottom
width: parent.width
- height: activePageCount > 0 ? 40 : 0
+ height: root.activePageCount > 0 ? 40 : 0
Behavior on height {
NumberAnimation {
@@ -222,7 +223,7 @@ Rectangle {
TapHandler {
id: tapHandler
- enabled: activePageCount > 0
+ enabled: root.activePageCount > 0
onTapped: {
pageContainer.children[pageContainer.children.length - 1].exit()
}
diff --git a/examples/quick/shared/Slider.qml b/examples/quick/shared/Slider.qml
index cdda86e39e..5b08034571 100644
--- a/examples/quick/shared/Slider.qml
+++ b/examples/quick/shared/Slider.qml
@@ -83,7 +83,7 @@ Item {
anchors.left: parent.left
anchors.leftMargin: 16
height: childrenRect.height
- width: Math.max(minLabelWidth, childrenRect.width)
+ width: Math.max(slider.minLabelWidth, childrenRect.width)
anchors.verticalCenter: parent.verticalCenter
Text {
text: slider.name + ":"
diff --git a/examples/quick/tutorials/helloworld/Cell.qml b/examples/quick/tutorials/helloworld/Cell.qml
index 33b9bf9935..7462a5ceb1 100644
--- a/examples/quick/tutorials/helloworld/Cell.qml
+++ b/examples/quick/tutorials/helloworld/Cell.qml
@@ -58,7 +58,7 @@ Item {
property alias cellColor: rectangle.color
//![4]
//![5]
- signal clicked(color cellColor)
+ signal clicked(cellColor: color)
//![5]
width: 40; height: 25
diff --git a/src/3rdparty/llvm/LICENSE.TXT b/src/3rdparty/llvm/LICENSE.TXT
new file mode 100644
index 0000000000..ff63f2b6aa
--- /dev/null
+++ b/src/3rdparty/llvm/LICENSE.TXT
@@ -0,0 +1,68 @@
+==============================================================================
+LLVM Release License
+==============================================================================
+University of Illinois/NCSA
+Open Source License
+
+Copyright (c) 2003-2017 University of Illinois at Urbana-Champaign.
+All rights reserved.
+
+Developed by:
+
+ LLVM Team
+
+ University of Illinois at Urbana-Champaign
+
+ http://llvm.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimers.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimers in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the names of the LLVM Team, University of Illinois at
+ Urbana-Champaign, nor the names of its contributors may be used to
+ endorse or promote products derived from this Software without specific
+ prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+SOFTWARE.
+
+==============================================================================
+Copyrights and Licenses for Third Party Software Distributed with LLVM:
+==============================================================================
+The LLVM software contains code written by third parties. Such software will
+have its own individual LICENSE.TXT file in the directory in which it appears.
+This file will describe the copyrights, license, and restrictions which apply
+to that code.
+
+The disclaimer of warranty in the University of Illinois Open Source License
+applies to all code in the LLVM Distribution, and nothing in any of the
+other licenses gives permission to use the names of the LLVM Team or the
+University of Illinois to endorse or promote products derived from this
+Software.
+
+The following pieces of software have additional or alternate copyrights,
+licenses, and/or restrictions:
+
+Program Directory
+------- ---------
+Google Test llvm/utils/unittest/googletest
+OpenBSD regex llvm/lib/Support/{reg*, COPYRIGHT.regex}
+pyyaml tests llvm/test/YAMLParser/{*.data, LICENSE.TXT}
+ARM contributions llvm/lib/Target/ARM/LICENSE.TXT
+md5 contributions llvm/lib/Support/MD5.cpp llvm/include/llvm/Support/MD5.h
diff --git a/src/3rdparty/llvm/include/llvm-c/DataTypes.h b/src/3rdparty/llvm/include/llvm-c/DataTypes.h
new file mode 100644
index 0000000000..7081c83ffc
--- /dev/null
+++ b/src/3rdparty/llvm/include/llvm-c/DataTypes.h
@@ -0,0 +1,90 @@
+/*===-- include/llvm-c/DataTypes.h - Define fixed size types ------*- C -*-===*\
+|* *|
+|* The LLVM Compiler Infrastructure *|
+|* *|
+|* This file is distributed under the University of Illinois Open Source *|
+|* License. See LICENSE.TXT for details. *|
+|* *|
+|*===----------------------------------------------------------------------===*|
+|* *|
+|* This file contains definitions to figure out the size of _HOST_ data types.*|
+|* This file is important because different host OS's define different macros,*|
+|* which makes portability tough. This file exports the following *|
+|* definitions: *|
+|* *|
+|* [u]int(32|64)_t : typedefs for signed and unsigned 32/64 bit system types*|
+|* [U]INT(8|16|32|64)_(MIN|MAX) : Constants for the min and max values. *|
+|* *|
+|* No library is required when using these functions. *|
+|* *|
+|*===----------------------------------------------------------------------===*/
+
+/* Please leave this file C-compatible. */
+
+#ifndef LLVM_C_DATATYPES_H
+#define LLVM_C_DATATYPES_H
+
+#ifdef __cplusplus
+#include <cmath>
+#else
+#include <math.h>
+#endif
+
+#include <inttypes.h>
+#include <stdint.h>
+
+#ifndef _MSC_VER
+
+#if !defined(UINT32_MAX)
+# error "The standard header <cstdint> is not C++11 compliant. Must #define "\
+ "__STDC_LIMIT_MACROS before #including llvm-c/DataTypes.h"
+#endif
+
+#if !defined(UINT32_C)
+# error "The standard header <cstdint> is not C++11 compliant. Must #define "\
+ "__STDC_CONSTANT_MACROS before #including llvm-c/DataTypes.h"
+#endif
+
+/* Note that <inttypes.h> includes <stdint.h>, if this is a C99 system. */
+#include <sys/types.h>
+
+#ifdef _AIX
+// GCC is strict about defining large constants: they must have LL modifier.
+#undef INT64_MAX
+#undef INT64_MIN
+#endif
+
+#else /* _MSC_VER */
+#ifdef __cplusplus
+#include <cstddef>
+#include <cstdlib>
+#else
+#include <stddef.h>
+#include <stdlib.h>
+#endif
+#include <sys/types.h>
+
+#if defined(_WIN64)
+typedef signed __int64 ssize_t;
+#else
+typedef signed int ssize_t;
+#endif /* _WIN64 */
+
+#endif /* _MSC_VER */
+
+/* Set defaults for constants which we cannot find. */
+#if !defined(INT64_MAX)
+# define INT64_MAX 9223372036854775807LL
+#endif
+#if !defined(INT64_MIN)
+# define INT64_MIN ((-INT64_MAX)-1)
+#endif
+#if !defined(UINT64_MAX)
+# define UINT64_MAX 0xffffffffffffffffULL
+#endif
+
+#ifndef HUGE_VALF
+#define HUGE_VALF (float)HUGE_VAL
+#endif
+
+#endif /* LLVM_C_DATATYPES_H */
diff --git a/src/3rdparty/llvm/include/llvm/ADT/PointerIntPair.h b/src/3rdparty/llvm/include/llvm/ADT/PointerIntPair.h
new file mode 100644
index 0000000000..884d05155b
--- /dev/null
+++ b/src/3rdparty/llvm/include/llvm/ADT/PointerIntPair.h
@@ -0,0 +1,233 @@
+//===- llvm/ADT/PointerIntPair.h - Pair for pointer and int -----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the PointerIntPair class.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_POINTERINTPAIR_H
+#define LLVM_ADT_POINTERINTPAIR_H
+
+#include "llvm/Support/PointerLikeTypeTraits.h"
+#include <cassert>
+#include <cstdint>
+#include <limits>
+
+namespace llvm {
+
+template <typename T> struct DenseMapInfo;
+template <typename PointerT, unsigned IntBits, typename PtrTraits>
+struct PointerIntPairInfo;
+
+/// PointerIntPair - This class implements a pair of a pointer and small
+/// integer. It is designed to represent this in the space required by one
+/// pointer by bitmangling the integer into the low part of the pointer. This
+/// can only be done for small integers: typically up to 3 bits, but it depends
+/// on the number of bits available according to PointerLikeTypeTraits for the
+/// type.
+///
+/// Note that PointerIntPair always puts the IntVal part in the highest bits
+/// possible. For example, PointerIntPair<void*, 1, bool> will put the bit for
+/// the bool into bit #2, not bit #0, which allows the low two bits to be used
+/// for something else. For example, this allows:
+/// PointerIntPair<PointerIntPair<void*, 1, bool>, 1, bool>
+/// ... and the two bools will land in different bits.
+template <typename PointerTy, unsigned IntBits, typename IntType = unsigned,
+ typename PtrTraits = PointerLikeTypeTraits<PointerTy>,
+ typename Info = PointerIntPairInfo<PointerTy, IntBits, PtrTraits>>
+class PointerIntPair {
+ intptr_t Value = 0;
+
+public:
+ constexpr PointerIntPair() = default;
+
+ PointerIntPair(PointerTy PtrVal, IntType IntVal) {
+ setPointerAndInt(PtrVal, IntVal);
+ }
+
+ explicit PointerIntPair(PointerTy PtrVal) { initWithPointer(PtrVal); }
+
+ PointerTy getPointer() const { return Info::getPointer(Value); }
+
+ IntType getInt() const { return (IntType)Info::getInt(Value); }
+
+ void setPointer(PointerTy PtrVal) {
+ Value = Info::updatePointer(Value, PtrVal);
+ }
+
+ void setInt(IntType IntVal) {
+ Value = Info::updateInt(Value, static_cast<intptr_t>(IntVal));
+ }
+
+ void initWithPointer(PointerTy PtrVal) {
+ Value = Info::updatePointer(0, PtrVal);
+ }
+
+ void setPointerAndInt(PointerTy PtrVal, IntType IntVal) {
+ Value = Info::updateInt(Info::updatePointer(0, PtrVal),
+ static_cast<intptr_t>(IntVal));
+ }
+
+ PointerTy const *getAddrOfPointer() const {
+ return const_cast<PointerIntPair *>(this)->getAddrOfPointer();
+ }
+
+ PointerTy *getAddrOfPointer() {
+ assert(Value == reinterpret_cast<intptr_t>(getPointer()) &&
+ "Can only return the address if IntBits is cleared and "
+ "PtrTraits doesn't change the pointer");
+ return reinterpret_cast<PointerTy *>(&Value);
+ }
+
+ void *getOpaqueValue() const { return reinterpret_cast<void *>(Value); }
+
+ void setFromOpaqueValue(void *Val) {
+ Value = reinterpret_cast<intptr_t>(Val);
+ }
+
+ static PointerIntPair getFromOpaqueValue(void *V) {
+ PointerIntPair P;
+ P.setFromOpaqueValue(V);
+ return P;
+ }
+
+ // Allow PointerIntPairs to be created from const void * if and only if the
+ // pointer type could be created from a const void *.
+ static PointerIntPair getFromOpaqueValue(const void *V) {
+ (void)PtrTraits::getFromVoidPointer(V);
+ return getFromOpaqueValue(const_cast<void *>(V));
+ }
+
+ bool operator==(const PointerIntPair &RHS) const {
+ return Value == RHS.Value;
+ }
+
+ bool operator!=(const PointerIntPair &RHS) const {
+ return Value != RHS.Value;
+ }
+
+ bool operator<(const PointerIntPair &RHS) const { return Value < RHS.Value; }
+ bool operator>(const PointerIntPair &RHS) const { return Value > RHS.Value; }
+
+ bool operator<=(const PointerIntPair &RHS) const {
+ return Value <= RHS.Value;
+ }
+
+ bool operator>=(const PointerIntPair &RHS) const {
+ return Value >= RHS.Value;
+ }
+};
+
+template <typename PointerT, unsigned IntBits, typename PtrTraits>
+struct PointerIntPairInfo {
+ static_assert(PtrTraits::NumLowBitsAvailable <
+ std::numeric_limits<uintptr_t>::digits,
+ "cannot use a pointer type that has all bits free");
+ static_assert(IntBits <= PtrTraits::NumLowBitsAvailable,
+ "PointerIntPair with integer size too large for pointer");
+ enum : uintptr_t {
+ /// PointerBitMask - The bits that come from the pointer.
+ PointerBitMask =
+ ~(uintptr_t)(((intptr_t)1 << PtrTraits::NumLowBitsAvailable) - 1),
+
+ /// IntShift - The number of low bits that we reserve for other uses, and
+ /// keep zero.
+ IntShift = (uintptr_t)PtrTraits::NumLowBitsAvailable - IntBits,
+
+ /// IntMask - This is the unshifted mask for valid bits of the int type.
+ IntMask = (uintptr_t)(((intptr_t)1 << IntBits) - 1),
+
+ // ShiftedIntMask - This is the bits for the integer shifted in place.
+ ShiftedIntMask = (uintptr_t)(IntMask << IntShift)
+ };
+
+ static PointerT getPointer(intptr_t Value) {
+ return PtrTraits::getFromVoidPointer(
+ reinterpret_cast<void *>(Value & PointerBitMask));
+ }
+
+ static intptr_t getInt(intptr_t Value) {
+ return (Value >> IntShift) & IntMask;
+ }
+
+ static intptr_t updatePointer(intptr_t OrigValue, PointerT Ptr) {
+ intptr_t PtrWord =
+ reinterpret_cast<intptr_t>(PtrTraits::getAsVoidPointer(Ptr));
+ assert((PtrWord & ~PointerBitMask) == 0 &&
+ "Pointer is not sufficiently aligned");
+ // Preserve all low bits, just update the pointer.
+ return PtrWord | (OrigValue & ~PointerBitMask);
+ }
+
+ static intptr_t updateInt(intptr_t OrigValue, intptr_t Int) {
+ intptr_t IntWord = static_cast<intptr_t>(Int);
+ assert((IntWord & ~IntMask) == 0 && "Integer too large for field");
+
+ // Preserve all bits other than the ones we are updating.
+ return (OrigValue & ~ShiftedIntMask) | IntWord << IntShift;
+ }
+};
+
+template <typename T> struct isPodLike;
+template <typename PointerTy, unsigned IntBits, typename IntType>
+struct isPodLike<PointerIntPair<PointerTy, IntBits, IntType>> {
+ static const bool value = true;
+};
+
+// Provide specialization of DenseMapInfo for PointerIntPair.
+template <typename PointerTy, unsigned IntBits, typename IntType>
+struct DenseMapInfo<PointerIntPair<PointerTy, IntBits, IntType>> {
+ using Ty = PointerIntPair<PointerTy, IntBits, IntType>;
+
+ static Ty getEmptyKey() {
+ uintptr_t Val = static_cast<uintptr_t>(-1);
+ Val <<= PointerLikeTypeTraits<Ty>::NumLowBitsAvailable;
+ return Ty::getFromOpaqueValue(reinterpret_cast<void *>(Val));
+ }
+
+ static Ty getTombstoneKey() {
+ uintptr_t Val = static_cast<uintptr_t>(-2);
+ Val <<= PointerLikeTypeTraits<PointerTy>::NumLowBitsAvailable;
+ return Ty::getFromOpaqueValue(reinterpret_cast<void *>(Val));
+ }
+
+ static unsigned getHashValue(Ty V) {
+ uintptr_t IV = reinterpret_cast<uintptr_t>(V.getOpaqueValue());
+ return unsigned(IV) ^ unsigned(IV >> 9);
+ }
+
+ static bool isEqual(const Ty &LHS, const Ty &RHS) { return LHS == RHS; }
+};
+
+// Teach SmallPtrSet that PointerIntPair is "basically a pointer".
+template <typename PointerTy, unsigned IntBits, typename IntType,
+ typename PtrTraits>
+struct PointerLikeTypeTraits<
+ PointerIntPair<PointerTy, IntBits, IntType, PtrTraits>> {
+ static inline void *
+ getAsVoidPointer(const PointerIntPair<PointerTy, IntBits, IntType> &P) {
+ return P.getOpaqueValue();
+ }
+
+ static inline PointerIntPair<PointerTy, IntBits, IntType>
+ getFromVoidPointer(void *P) {
+ return PointerIntPair<PointerTy, IntBits, IntType>::getFromOpaqueValue(P);
+ }
+
+ static inline PointerIntPair<PointerTy, IntBits, IntType>
+ getFromVoidPointer(const void *P) {
+ return PointerIntPair<PointerTy, IntBits, IntType>::getFromOpaqueValue(P);
+ }
+
+ enum { NumLowBitsAvailable = PtrTraits::NumLowBitsAvailable - IntBits };
+};
+
+} // end namespace llvm
+
+#endif // LLVM_ADT_POINTERINTPAIR_H
diff --git a/src/3rdparty/llvm/include/llvm/ADT/ilist.h b/src/3rdparty/llvm/include/llvm/ADT/ilist.h
new file mode 100644
index 0000000000..7e346e1260
--- /dev/null
+++ b/src/3rdparty/llvm/include/llvm/ADT/ilist.h
@@ -0,0 +1,431 @@
+//==-- llvm/ADT/ilist.h - Intrusive Linked List Template ---------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines classes to implement an intrusive doubly linked list class
+// (i.e. each node of the list must contain a next and previous field for the
+// list.
+//
+// The ilist class itself should be a plug in replacement for list. This list
+// replacement does not provide a constant time size() method, so be careful to
+// use empty() when you really want to know if it's empty.
+//
+// The ilist class is implemented as a circular list. The list itself contains
+// a sentinel node, whose Next points at begin() and whose Prev points at
+// rbegin(). The sentinel node itself serves as end() and rend().
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_ILIST_H
+#define LLVM_ADT_ILIST_H
+
+#include "llvm/ADT/simple_ilist.h"
+#include <cassert>
+#include <cstddef>
+#include <iterator>
+
+namespace llvm {
+
+/// Use delete by default for iplist and ilist.
+///
+/// Specialize this to get different behaviour for ownership-related API. (If
+/// you really want ownership semantics, consider using std::list or building
+/// something like \a BumpPtrList.)
+///
+/// \see ilist_noalloc_traits
+template <typename NodeTy> struct ilist_alloc_traits {
+ static void deleteNode(NodeTy *V) { delete V; }
+};
+
+/// Custom traits to do nothing on deletion.
+///
+/// Specialize ilist_alloc_traits to inherit from this to disable the
+/// non-intrusive deletion in iplist (which implies ownership).
+///
+/// If you want purely intrusive semantics with no callbacks, consider using \a
+/// simple_ilist instead.
+///
+/// \code
+/// template <>
+/// struct ilist_alloc_traits<MyType> : ilist_noalloc_traits<MyType> {};
+/// \endcode
+template <typename NodeTy> struct ilist_noalloc_traits {
+ static void deleteNode(NodeTy *) {}
+};
+
+/// Callbacks do nothing by default in iplist and ilist.
+///
+/// Specialize this for to use callbacks for when nodes change their list
+/// membership.
+template <typename NodeTy> struct ilist_callback_traits {
+ void addNodeToList(NodeTy *) {}
+ void removeNodeFromList(NodeTy *) {}
+
+ /// Callback before transferring nodes to this list.
+ ///
+ /// \pre \c this!=&OldList
+ template <class Iterator>
+ void transferNodesFromList(ilist_callback_traits &OldList, Iterator /*first*/,
+ Iterator /*last*/) {
+ (void)OldList;
+ }
+};
+
+/// A fragment for template traits for intrusive list that provides default
+/// node related operations.
+///
+/// TODO: Remove this layer of indirection. It's not necessary.
+template <typename NodeTy>
+struct ilist_node_traits : ilist_alloc_traits<NodeTy>,
+ ilist_callback_traits<NodeTy> {};
+
+/// Default template traits for intrusive list.
+///
+/// By inheriting from this, you can easily use default implementations for all
+/// common operations.
+///
+/// TODO: Remove this customization point. Specializing ilist_traits is
+/// already fully general.
+template <typename NodeTy>
+struct ilist_default_traits : public ilist_node_traits<NodeTy> {};
+
+/// Template traits for intrusive list.
+///
+/// Customize callbacks and allocation semantics.
+template <typename NodeTy>
+struct ilist_traits : public ilist_default_traits<NodeTy> {};
+
+/// Const traits should never be instantiated.
+template <typename Ty> struct ilist_traits<const Ty> {};
+
+namespace ilist_detail {
+
+template <class T> T &make();
+
+/// Type trait to check for a traits class that has a getNext member (as a
+/// canary for any of the ilist_nextprev_traits API).
+template <class TraitsT, class NodeT> struct HasGetNext {
+ typedef char Yes[1];
+ typedef char No[2];
+ template <size_t N> struct SFINAE {};
+
+ template <class U>
+ static Yes &test(U *I, decltype(I->getNext(&make<NodeT>())) * = 0);
+ template <class> static No &test(...);
+
+public:
+ static const bool value = sizeof(test<TraitsT>(nullptr)) == sizeof(Yes);
+};
+
+/// Type trait to check for a traits class that has a createSentinel member (as
+/// a canary for any of the ilist_sentinel_traits API).
+template <class TraitsT> struct HasCreateSentinel {
+ typedef char Yes[1];
+ typedef char No[2];
+
+ template <class U>
+ static Yes &test(U *I, decltype(I->createSentinel()) * = 0);
+ template <class> static No &test(...);
+
+public:
+ static const bool value = sizeof(test<TraitsT>(nullptr)) == sizeof(Yes);
+};
+
+/// Type trait to check for a traits class that has a createNode member.
+/// Allocation should be managed in a wrapper class, instead of in
+/// ilist_traits.
+template <class TraitsT, class NodeT> struct HasCreateNode {
+ typedef char Yes[1];
+ typedef char No[2];
+ template <size_t N> struct SFINAE {};
+
+ template <class U>
+ static Yes &test(U *I, decltype(I->createNode(make<NodeT>())) * = 0);
+ template <class> static No &test(...);
+
+public:
+ static const bool value = sizeof(test<TraitsT>(nullptr)) == sizeof(Yes);
+};
+
+template <class TraitsT, class NodeT> struct HasObsoleteCustomization {
+ static const bool value = HasGetNext<TraitsT, NodeT>::value ||
+ HasCreateSentinel<TraitsT>::value ||
+ HasCreateNode<TraitsT, NodeT>::value;
+};
+
+} // end namespace ilist_detail
+
+//===----------------------------------------------------------------------===//
+//
+/// A wrapper around an intrusive list with callbacks and non-intrusive
+/// ownership.
+///
+/// This wraps a purely intrusive list (like simple_ilist) with a configurable
+/// traits class. The traits can implement callbacks and customize the
+/// ownership semantics.
+///
+/// This is a subset of ilist functionality that can safely be used on nodes of
+/// polymorphic types, i.e. a heterogeneous list with a common base class that
+/// holds the next/prev pointers. The only state of the list itself is an
+/// ilist_sentinel, which holds pointers to the first and last nodes in the
+/// list.
+template <class IntrusiveListT, class TraitsT>
+class iplist_impl : public TraitsT, IntrusiveListT {
+ typedef IntrusiveListT base_list_type;
+
+public:
+ typedef typename base_list_type::pointer pointer;
+ typedef typename base_list_type::const_pointer const_pointer;
+ typedef typename base_list_type::reference reference;
+ typedef typename base_list_type::const_reference const_reference;
+ typedef typename base_list_type::value_type value_type;
+ typedef typename base_list_type::size_type size_type;
+ typedef typename base_list_type::difference_type difference_type;
+ typedef typename base_list_type::iterator iterator;
+ typedef typename base_list_type::const_iterator const_iterator;
+ typedef typename base_list_type::reverse_iterator reverse_iterator;
+ typedef
+ typename base_list_type::const_reverse_iterator const_reverse_iterator;
+
+private:
+ // TODO: Drop this assertion and the transitive type traits anytime after
+ // v4.0 is branched (i.e,. keep them for one release to help out-of-tree code
+ // update).
+ static_assert(
+ !ilist_detail::HasObsoleteCustomization<TraitsT, value_type>::value,
+ "ilist customization points have changed!");
+
+ static bool op_less(const_reference L, const_reference R) { return L < R; }
+ static bool op_equal(const_reference L, const_reference R) { return L == R; }
+
+public:
+ iplist_impl() = default;
+
+ iplist_impl(const iplist_impl &) = delete;
+ iplist_impl &operator=(const iplist_impl &) = delete;
+
+ iplist_impl(iplist_impl &&X)
+ : TraitsT(std::move(X)), IntrusiveListT(std::move(X)) {}
+ iplist_impl &operator=(iplist_impl &&X) {
+ *static_cast<TraitsT *>(this) = std::move(X);
+ *static_cast<IntrusiveListT *>(this) = std::move(X);
+ return *this;
+ }
+
+ ~iplist_impl() { clear(); }
+
+ // Miscellaneous inspection routines.
+ size_type max_size() const { return size_type(-1); }
+
+ using base_list_type::begin;
+ using base_list_type::end;
+ using base_list_type::rbegin;
+ using base_list_type::rend;
+ using base_list_type::empty;
+ using base_list_type::front;
+ using base_list_type::back;
+
+ void swap(iplist_impl &RHS) {
+ assert(0 && "Swap does not use list traits callback correctly yet!");
+ base_list_type::swap(RHS);
+ }
+
+ iterator insert(iterator where, pointer New) {
+ this->addNodeToList(New); // Notify traits that we added a node...
+ return base_list_type::insert(where, *New);
+ }
+
+ iterator insert(iterator where, const_reference New) {
+ return this->insert(where, new value_type(New));
+ }
+
+ iterator insertAfter(iterator where, pointer New) {
+ if (empty())
+ return insert(begin(), New);
+ else
+ return insert(++where, New);
+ }
+
+ /// Clone another list.
+ template <class Cloner> void cloneFrom(const iplist_impl &L2, Cloner clone) {
+ clear();
+ for (const_reference V : L2)
+ push_back(clone(V));
+ }
+
+ pointer remove(iterator &IT) {
+ pointer Node = &*IT++;
+ this->removeNodeFromList(Node); // Notify traits that we removed a node...
+ base_list_type::remove(*Node);
+ return Node;
+ }
+
+ pointer remove(const iterator &IT) {
+ iterator MutIt = IT;
+ return remove(MutIt);
+ }
+
+ pointer remove(pointer IT) { return remove(iterator(IT)); }
+ pointer remove(reference IT) { return remove(iterator(IT)); }
+
+ // erase - remove a node from the controlled sequence... and delete it.
+ iterator erase(iterator where) {
+ this->deleteNode(remove(where));
+ return where;
+ }
+
+ iterator erase(pointer IT) { return erase(iterator(IT)); }
+ iterator erase(reference IT) { return erase(iterator(IT)); }
+
+ /// Remove all nodes from the list like clear(), but do not call
+ /// removeNodeFromList() or deleteNode().
+ ///
+ /// This should only be used immediately before freeing nodes in bulk to
+ /// avoid traversing the list and bringing all the nodes into cache.
+ void clearAndLeakNodesUnsafely() { base_list_type::clear(); }
+
+private:
+ // transfer - The heart of the splice function. Move linked list nodes from
+ // [first, last) into position.
+ //
+ void transfer(iterator position, iplist_impl &L2, iterator first, iterator last) {
+ if (position == last)
+ return;
+
+ if (this != &L2) // Notify traits we moved the nodes...
+ this->transferNodesFromList(L2, first, last);
+
+ base_list_type::splice(position, L2, first, last);
+ }
+
+public:
+ //===----------------------------------------------------------------------===
+ // Functionality derived from other functions defined above...
+ //
+
+ using base_list_type::size;
+
+ iterator erase(iterator first, iterator last) {
+ while (first != last)
+ first = erase(first);
+ return last;
+ }
+
+ void clear() { erase(begin(), end()); }
+
+ // Front and back inserters...
+ void push_front(pointer val) { insert(begin(), val); }
+ void push_back(pointer val) { insert(end(), val); }
+ void pop_front() {
+ assert(!empty() && "pop_front() on empty list!");
+ erase(begin());
+ }
+ void pop_back() {
+ assert(!empty() && "pop_back() on empty list!");
+ iterator t = end(); erase(--t);
+ }
+
+ // Special forms of insert...
+ template<class InIt> void insert(iterator where, InIt first, InIt last) {
+ for (; first != last; ++first) insert(where, *first);
+ }
+
+ // Splice members - defined in terms of transfer...
+ void splice(iterator where, iplist_impl &L2) {
+ if (!L2.empty())
+ transfer(where, L2, L2.begin(), L2.end());
+ }
+ void splice(iterator where, iplist_impl &L2, iterator first) {
+ iterator last = first; ++last;
+ if (where == first || where == last) return; // No change
+ transfer(where, L2, first, last);
+ }
+ void splice(iterator where, iplist_impl &L2, iterator first, iterator last) {
+ if (first != last) transfer(where, L2, first, last);
+ }
+ void splice(iterator where, iplist_impl &L2, reference N) {
+ splice(where, L2, iterator(N));
+ }
+ void splice(iterator where, iplist_impl &L2, pointer N) {
+ splice(where, L2, iterator(N));
+ }
+
+ template <class Compare>
+ void merge(iplist_impl &Right, Compare comp) {
+ if (this == &Right)
+ return;
+ this->transferNodesFromList(Right, Right.begin(), Right.end());
+ base_list_type::merge(Right, comp);
+ }
+ void merge(iplist_impl &Right) { return merge(Right, op_less); }
+
+ using base_list_type::sort;
+
+ /// \brief Get the previous node, or \c nullptr for the list head.
+ pointer getPrevNode(reference N) const {
+ auto I = N.getIterator();
+ if (I == begin())
+ return nullptr;
+ return &*std::prev(I);
+ }
+ /// \brief Get the previous node, or \c nullptr for the list head.
+ const_pointer getPrevNode(const_reference N) const {
+ return getPrevNode(const_cast<reference >(N));
+ }
+
+ /// \brief Get the next node, or \c nullptr for the list tail.
+ pointer getNextNode(reference N) const {
+ auto Next = std::next(N.getIterator());
+ if (Next == end())
+ return nullptr;
+ return &*Next;
+ }
+ /// \brief Get the next node, or \c nullptr for the list tail.
+ const_pointer getNextNode(const_reference N) const {
+ return getNextNode(const_cast<reference >(N));
+ }
+};
+
+/// An intrusive list with ownership and callbacks specified/controlled by
+/// ilist_traits, only with API safe for polymorphic types.
+///
+/// The \p Options parameters are the same as those for \a simple_ilist. See
+/// there for a description of what's available.
+template <class T, class... Options>
+class iplist
+ : public iplist_impl<simple_ilist<T, Options...>, ilist_traits<T>> {
+ using iplist_impl_type = typename iplist::iplist_impl;
+
+public:
+ iplist() = default;
+
+ iplist(const iplist &X) = delete;
+ iplist &operator=(const iplist &X) = delete;
+
+ iplist(iplist &&X) : iplist_impl_type(std::move(X)) {}
+ iplist &operator=(iplist &&X) {
+ *static_cast<iplist_impl_type *>(this) = std::move(X);
+ return *this;
+ }
+};
+
+template <class T, class... Options> using ilist = iplist<T, Options...>;
+
+} // end namespace llvm
+
+namespace std {
+
+ // Ensure that swap uses the fast list swap...
+ template<class Ty>
+ void swap(llvm::iplist<Ty> &Left, llvm::iplist<Ty> &Right) {
+ Left.swap(Right);
+ }
+
+} // end namespace std
+
+#endif // LLVM_ADT_ILIST_H
diff --git a/src/3rdparty/llvm/include/llvm/ADT/ilist_base.h b/src/3rdparty/llvm/include/llvm/ADT/ilist_base.h
new file mode 100644
index 0000000000..3d818a48d4
--- /dev/null
+++ b/src/3rdparty/llvm/include/llvm/ADT/ilist_base.h
@@ -0,0 +1,93 @@
+//===- llvm/ADT/ilist_base.h - Intrusive List Base --------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_ILIST_BASE_H
+#define LLVM_ADT_ILIST_BASE_H
+
+#include "llvm/ADT/ilist_node_base.h"
+#include <cassert>
+
+namespace llvm {
+
+/// Implementations of list algorithms using ilist_node_base.
+template <bool EnableSentinelTracking> class ilist_base {
+public:
+ using node_base_type = ilist_node_base<EnableSentinelTracking>;
+
+ static void insertBeforeImpl(node_base_type &Next, node_base_type &N) {
+ node_base_type &Prev = *Next.getPrev();
+ N.setNext(&Next);
+ N.setPrev(&Prev);
+ Prev.setNext(&N);
+ Next.setPrev(&N);
+ }
+
+ static void removeImpl(node_base_type &N) {
+ node_base_type *Prev = N.getPrev();
+ node_base_type *Next = N.getNext();
+ Next->setPrev(Prev);
+ Prev->setNext(Next);
+
+ // Not strictly necessary, but helps catch a class of bugs.
+ N.setPrev(nullptr);
+ N.setNext(nullptr);
+ }
+
+ static void removeRangeImpl(node_base_type &First, node_base_type &Last) {
+ node_base_type *Prev = First.getPrev();
+ node_base_type *Final = Last.getPrev();
+ Last.setPrev(Prev);
+ Prev->setNext(&Last);
+
+ // Not strictly necessary, but helps catch a class of bugs.
+ First.setPrev(nullptr);
+ Final->setNext(nullptr);
+ }
+
+ static void transferBeforeImpl(node_base_type &Next, node_base_type &First,
+ node_base_type &Last) {
+ if (&Next == &Last || &First == &Last)
+ return;
+
+ // Position cannot be contained in the range to be transferred.
+ assert(&Next != &First &&
+ // Check for the most common mistake.
+ "Insertion point can't be one of the transferred nodes");
+
+ node_base_type &Final = *Last.getPrev();
+
+ // Detach from old list/position.
+ First.getPrev()->setNext(&Last);
+ Last.setPrev(First.getPrev());
+
+ // Splice [First, Final] into its new list/position.
+ node_base_type &Prev = *Next.getPrev();
+ Final.setNext(&Next);
+ First.setPrev(&Prev);
+ Prev.setNext(&First);
+ Next.setPrev(&Final);
+ }
+
+ template <class T> static void insertBefore(T &Next, T &N) {
+ insertBeforeImpl(Next, N);
+ }
+
+ template <class T> static void remove(T &N) { removeImpl(N); }
+ template <class T> static void removeRange(T &First, T &Last) {
+ removeRangeImpl(First, Last);
+ }
+
+ template <class T> static void transferBefore(T &Next, T &First, T &Last) {
+ transferBeforeImpl(Next, First, Last);
+ }
+};
+
+} // end namespace llvm
+
+#endif // LLVM_ADT_ILIST_BASE_H
diff --git a/src/3rdparty/llvm/include/llvm/ADT/ilist_iterator.h b/src/3rdparty/llvm/include/llvm/ADT/ilist_iterator.h
new file mode 100644
index 0000000000..671e644e01
--- /dev/null
+++ b/src/3rdparty/llvm/include/llvm/ADT/ilist_iterator.h
@@ -0,0 +1,199 @@
+//===- llvm/ADT/ilist_iterator.h - Intrusive List Iterator ------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_ILIST_ITERATOR_H
+#define LLVM_ADT_ILIST_ITERATOR_H
+
+#include "llvm/ADT/ilist_node.h"
+#include <cassert>
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+
+namespace llvm {
+
+namespace ilist_detail {
+
+/// Find const-correct node types.
+template <class OptionsT, bool IsConst> struct IteratorTraits;
+template <class OptionsT> struct IteratorTraits<OptionsT, false> {
+ using value_type = typename OptionsT::value_type;
+ using pointer = typename OptionsT::pointer;
+ using reference = typename OptionsT::reference;
+ using node_pointer = ilist_node_impl<OptionsT> *;
+ using node_reference = ilist_node_impl<OptionsT> &;
+};
+template <class OptionsT> struct IteratorTraits<OptionsT, true> {
+ using value_type = const typename OptionsT::value_type;
+ using pointer = typename OptionsT::const_pointer;
+ using reference = typename OptionsT::const_reference;
+ using node_pointer = const ilist_node_impl<OptionsT> *;
+ using node_reference = const ilist_node_impl<OptionsT> &;
+};
+
+template <bool IsReverse> struct IteratorHelper;
+template <> struct IteratorHelper<false> : ilist_detail::NodeAccess {
+ using Access = ilist_detail::NodeAccess;
+
+ template <class T> static void increment(T *&I) { I = Access::getNext(*I); }
+ template <class T> static void decrement(T *&I) { I = Access::getPrev(*I); }
+};
+template <> struct IteratorHelper<true> : ilist_detail::NodeAccess {
+ using Access = ilist_detail::NodeAccess;
+
+ template <class T> static void increment(T *&I) { I = Access::getPrev(*I); }
+ template <class T> static void decrement(T *&I) { I = Access::getNext(*I); }
+};
+
+} // end namespace ilist_detail
+
+/// Iterator for intrusive lists based on ilist_node.
+template <class OptionsT, bool IsReverse, bool IsConst>
+class ilist_iterator : ilist_detail::SpecificNodeAccess<OptionsT> {
+ friend ilist_iterator<OptionsT, IsReverse, !IsConst>;
+ friend ilist_iterator<OptionsT, !IsReverse, IsConst>;
+ friend ilist_iterator<OptionsT, !IsReverse, !IsConst>;
+
+ using Traits = ilist_detail::IteratorTraits<OptionsT, IsConst>;
+ using Access = ilist_detail::SpecificNodeAccess<OptionsT>;
+
+public:
+ using value_type = typename Traits::value_type;
+ using pointer = typename Traits::pointer;
+ using reference = typename Traits::reference;
+ using difference_type = ptrdiff_t;
+ using iterator_category = std::bidirectional_iterator_tag;
+ using const_pointer = typename OptionsT::const_pointer;
+ using const_reference = typename OptionsT::const_reference;
+
+private:
+ using node_pointer = typename Traits::node_pointer;
+ using node_reference = typename Traits::node_reference;
+
+ node_pointer NodePtr = nullptr;
+
+public:
+ /// Create from an ilist_node.
+ explicit ilist_iterator(node_reference N) : NodePtr(&N) {}
+
+ explicit ilist_iterator(pointer NP) : NodePtr(Access::getNodePtr(NP)) {}
+ explicit ilist_iterator(reference NR) : NodePtr(Access::getNodePtr(&NR)) {}
+ ilist_iterator() = default;
+
+ // This is templated so that we can allow constructing a const iterator from
+ // a nonconst iterator...
+ template <bool RHSIsConst>
+ ilist_iterator(
+ const ilist_iterator<OptionsT, IsReverse, RHSIsConst> &RHS,
+ typename std::enable_if<IsConst || !RHSIsConst, void *>::type = nullptr)
+ : NodePtr(RHS.NodePtr) {}
+
+ // This is templated so that we can allow assigning to a const iterator from
+ // a nonconst iterator...
+ template <bool RHSIsConst>
+ typename std::enable_if<IsConst || !RHSIsConst, ilist_iterator &>::type
+ operator=(const ilist_iterator<OptionsT, IsReverse, RHSIsConst> &RHS) {
+ NodePtr = RHS.NodePtr;
+ return *this;
+ }
+
+ /// Explicit conversion between forward/reverse iterators.
+ ///
+ /// Translate between forward and reverse iterators without changing range
+ /// boundaries. The resulting iterator will dereference (and have a handle)
+ /// to the previous node, which is somewhat unexpected; but converting the
+ /// two endpoints in a range will give the same range in reverse.
+ ///
+ /// This matches std::reverse_iterator conversions.
+ explicit ilist_iterator(
+ const ilist_iterator<OptionsT, !IsReverse, IsConst> &RHS)
+ : ilist_iterator(++RHS.getReverse()) {}
+
+ /// Get a reverse iterator to the same node.
+ ///
+ /// Gives a reverse iterator that will dereference (and have a handle) to the
+ /// same node. Converting the endpoint iterators in a range will give a
+ /// different range; for range operations, use the explicit conversions.
+ ilist_iterator<OptionsT, !IsReverse, IsConst> getReverse() const {
+ if (NodePtr)
+ return ilist_iterator<OptionsT, !IsReverse, IsConst>(*NodePtr);
+ return ilist_iterator<OptionsT, !IsReverse, IsConst>();
+ }
+
+ /// Const-cast.
+ ilist_iterator<OptionsT, IsReverse, false> getNonConst() const {
+ if (NodePtr)
+ return ilist_iterator<OptionsT, IsReverse, false>(
+ const_cast<typename ilist_iterator<OptionsT, IsReverse,
+ false>::node_reference>(*NodePtr));
+ return ilist_iterator<OptionsT, IsReverse, false>();
+ }
+
+ // Accessors...
+ reference operator*() const {
+ assert(!NodePtr->isKnownSentinel());
+ return *Access::getValuePtr(NodePtr);
+ }
+ pointer operator->() const { return &operator*(); }
+
+ // Comparison operators
+ friend bool operator==(const ilist_iterator &LHS, const ilist_iterator &RHS) {
+ return LHS.NodePtr == RHS.NodePtr;
+ }
+ friend bool operator!=(const ilist_iterator &LHS, const ilist_iterator &RHS) {
+ return LHS.NodePtr != RHS.NodePtr;
+ }
+
+ // Increment and decrement operators...
+ ilist_iterator &operator--() {
+ NodePtr = IsReverse ? NodePtr->getNext() : NodePtr->getPrev();
+ return *this;
+ }
+ ilist_iterator &operator++() {
+ NodePtr = IsReverse ? NodePtr->getPrev() : NodePtr->getNext();
+ return *this;
+ }
+ ilist_iterator operator--(int) {
+ ilist_iterator tmp = *this;
+ --*this;
+ return tmp;
+ }
+ ilist_iterator operator++(int) {
+ ilist_iterator tmp = *this;
+ ++*this;
+ return tmp;
+ }
+
+ /// Get the underlying ilist_node.
+ node_pointer getNodePtr() const { return static_cast<node_pointer>(NodePtr); }
+
+ /// Check for end. Only valid if ilist_sentinel_tracking<true>.
+ bool isEnd() const { return NodePtr ? NodePtr->isSentinel() : false; }
+};
+
+template <typename From> struct simplify_type;
+
+/// Allow ilist_iterators to convert into pointers to a node automatically when
+/// used by the dyn_cast, cast, isa mechanisms...
+///
+/// FIXME: remove this, since there is no implicit conversion to NodeTy.
+template <class OptionsT, bool IsConst>
+struct simplify_type<ilist_iterator<OptionsT, false, IsConst>> {
+ using iterator = ilist_iterator<OptionsT, false, IsConst>;
+ using SimpleType = typename iterator::pointer;
+
+ static SimpleType getSimplifiedValue(const iterator &Node) { return &*Node; }
+};
+template <class OptionsT, bool IsConst>
+struct simplify_type<const ilist_iterator<OptionsT, false, IsConst>>
+ : simplify_type<ilist_iterator<OptionsT, false, IsConst>> {};
+
+} // end namespace llvm
+
+#endif // LLVM_ADT_ILIST_ITERATOR_H
diff --git a/src/3rdparty/llvm/include/llvm/ADT/ilist_node.h b/src/3rdparty/llvm/include/llvm/ADT/ilist_node.h
new file mode 100644
index 0000000000..3362611697
--- /dev/null
+++ b/src/3rdparty/llvm/include/llvm/ADT/ilist_node.h
@@ -0,0 +1,306 @@
+//===- llvm/ADT/ilist_node.h - Intrusive Linked List Helper -----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the ilist_node class template, which is a convenient
+// base class for creating classes that can be used with ilists.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_ILIST_NODE_H
+#define LLVM_ADT_ILIST_NODE_H
+
+#include "llvm/ADT/ilist_node_base.h"
+#include "llvm/ADT/ilist_node_options.h"
+
+namespace llvm {
+
+namespace ilist_detail {
+
+struct NodeAccess;
+
+} // end namespace ilist_detail
+
+template <class OptionsT, bool IsReverse, bool IsConst> class ilist_iterator;
+template <class OptionsT> class ilist_sentinel;
+
+/// Implementation for an ilist node.
+///
+/// Templated on an appropriate \a ilist_detail::node_options, usually computed
+/// by \a ilist_detail::compute_node_options.
+///
+/// This is a wrapper around \a ilist_node_base whose main purpose is to
+/// provide type safety: you can't insert nodes of \a ilist_node_impl into the
+/// wrong \a simple_ilist or \a iplist.
+template <class OptionsT> class ilist_node_impl : OptionsT::node_base_type {
+ using value_type = typename OptionsT::value_type;
+ using node_base_type = typename OptionsT::node_base_type;
+ using list_base_type = typename OptionsT::list_base_type;
+
+ friend typename OptionsT::list_base_type;
+ friend struct ilist_detail::NodeAccess;
+ friend class ilist_sentinel<OptionsT>;
+ friend class ilist_iterator<OptionsT, false, false>;
+ friend class ilist_iterator<OptionsT, false, true>;
+ friend class ilist_iterator<OptionsT, true, false>;
+ friend class ilist_iterator<OptionsT, true, true>;
+
+protected:
+ using self_iterator = ilist_iterator<OptionsT, false, false>;
+ using const_self_iterator = ilist_iterator<OptionsT, false, true>;
+ using reverse_self_iterator = ilist_iterator<OptionsT, true, false>;
+ using const_reverse_self_iterator = ilist_iterator<OptionsT, true, true>;
+
+ ilist_node_impl() = default;
+
+private:
+ ilist_node_impl *getPrev() {
+ return static_cast<ilist_node_impl *>(node_base_type::getPrev());
+ }
+
+ ilist_node_impl *getNext() {
+ return static_cast<ilist_node_impl *>(node_base_type::getNext());
+ }
+
+ const ilist_node_impl *getPrev() const {
+ return static_cast<ilist_node_impl *>(node_base_type::getPrev());
+ }
+
+ const ilist_node_impl *getNext() const {
+ return static_cast<ilist_node_impl *>(node_base_type::getNext());
+ }
+
+ void setPrev(ilist_node_impl *N) { node_base_type::setPrev(N); }
+ void setNext(ilist_node_impl *N) { node_base_type::setNext(N); }
+
+public:
+ self_iterator getIterator() { return self_iterator(*this); }
+ const_self_iterator getIterator() const { return const_self_iterator(*this); }
+
+ reverse_self_iterator getReverseIterator() {
+ return reverse_self_iterator(*this);
+ }
+
+ const_reverse_self_iterator getReverseIterator() const {
+ return const_reverse_self_iterator(*this);
+ }
+
+ // Under-approximation, but always available for assertions.
+ using node_base_type::isKnownSentinel;
+
+ /// Check whether this is the sentinel node.
+ ///
+ /// This requires sentinel tracking to be explicitly enabled. Use the
+ /// ilist_sentinel_tracking<true> option to get this API.
+ bool isSentinel() const {
+ static_assert(OptionsT::is_sentinel_tracking_explicit,
+ "Use ilist_sentinel_tracking<true> to enable isSentinel()");
+ return node_base_type::isSentinel();
+ }
+};
+
+/// An intrusive list node.
+///
+/// A base class to enable membership in intrusive lists, including \a
+/// simple_ilist, \a iplist, and \a ilist. The first template parameter is the
+/// \a value_type for the list.
+///
+/// An ilist node can be configured with compile-time options to change
+/// behaviour and/or add API.
+///
+/// By default, an \a ilist_node knows whether it is the list sentinel (an
+/// instance of \a ilist_sentinel) if and only if
+/// LLVM_ENABLE_ABI_BREAKING_CHECKS. The function \a isKnownSentinel() always
+/// returns \c false tracking is off. Sentinel tracking steals a bit from the
+/// "prev" link, which adds a mask operation when decrementing an iterator, but
+/// enables bug-finding assertions in \a ilist_iterator.
+///
+/// To turn sentinel tracking on all the time, pass in the
+/// ilist_sentinel_tracking<true> template parameter. This also enables the \a
+/// isSentinel() function. The same option must be passed to the intrusive
+/// list. (ilist_sentinel_tracking<false> turns sentinel tracking off all the
+/// time.)
+///
+/// A type can inherit from ilist_node multiple times by passing in different
+/// \a ilist_tag options. This allows a single instance to be inserted into
+/// multiple lists simultaneously, where each list is given the same tag.
+///
+/// \example
+/// struct A {};
+/// struct B {};
+/// struct N : ilist_node<N, ilist_tag<A>>, ilist_node<N, ilist_tag<B>> {};
+///
+/// void foo() {
+/// simple_ilist<N, ilist_tag<A>> ListA;
+/// simple_ilist<N, ilist_tag<B>> ListB;
+/// N N1;
+/// ListA.push_back(N1);
+/// ListB.push_back(N1);
+/// }
+/// \endexample
+///
+/// See \a is_valid_option for steps on adding a new option.
+template <class T, class... Options>
+class ilist_node
+ : public ilist_node_impl<
+ typename ilist_detail::compute_node_options<T, Options...>::type> {
+ static_assert(ilist_detail::check_options<Options...>::value,
+ "Unrecognized node option!");
+};
+
+namespace ilist_detail {
+
+/// An access class for ilist_node private API.
+///
+/// This gives access to the private parts of ilist nodes. Nodes for an ilist
+/// should friend this class if they inherit privately from ilist_node.
+///
+/// Using this class outside of the ilist implementation is unsupported.
+struct NodeAccess {
+protected:
+ template <class OptionsT>
+ static ilist_node_impl<OptionsT> *getNodePtr(typename OptionsT::pointer N) {
+ return N;
+ }
+
+ template <class OptionsT>
+ static const ilist_node_impl<OptionsT> *
+ getNodePtr(typename OptionsT::const_pointer N) {
+ return N;
+ }
+
+ template <class OptionsT>
+ static typename OptionsT::pointer getValuePtr(ilist_node_impl<OptionsT> *N) {
+ return static_cast<typename OptionsT::pointer>(N);
+ }
+
+ template <class OptionsT>
+ static typename OptionsT::const_pointer
+ getValuePtr(const ilist_node_impl<OptionsT> *N) {
+ return static_cast<typename OptionsT::const_pointer>(N);
+ }
+
+ template <class OptionsT>
+ static ilist_node_impl<OptionsT> *getPrev(ilist_node_impl<OptionsT> &N) {
+ return N.getPrev();
+ }
+
+ template <class OptionsT>
+ static ilist_node_impl<OptionsT> *getNext(ilist_node_impl<OptionsT> &N) {
+ return N.getNext();
+ }
+
+ template <class OptionsT>
+ static const ilist_node_impl<OptionsT> *
+ getPrev(const ilist_node_impl<OptionsT> &N) {
+ return N.getPrev();
+ }
+
+ template <class OptionsT>
+ static const ilist_node_impl<OptionsT> *
+ getNext(const ilist_node_impl<OptionsT> &N) {
+ return N.getNext();
+ }
+};
+
+template <class OptionsT> struct SpecificNodeAccess : NodeAccess {
+protected:
+ using pointer = typename OptionsT::pointer;
+ using const_pointer = typename OptionsT::const_pointer;
+ using node_type = ilist_node_impl<OptionsT>;
+
+ static node_type *getNodePtr(pointer N) {
+ return NodeAccess::getNodePtr<OptionsT>(N);
+ }
+
+ static const node_type *getNodePtr(const_pointer N) {
+ return NodeAccess::getNodePtr<OptionsT>(N);
+ }
+
+ static pointer getValuePtr(node_type *N) {
+ return NodeAccess::getValuePtr<OptionsT>(N);
+ }
+
+ static const_pointer getValuePtr(const node_type *N) {
+ return NodeAccess::getValuePtr<OptionsT>(N);
+ }
+};
+
+} // end namespace ilist_detail
+
+template <class OptionsT>
+class ilist_sentinel : public ilist_node_impl<OptionsT> {
+public:
+ ilist_sentinel() {
+ this->initializeSentinel();
+ reset();
+ }
+
+ void reset() {
+ this->setPrev(this);
+ this->setNext(this);
+ }
+
+ bool empty() const { return this == this->getPrev(); }
+};
+
+/// An ilist node that can access its parent list.
+///
+/// Requires \c NodeTy to have \a getParent() to find the parent node, and the
+/// \c ParentTy to have \a getSublistAccess() to get a reference to the list.
+template <typename NodeTy, typename ParentTy, class... Options>
+class ilist_node_with_parent : public ilist_node<NodeTy, Options...> {
+protected:
+ ilist_node_with_parent() = default;
+
+private:
+ /// Forward to NodeTy::getParent().
+ ///
+ /// Note: do not use the name "getParent()". We want a compile error
+ /// (instead of recursion) when the subclass fails to implement \a
+ /// getParent().
+ const ParentTy *getNodeParent() const {
+ return static_cast<const NodeTy *>(this)->getParent();
+ }
+
+public:
+ /// @name Adjacent Node Accessors
+ /// @{
+ /// \brief Get the previous node, or \c nullptr for the list head.
+ NodeTy *getPrevNode() {
+ // Should be separated to a reused function, but then we couldn't use auto
+ // (and would need the type of the list).
+ const auto &List =
+ getNodeParent()->*(ParentTy::getSublistAccess((NodeTy *)nullptr));
+ return List.getPrevNode(*static_cast<NodeTy *>(this));
+ }
+
+ /// \brief Get the previous node, or \c nullptr for the list head.
+ const NodeTy *getPrevNode() const {
+ return const_cast<ilist_node_with_parent *>(this)->getPrevNode();
+ }
+
+ /// \brief Get the next node, or \c nullptr for the list tail.
+ NodeTy *getNextNode() {
+ // Should be separated to a reused function, but then we couldn't use auto
+ // (and would need the type of the list).
+ const auto &List =
+ getNodeParent()->*(ParentTy::getSublistAccess((NodeTy *)nullptr));
+ return List.getNextNode(*static_cast<NodeTy *>(this));
+ }
+
+ /// \brief Get the next node, or \c nullptr for the list tail.
+ const NodeTy *getNextNode() const {
+ return const_cast<ilist_node_with_parent *>(this)->getNextNode();
+ }
+ /// @}
+};
+
+} // end namespace llvm
+
+#endif // LLVM_ADT_ILIST_NODE_H
diff --git a/src/3rdparty/llvm/include/llvm/ADT/ilist_node_base.h b/src/3rdparty/llvm/include/llvm/ADT/ilist_node_base.h
new file mode 100644
index 0000000000..e5062ac4ea
--- /dev/null
+++ b/src/3rdparty/llvm/include/llvm/ADT/ilist_node_base.h
@@ -0,0 +1,53 @@
+//===- llvm/ADT/ilist_node_base.h - Intrusive List Node Base -----*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_ILIST_NODE_BASE_H
+#define LLVM_ADT_ILIST_NODE_BASE_H
+
+#include "llvm/ADT/PointerIntPair.h"
+
+namespace llvm {
+
+/// Base class for ilist nodes.
+///
+/// Optionally tracks whether this node is the sentinel.
+template <bool EnableSentinelTracking> class ilist_node_base;
+
+template <> class ilist_node_base<false> {
+ ilist_node_base *Prev = nullptr;
+ ilist_node_base *Next = nullptr;
+
+public:
+ void setPrev(ilist_node_base *Prev) { this->Prev = Prev; }
+ void setNext(ilist_node_base *Next) { this->Next = Next; }
+ ilist_node_base *getPrev() const { return Prev; }
+ ilist_node_base *getNext() const { return Next; }
+
+ bool isKnownSentinel() const { return false; }
+ void initializeSentinel() {}
+};
+
+template <> class ilist_node_base<true> {
+ PointerIntPair<ilist_node_base *, 1> PrevAndSentinel;
+ ilist_node_base *Next = nullptr;
+
+public:
+ void setPrev(ilist_node_base *Prev) { PrevAndSentinel.setPointer(Prev); }
+ void setNext(ilist_node_base *Next) { this->Next = Next; }
+ ilist_node_base *getPrev() const { return PrevAndSentinel.getPointer(); }
+ ilist_node_base *getNext() const { return Next; }
+
+ bool isSentinel() const { return PrevAndSentinel.getInt(); }
+ bool isKnownSentinel() const { return isSentinel(); }
+ void initializeSentinel() { PrevAndSentinel.setInt(true); }
+};
+
+} // end namespace llvm
+
+#endif // LLVM_ADT_ILIST_NODE_BASE_H
diff --git a/src/3rdparty/llvm/include/llvm/ADT/ilist_node_options.h b/src/3rdparty/llvm/include/llvm/ADT/ilist_node_options.h
new file mode 100644
index 0000000000..a09fdda31c
--- /dev/null
+++ b/src/3rdparty/llvm/include/llvm/ADT/ilist_node_options.h
@@ -0,0 +1,133 @@
+//===- llvm/ADT/ilist_node_options.h - ilist_node Options -------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_ILIST_NODE_OPTIONS_H
+#define LLVM_ADT_ILIST_NODE_OPTIONS_H
+
+//#include "llvm/Config/abi-breaking.h"
+//#include "llvm/Config/llvm-config.h"
+
+#include <type_traits>
+
+namespace llvm {
+
+template <bool EnableSentinelTracking> class ilist_node_base;
+template <bool EnableSentinelTracking> class ilist_base;
+
+/// Option to choose whether to track sentinels.
+///
+/// This option affects the ABI for the nodes. When not specified explicitly,
+/// the ABI depends on LLVM_ENABLE_ABI_BREAKING_CHECKS. Specify explicitly to
+/// enable \a ilist_node::isSentinel().
+template <bool EnableSentinelTracking> struct ilist_sentinel_tracking {};
+
+/// Option to specify a tag for the node type.
+///
+/// This option allows a single value type to be inserted in multiple lists
+/// simultaneously. See \a ilist_node for usage examples.
+template <class Tag> struct ilist_tag {};
+
+namespace ilist_detail {
+
+/// Helper trait for recording whether an option is specified explicitly.
+template <bool IsExplicit> struct explicitness {
+ static const bool is_explicit = IsExplicit;
+};
+typedef explicitness<true> is_explicit;
+typedef explicitness<false> is_implicit;
+
+/// Check whether an option is valid.
+///
+/// The steps for adding and enabling a new ilist option include:
+/// \li define the option, ilist_foo<Bar>, above;
+/// \li add new parameters for Bar to \a ilist_detail::node_options;
+/// \li add an extraction meta-function, ilist_detail::extract_foo;
+/// \li call extract_foo from \a ilist_detail::compute_node_options and pass it
+/// into \a ilist_detail::node_options; and
+/// \li specialize \c is_valid_option<ilist_foo<Bar>> to inherit from \c
+/// std::true_type to get static assertions passing in \a simple_ilist and \a
+/// ilist_node.
+template <class Option> struct is_valid_option : std::false_type {};
+
+/// Extract sentinel tracking option.
+///
+/// Look through \p Options for the \a ilist_sentinel_tracking option, with the
+/// default depending on LLVM_ENABLE_ABI_BREAKING_CHECKS.
+template <class... Options> struct extract_sentinel_tracking;
+template <bool EnableSentinelTracking, class... Options>
+struct extract_sentinel_tracking<
+ ilist_sentinel_tracking<EnableSentinelTracking>, Options...>
+ : std::integral_constant<bool, EnableSentinelTracking>, is_explicit {};
+template <class Option1, class... Options>
+struct extract_sentinel_tracking<Option1, Options...>
+ : extract_sentinel_tracking<Options...> {};
+#if LLVM_ENABLE_ABI_BREAKING_CHECKS
+template <> struct extract_sentinel_tracking<> : std::true_type, is_implicit {};
+#else
+template <>
+struct extract_sentinel_tracking<> : std::false_type, is_implicit {};
+#endif
+template <bool EnableSentinelTracking>
+struct is_valid_option<ilist_sentinel_tracking<EnableSentinelTracking>>
+ : std::true_type {};
+
+/// Extract custom tag option.
+///
+/// Look through \p Options for the \a ilist_tag option, pulling out the
+/// custom tag type, using void as a default.
+template <class... Options> struct extract_tag;
+template <class Tag, class... Options>
+struct extract_tag<ilist_tag<Tag>, Options...> {
+ typedef Tag type;
+};
+template <class Option1, class... Options>
+struct extract_tag<Option1, Options...> : extract_tag<Options...> {};
+template <> struct extract_tag<> { typedef void type; };
+template <class Tag> struct is_valid_option<ilist_tag<Tag>> : std::true_type {};
+
+/// Check whether options are valid.
+///
+/// The conjunction of \a is_valid_option on each individual option.
+template <class... Options> struct check_options;
+template <> struct check_options<> : std::true_type {};
+template <class Option1, class... Options>
+struct check_options<Option1, Options...>
+ : std::integral_constant<bool, is_valid_option<Option1>::value &&
+ check_options<Options...>::value> {};
+
+/// Traits for options for \a ilist_node.
+///
+/// This is usually computed via \a compute_node_options.
+template <class T, bool EnableSentinelTracking, bool IsSentinelTrackingExplicit,
+ class TagT>
+struct node_options {
+ typedef T value_type;
+ typedef T *pointer;
+ typedef T &reference;
+ typedef const T *const_pointer;
+ typedef const T &const_reference;
+
+ static const bool enable_sentinel_tracking = EnableSentinelTracking;
+ static const bool is_sentinel_tracking_explicit = IsSentinelTrackingExplicit;
+ typedef TagT tag;
+ typedef ilist_node_base<enable_sentinel_tracking> node_base_type;
+ typedef ilist_base<enable_sentinel_tracking> list_base_type;
+};
+
+template <class T, class... Options> struct compute_node_options {
+ typedef node_options<T, extract_sentinel_tracking<Options...>::value,
+ extract_sentinel_tracking<Options...>::is_explicit,
+ typename extract_tag<Options...>::type>
+ type;
+};
+
+} // end namespace ilist_detail
+} // end namespace llvm
+
+#endif // LLVM_ADT_ILIST_NODE_OPTIONS_H
diff --git a/src/3rdparty/llvm/include/llvm/ADT/iterator.h b/src/3rdparty/llvm/include/llvm/ADT/iterator.h
new file mode 100644
index 0000000000..711f8f2216
--- /dev/null
+++ b/src/3rdparty/llvm/include/llvm/ADT/iterator.h
@@ -0,0 +1,339 @@
+//===- iterator.h - Utilities for using and defining iterators --*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_ITERATOR_H
+#define LLVM_ADT_ITERATOR_H
+
+#include "llvm/ADT/iterator_range.h"
+#include <algorithm>
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+
+namespace llvm {
+
+/// \brief CRTP base class which implements the entire standard iterator facade
+/// in terms of a minimal subset of the interface.
+///
+/// Use this when it is reasonable to implement most of the iterator
+/// functionality in terms of a core subset. If you need special behavior or
+/// there are performance implications for this, you may want to override the
+/// relevant members instead.
+///
+/// Note, one abstraction that this does *not* provide is implementing
+/// subtraction in terms of addition by negating the difference. Negation isn't
+/// always information preserving, and I can see very reasonable iterator
+/// designs where this doesn't work well. It doesn't really force much added
+/// boilerplate anyways.
+///
+/// Another abstraction that this doesn't provide is implementing increment in
+/// terms of addition of one. These aren't equivalent for all iterator
+/// categories, and respecting that adds a lot of complexity for little gain.
+///
+/// Classes wishing to use `iterator_facade_base` should implement the following
+/// methods:
+///
+/// Forward Iterators:
+/// (All of the following methods)
+/// - DerivedT &operator=(const DerivedT &R);
+/// - bool operator==(const DerivedT &R) const;
+/// - const T &operator*() const;
+/// - T &operator*();
+/// - DerivedT &operator++();
+///
+/// Bidirectional Iterators:
+/// (All methods of forward iterators, plus the following)
+/// - DerivedT &operator--();
+///
+/// Random-access Iterators:
+/// (All methods of bidirectional iterators excluding the following)
+/// - DerivedT &operator++();
+/// - DerivedT &operator--();
+/// (and plus the following)
+/// - bool operator<(const DerivedT &RHS) const;
+/// - DifferenceTypeT operator-(const DerivedT &R) const;
+/// - DerivedT &operator+=(DifferenceTypeT N);
+/// - DerivedT &operator-=(DifferenceTypeT N);
+///
+template <typename DerivedT, typename IteratorCategoryT, typename T,
+ typename DifferenceTypeT = std::ptrdiff_t, typename PointerT = T *,
+ typename ReferenceT = T &>
+class iterator_facade_base
+ : public std::iterator<IteratorCategoryT, T, DifferenceTypeT, PointerT,
+ ReferenceT> {
+protected:
+ enum {
+ IsRandomAccess = std::is_base_of<std::random_access_iterator_tag,
+ IteratorCategoryT>::value,
+ IsBidirectional = std::is_base_of<std::bidirectional_iterator_tag,
+ IteratorCategoryT>::value,
+ };
+
+ /// A proxy object for computing a reference via indirecting a copy of an
+ /// iterator. This is used in APIs which need to produce a reference via
+ /// indirection but for which the iterator object might be a temporary. The
+ /// proxy preserves the iterator internally and exposes the indirected
+ /// reference via a conversion operator.
+ class ReferenceProxy {
+ friend iterator_facade_base;
+
+ DerivedT I;
+
+ ReferenceProxy(DerivedT I) : I(std::move(I)) {}
+
+ public:
+ operator ReferenceT() const { return *I; }
+ };
+
+public:
+ DerivedT operator+(DifferenceTypeT n) const {
+ static_assert(std::is_base_of<iterator_facade_base, DerivedT>::value,
+ "Must pass the derived type to this template!");
+ static_assert(
+ IsRandomAccess,
+ "The '+' operator is only defined for random access iterators.");
+ DerivedT tmp = *static_cast<const DerivedT *>(this);
+ tmp += n;
+ return tmp;
+ }
+ friend DerivedT operator+(DifferenceTypeT n, const DerivedT &i) {
+ static_assert(
+ IsRandomAccess,
+ "The '+' operator is only defined for random access iterators.");
+ return i + n;
+ }
+ DerivedT operator-(DifferenceTypeT n) const {
+ static_assert(
+ IsRandomAccess,
+ "The '-' operator is only defined for random access iterators.");
+ DerivedT tmp = *static_cast<const DerivedT *>(this);
+ tmp -= n;
+ return tmp;
+ }
+
+ DerivedT &operator++() {
+ static_assert(std::is_base_of<iterator_facade_base, DerivedT>::value,
+ "Must pass the derived type to this template!");
+ return static_cast<DerivedT *>(this)->operator+=(1);
+ }
+ DerivedT operator++(int) {
+ DerivedT tmp = *static_cast<DerivedT *>(this);
+ ++*static_cast<DerivedT *>(this);
+ return tmp;
+ }
+ DerivedT &operator--() {
+ static_assert(
+ IsBidirectional,
+ "The decrement operator is only defined for bidirectional iterators.");
+ return static_cast<DerivedT *>(this)->operator-=(1);
+ }
+ DerivedT operator--(int) {
+ static_assert(
+ IsBidirectional,
+ "The decrement operator is only defined for bidirectional iterators.");
+ DerivedT tmp = *static_cast<DerivedT *>(this);
+ --*static_cast<DerivedT *>(this);
+ return tmp;
+ }
+
+ bool operator!=(const DerivedT &RHS) const {
+ return !static_cast<const DerivedT *>(this)->operator==(RHS);
+ }
+
+ bool operator>(const DerivedT &RHS) const {
+ static_assert(
+ IsRandomAccess,
+ "Relational operators are only defined for random access iterators.");
+ return !static_cast<const DerivedT *>(this)->operator<(RHS) &&
+ !static_cast<const DerivedT *>(this)->operator==(RHS);
+ }
+ bool operator<=(const DerivedT &RHS) const {
+ static_assert(
+ IsRandomAccess,
+ "Relational operators are only defined for random access iterators.");
+ return !static_cast<const DerivedT *>(this)->operator>(RHS);
+ }
+ bool operator>=(const DerivedT &RHS) const {
+ static_assert(
+ IsRandomAccess,
+ "Relational operators are only defined for random access iterators.");
+ return !static_cast<const DerivedT *>(this)->operator<(RHS);
+ }
+
+ PointerT operator->() { return &static_cast<DerivedT *>(this)->operator*(); }
+ PointerT operator->() const {
+ return &static_cast<const DerivedT *>(this)->operator*();
+ }
+ ReferenceProxy operator[](DifferenceTypeT n) {
+ static_assert(IsRandomAccess,
+ "Subscripting is only defined for random access iterators.");
+ return ReferenceProxy(static_cast<DerivedT *>(this)->operator+(n));
+ }
+ ReferenceProxy operator[](DifferenceTypeT n) const {
+ static_assert(IsRandomAccess,
+ "Subscripting is only defined for random access iterators.");
+ return ReferenceProxy(static_cast<const DerivedT *>(this)->operator+(n));
+ }
+};
+
+/// \brief CRTP base class for adapting an iterator to a different type.
+///
+/// This class can be used through CRTP to adapt one iterator into another.
+/// Typically this is done through providing in the derived class a custom \c
+/// operator* implementation. Other methods can be overridden as well.
+template <
+ typename DerivedT, typename WrappedIteratorT,
+ typename IteratorCategoryT =
+ typename std::iterator_traits<WrappedIteratorT>::iterator_category,
+ typename T = typename std::iterator_traits<WrappedIteratorT>::value_type,
+ typename DifferenceTypeT =
+ typename std::iterator_traits<WrappedIteratorT>::difference_type,
+ typename PointerT = typename std::conditional<
+ std::is_same<T, typename std::iterator_traits<
+ WrappedIteratorT>::value_type>::value,
+ typename std::iterator_traits<WrappedIteratorT>::pointer, T *>::type,
+ typename ReferenceT = typename std::conditional<
+ std::is_same<T, typename std::iterator_traits<
+ WrappedIteratorT>::value_type>::value,
+ typename std::iterator_traits<WrappedIteratorT>::reference, T &>::type,
+ // Don't provide these, they are mostly to act as aliases below.
+ typename WrappedTraitsT = std::iterator_traits<WrappedIteratorT>>
+class iterator_adaptor_base
+ : public iterator_facade_base<DerivedT, IteratorCategoryT, T,
+ DifferenceTypeT, PointerT, ReferenceT> {
+ using BaseT = typename iterator_adaptor_base::iterator_facade_base;
+
+protected:
+ WrappedIteratorT I;
+
+ iterator_adaptor_base() = default;
+
+ explicit iterator_adaptor_base(WrappedIteratorT u) : I(std::move(u)) {
+ static_assert(std::is_base_of<iterator_adaptor_base, DerivedT>::value,
+ "Must pass the derived type to this template!");
+ }
+
+ const WrappedIteratorT &wrapped() const { return I; }
+
+public:
+ using difference_type = DifferenceTypeT;
+
+ DerivedT &operator+=(difference_type n) {
+ static_assert(
+ BaseT::IsRandomAccess,
+ "The '+=' operator is only defined for random access iterators.");
+ I += n;
+ return *static_cast<DerivedT *>(this);
+ }
+ DerivedT &operator-=(difference_type n) {
+ static_assert(
+ BaseT::IsRandomAccess,
+ "The '-=' operator is only defined for random access iterators.");
+ I -= n;
+ return *static_cast<DerivedT *>(this);
+ }
+ using BaseT::operator-;
+ difference_type operator-(const DerivedT &RHS) const {
+ static_assert(
+ BaseT::IsRandomAccess,
+ "The '-' operator is only defined for random access iterators.");
+ return I - RHS.I;
+ }
+
+ // We have to explicitly provide ++ and -- rather than letting the facade
+ // forward to += because WrappedIteratorT might not support +=.
+ using BaseT::operator++;
+ DerivedT &operator++() {
+ ++I;
+ return *static_cast<DerivedT *>(this);
+ }
+ using BaseT::operator--;
+ DerivedT &operator--() {
+ static_assert(
+ BaseT::IsBidirectional,
+ "The decrement operator is only defined for bidirectional iterators.");
+ --I;
+ return *static_cast<DerivedT *>(this);
+ }
+
+ bool operator==(const DerivedT &RHS) const { return I == RHS.I; }
+ bool operator<(const DerivedT &RHS) const {
+ static_assert(
+ BaseT::IsRandomAccess,
+ "Relational operators are only defined for random access iterators.");
+ return I < RHS.I;
+ }
+
+ ReferenceT operator*() const { return *I; }
+};
+
+/// \brief An iterator type that allows iterating over the pointees via some
+/// other iterator.
+///
+/// The typical usage of this is to expose a type that iterates over Ts, but
+/// which is implemented with some iterator over T*s:
+///
+/// \code
+/// using iterator = pointee_iterator<SmallVectorImpl<T *>::iterator>;
+/// \endcode
+template <typename WrappedIteratorT,
+ typename T = typename std::remove_reference<
+ decltype(**std::declval<WrappedIteratorT>())>::type>
+struct pointee_iterator
+ : iterator_adaptor_base<
+ pointee_iterator<WrappedIteratorT>, WrappedIteratorT,
+ typename std::iterator_traits<WrappedIteratorT>::iterator_category,
+ T> {
+ pointee_iterator() = default;
+ template <typename U>
+ pointee_iterator(U &&u)
+ : pointee_iterator::iterator_adaptor_base(std::forward<U &&>(u)) {}
+
+ T &operator*() const { return **this->I; }
+};
+
+template <typename RangeT, typename WrappedIteratorT =
+ decltype(std::begin(std::declval<RangeT>()))>
+iterator_range<pointee_iterator<WrappedIteratorT>>
+make_pointee_range(RangeT &&Range) {
+ using PointeeIteratorT = pointee_iterator<WrappedIteratorT>;
+ return make_range(PointeeIteratorT(std::begin(std::forward<RangeT>(Range))),
+ PointeeIteratorT(std::end(std::forward<RangeT>(Range))));
+}
+
+template <typename WrappedIteratorT,
+ typename T = decltype(&*std::declval<WrappedIteratorT>())>
+class pointer_iterator
+ : public iterator_adaptor_base<pointer_iterator<WrappedIteratorT>,
+ WrappedIteratorT, T> {
+ mutable T Ptr;
+
+public:
+ pointer_iterator() = default;
+
+ explicit pointer_iterator(WrappedIteratorT u)
+ : pointer_iterator::iterator_adaptor_base(std::move(u)) {}
+
+ T &operator*() { return Ptr = &*this->I; }
+ const T &operator*() const { return Ptr = &*this->I; }
+};
+
+template <typename RangeT, typename WrappedIteratorT =
+ decltype(std::begin(std::declval<RangeT>()))>
+iterator_range<pointer_iterator<WrappedIteratorT>>
+make_pointer_range(RangeT &&Range) {
+ using PointerIteratorT = pointer_iterator<WrappedIteratorT>;
+ return make_range(PointerIteratorT(std::begin(std::forward<RangeT>(Range))),
+ PointerIteratorT(std::end(std::forward<RangeT>(Range))));
+}
+
+} // end namespace llvm
+
+#endif // LLVM_ADT_ITERATOR_H
diff --git a/src/3rdparty/llvm/include/llvm/ADT/iterator_range.h b/src/3rdparty/llvm/include/llvm/ADT/iterator_range.h
new file mode 100644
index 0000000000..3cbf6198eb
--- /dev/null
+++ b/src/3rdparty/llvm/include/llvm/ADT/iterator_range.h
@@ -0,0 +1,68 @@
+//===- iterator_range.h - A range adaptor for iterators ---------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This provides a very simple, boring adaptor for a begin and end iterator
+/// into a range type. This should be used to build range views that work well
+/// with range based for loops and range based constructors.
+///
+/// Note that code here follows more standards-based coding conventions as it
+/// is mirroring proposed interfaces for standardization.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_ITERATOR_RANGE_H
+#define LLVM_ADT_ITERATOR_RANGE_H
+
+#include <iterator>
+#include <utility>
+
+namespace llvm {
+
+/// \brief A range adaptor for a pair of iterators.
+///
+/// This just wraps two iterators into a range-compatible interface. Nothing
+/// fancy at all.
+template <typename IteratorT>
+class iterator_range {
+ IteratorT begin_iterator, end_iterator;
+
+public:
+ //TODO: Add SFINAE to test that the Container's iterators match the range's
+ // iterators.
+ template <typename Container>
+ iterator_range(Container &&c)
+ //TODO: Consider ADL/non-member begin/end calls.
+ : begin_iterator(c.begin()), end_iterator(c.end()) {}
+ iterator_range(IteratorT begin_iterator, IteratorT end_iterator)
+ : begin_iterator(std::move(begin_iterator)),
+ end_iterator(std::move(end_iterator)) {}
+
+ IteratorT begin() const { return begin_iterator; }
+ IteratorT end() const { return end_iterator; }
+};
+
+/// \brief Convenience function for iterating over sub-ranges.
+///
+/// This provides a bit of syntactic sugar to make using sub-ranges
+/// in for loops a bit easier. Analogous to std::make_pair().
+template <class T> iterator_range<T> make_range(T x, T y) {
+ return iterator_range<T>(std::move(x), std::move(y));
+}
+
+template <typename T> iterator_range<T> make_range(std::pair<T, T> p) {
+ return iterator_range<T>(std::move(p.first), std::move(p.second));
+}
+
+template<typename T>
+iterator_range<decltype(begin(std::declval<T>()))> drop_begin(T &&t, int n) {
+ return make_range(std::next(begin(t), n), end(t));
+}
+}
+
+#endif
diff --git a/src/3rdparty/llvm/include/llvm/ADT/simple_ilist.h b/src/3rdparty/llvm/include/llvm/ADT/simple_ilist.h
new file mode 100644
index 0000000000..4c7598a1ac
--- /dev/null
+++ b/src/3rdparty/llvm/include/llvm/ADT/simple_ilist.h
@@ -0,0 +1,315 @@
+//===- llvm/ADT/simple_ilist.h - Simple Intrusive List ----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_SIMPLE_ILIST_H
+#define LLVM_ADT_SIMPLE_ILIST_H
+
+#include "llvm/ADT/ilist_base.h"
+#include "llvm/ADT/ilist_iterator.h"
+#include "llvm/ADT/ilist_node.h"
+#include "llvm/ADT/ilist_node_options.h"
+#include "llvm/Support/Compiler.h"
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <utility>
+
+namespace llvm {
+
+/// A simple intrusive list implementation.
+///
+/// This is a simple intrusive list for a \c T that inherits from \c
+/// ilist_node<T>. The list never takes ownership of anything inserted in it.
+///
+/// Unlike \a iplist<T> and \a ilist<T>, \a simple_ilist<T> never allocates or
+/// deletes values, and has no callback traits.
+///
+/// The API for adding nodes include \a push_front(), \a push_back(), and \a
+/// insert(). These all take values by reference (not by pointer), except for
+/// the range version of \a insert().
+///
+/// There are three sets of API for discarding nodes from the list: \a
+/// remove(), which takes a reference to the node to remove, \a erase(), which
+/// takes an iterator or iterator range and returns the next one, and \a
+/// clear(), which empties out the container. All three are constant time
+/// operations. None of these deletes any nodes; in particular, if there is a
+/// single node in the list, then these have identical semantics:
+/// \li \c L.remove(L.front());
+/// \li \c L.erase(L.begin());
+/// \li \c L.clear();
+///
+/// As a convenience for callers, there are parallel APIs that take a \c
+/// Disposer (such as \c std::default_delete<T>): \a removeAndDispose(), \a
+/// eraseAndDispose(), and \a clearAndDispose(). These have different names
+/// because the extra semantic is otherwise non-obvious. They are equivalent
+/// to calling \a std::for_each() on the range to be discarded.
+///
+/// The currently available \p Options customize the nodes in the list. The
+/// same options must be specified in the \a ilist_node instantation for
+/// compatibility (although the order is irrelevant).
+/// \li Use \a ilist_tag to designate which ilist_node for a given \p T this
+/// list should use. This is useful if a type \p T is part of multiple,
+/// independent lists simultaneously.
+/// \li Use \a ilist_sentinel_tracking to always (or never) track whether a
+/// node is a sentinel. Specifying \c true enables the \a
+/// ilist_node::isSentinel() API. Unlike \a ilist_node::isKnownSentinel(),
+/// which is only appropriate for assertions, \a ilist_node::isSentinel() is
+/// appropriate for real logic.
+///
+/// Here are examples of \p Options usage:
+/// \li \c simple_ilist<T> gives the defaults. \li \c
+/// simple_ilist<T,ilist_sentinel_tracking<true>> enables the \a
+/// ilist_node::isSentinel() API.
+/// \li \c simple_ilist<T,ilist_tag<A>,ilist_sentinel_tracking<false>>
+/// specifies a tag of A and that tracking should be off (even when
+/// LLVM_ENABLE_ABI_BREAKING_CHECKS are enabled).
+/// \li \c simple_ilist<T,ilist_sentinel_tracking<false>,ilist_tag<A>> is
+/// equivalent to the last.
+///
+/// See \a is_valid_option for steps on adding a new option.
+template <typename T, class... Options>
+class simple_ilist
+ : ilist_detail::compute_node_options<T, Options...>::type::list_base_type,
+ ilist_detail::SpecificNodeAccess<
+ typename ilist_detail::compute_node_options<T, Options...>::type> {
+ static_assert(ilist_detail::check_options<Options...>::value,
+ "Unrecognized node option!");
+ using OptionsT =
+ typename ilist_detail::compute_node_options<T, Options...>::type;
+ using list_base_type = typename OptionsT::list_base_type;
+ ilist_sentinel<OptionsT> Sentinel;
+
+public:
+ using value_type = typename OptionsT::value_type;
+ using pointer = typename OptionsT::pointer;
+ using reference = typename OptionsT::reference;
+ using const_pointer = typename OptionsT::const_pointer;
+ using const_reference = typename OptionsT::const_reference;
+ using iterator = ilist_iterator<OptionsT, false, false>;
+ using const_iterator = ilist_iterator<OptionsT, false, true>;
+ using reverse_iterator = ilist_iterator<OptionsT, true, false>;
+ using const_reverse_iterator = ilist_iterator<OptionsT, true, true>;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+
+ simple_ilist() = default;
+ ~simple_ilist() = default;
+
+ // No copy constructors.
+ simple_ilist(const simple_ilist &) = delete;
+ simple_ilist &operator=(const simple_ilist &) = delete;
+
+ // Move constructors.
+ simple_ilist(simple_ilist &&X) { splice(end(), X); }
+ simple_ilist &operator=(simple_ilist &&X) {
+ clear();
+ splice(end(), X);
+ return *this;
+ }
+
+ iterator begin() { return ++iterator(Sentinel); }
+ const_iterator begin() const { return ++const_iterator(Sentinel); }
+ iterator end() { return iterator(Sentinel); }
+ const_iterator end() const { return const_iterator(Sentinel); }
+ reverse_iterator rbegin() { return ++reverse_iterator(Sentinel); }
+ const_reverse_iterator rbegin() const {
+ return ++const_reverse_iterator(Sentinel);
+ }
+ reverse_iterator rend() { return reverse_iterator(Sentinel); }
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(Sentinel);
+ }
+
+ /// Check if the list is empty in constant time.
+ LLVM_NODISCARD bool empty() const { return Sentinel.empty(); }
+
+ /// Calculate the size of the list in linear time.
+ LLVM_NODISCARD size_type size() const {
+ return std::distance(begin(), end());
+ }
+
+ reference front() { return *begin(); }
+ const_reference front() const { return *begin(); }
+ reference back() { return *rbegin(); }
+ const_reference back() const { return *rbegin(); }
+
+ /// Insert a node at the front; never copies.
+ void push_front(reference Node) { insert(begin(), Node); }
+
+ /// Insert a node at the back; never copies.
+ void push_back(reference Node) { insert(end(), Node); }
+
+ /// Remove the node at the front; never deletes.
+ void pop_front() { erase(begin()); }
+
+ /// Remove the node at the back; never deletes.
+ void pop_back() { erase(--end()); }
+
+ /// Swap with another list in place using std::swap.
+ void swap(simple_ilist &X) { std::swap(*this, X); }
+
+ /// Insert a node by reference; never copies.
+ iterator insert(iterator I, reference Node) {
+ list_base_type::insertBefore(*I.getNodePtr(), *this->getNodePtr(&Node));
+ return iterator(&Node);
+ }
+
+ /// Insert a range of nodes; never copies.
+ template <class Iterator>
+ void insert(iterator I, Iterator First, Iterator Last) {
+ for (; First != Last; ++First)
+ insert(I, *First);
+ }
+
+ /// Clone another list.
+ template <class Cloner, class Disposer>
+ void cloneFrom(const simple_ilist &L2, Cloner clone, Disposer dispose) {
+ clearAndDispose(dispose);
+ for (const_reference V : L2)
+ push_back(*clone(V));
+ }
+
+ /// Remove a node by reference; never deletes.
+ ///
+ /// \see \a erase() for removing by iterator.
+ /// \see \a removeAndDispose() if the node should be deleted.
+ void remove(reference N) { list_base_type::remove(*this->getNodePtr(&N)); }
+
+ /// Remove a node by reference and dispose of it.
+ template <class Disposer>
+ void removeAndDispose(reference N, Disposer dispose) {
+ remove(N);
+ dispose(&N);
+ }
+
+ /// Remove a node by iterator; never deletes.
+ ///
+ /// \see \a remove() for removing by reference.
+ /// \see \a eraseAndDispose() it the node should be deleted.
+ iterator erase(iterator I) {
+ assert(I != end() && "Cannot remove end of list!");
+ remove(*I++);
+ return I;
+ }
+
+ /// Remove a range of nodes; never deletes.
+ ///
+ /// \see \a eraseAndDispose() if the nodes should be deleted.
+ iterator erase(iterator First, iterator Last) {
+ list_base_type::removeRange(*First.getNodePtr(), *Last.getNodePtr());
+ return Last;
+ }
+
+ /// Remove a node by iterator and dispose of it.
+ template <class Disposer>
+ iterator eraseAndDispose(iterator I, Disposer dispose) {
+ auto Next = std::next(I);
+ erase(I);
+ dispose(&*I);
+ return Next;
+ }
+
+ /// Remove a range of nodes and dispose of them.
+ template <class Disposer>
+ iterator eraseAndDispose(iterator First, iterator Last, Disposer dispose) {
+ while (First != Last)
+ First = eraseAndDispose(First, dispose);
+ return Last;
+ }
+
+ /// Clear the list; never deletes.
+ ///
+ /// \see \a clearAndDispose() if the nodes should be deleted.
+ void clear() { Sentinel.reset(); }
+
+ /// Clear the list and dispose of the nodes.
+ template <class Disposer> void clearAndDispose(Disposer dispose) {
+ eraseAndDispose(begin(), end(), dispose);
+ }
+
+ /// Splice in another list.
+ void splice(iterator I, simple_ilist &L2) {
+ splice(I, L2, L2.begin(), L2.end());
+ }
+
+ /// Splice in a node from another list.
+ void splice(iterator I, simple_ilist &L2, iterator Node) {
+ splice(I, L2, Node, std::next(Node));
+ }
+
+ /// Splice in a range of nodes from another list.
+ void splice(iterator I, simple_ilist &, iterator First, iterator Last) {
+ list_base_type::transferBefore(*I.getNodePtr(), *First.getNodePtr(),
+ *Last.getNodePtr());
+ }
+
+ /// Merge in another list.
+ ///
+ /// \pre \c this and \p RHS are sorted.
+ ///@{
+ void merge(simple_ilist &RHS) { merge(RHS, std::less<T>()); }
+ template <class Compare> void merge(simple_ilist &RHS, Compare comp);
+ ///@}
+
+ /// Sort the list.
+ ///@{
+ void sort() { sort(std::less<T>()); }
+ template <class Compare> void sort(Compare comp);
+ ///@}
+};
+
+template <class T, class... Options>
+template <class Compare>
+void simple_ilist<T, Options...>::merge(simple_ilist &RHS, Compare comp) {
+ if (this == &RHS || RHS.empty())
+ return;
+ iterator LI = begin(), LE = end();
+ iterator RI = RHS.begin(), RE = RHS.end();
+ while (LI != LE) {
+ if (comp(*RI, *LI)) {
+ // Transfer a run of at least size 1 from RHS to LHS.
+ iterator RunStart = RI++;
+ RI = std::find_if(RI, RE, [&](reference RV) { return !comp(RV, *LI); });
+ splice(LI, RHS, RunStart, RI);
+ if (RI == RE)
+ return;
+ }
+ ++LI;
+ }
+ // Transfer the remaining RHS nodes once LHS is finished.
+ splice(LE, RHS, RI, RE);
+}
+
+template <class T, class... Options>
+template <class Compare>
+void simple_ilist<T, Options...>::sort(Compare comp) {
+ // Vacuously sorted.
+ if (empty() || std::next(begin()) == end())
+ return;
+
+ // Split the list in the middle.
+ iterator Center = begin(), End = begin();
+ while (End != end() && ++End != end()) {
+ ++Center;
+ ++End;
+ }
+ simple_ilist RHS;
+ RHS.splice(RHS.end(), *this, Center, end());
+
+ // Sort the sublists and merge back together.
+ sort(comp);
+ RHS.sort(comp);
+ merge(RHS, comp);
+}
+
+} // end namespace llvm
+
+#endif // LLVM_ADT_SIMPLE_ILIST_H
diff --git a/src/3rdparty/llvm/include/llvm/Demangle/Compiler.h b/src/3rdparty/llvm/include/llvm/Demangle/Compiler.h
new file mode 100644
index 0000000000..963482c64f
--- /dev/null
+++ b/src/3rdparty/llvm/include/llvm/Demangle/Compiler.h
@@ -0,0 +1,524 @@
+//===-- llvm/Demangle/Compiler.h - Compiler abstraction support -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines several macros, based on the current compiler. This allows
+// use of compiler-specific features in a way that remains portable.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_COMPILER_H
+#define LLVM_SUPPORT_COMPILER_H
+
+//#include "llvm/Config/llvm-config.h"
+
+#if defined(_MSC_VER)
+#include <sal.h>
+#endif
+
+#ifndef __has_feature
+# define __has_feature(x) 0
+#endif
+
+#ifndef __has_extension
+# define __has_extension(x) 0
+#endif
+
+#ifndef __has_attribute
+# define __has_attribute(x) 0
+#endif
+
+#ifndef __has_cpp_attribute
+# define __has_cpp_attribute(x) 0
+#endif
+
+#ifndef __has_builtin
+# define __has_builtin(x) 0
+#endif
+
+/// \macro LLVM_GNUC_PREREQ
+/// \brief Extend the default __GNUC_PREREQ even if glibc's features.h isn't
+/// available.
+#ifndef LLVM_GNUC_PREREQ
+# if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
+# define LLVM_GNUC_PREREQ(maj, min, patch) \
+ ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= \
+ ((maj) << 20) + ((min) << 10) + (patch))
+# elif defined(__GNUC__) && defined(__GNUC_MINOR__)
+# define LLVM_GNUC_PREREQ(maj, min, patch) \
+ ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10))
+# else
+# define LLVM_GNUC_PREREQ(maj, min, patch) 0
+# endif
+#endif
+
+/// \macro LLVM_MSC_PREREQ
+/// \brief Is the compiler MSVC of at least the specified version?
+/// The common \param version values to check for are:
+/// * 1900: Microsoft Visual Studio 2015 / 14.0
+#ifdef _MSC_VER
+#define LLVM_MSC_PREREQ(version) (_MSC_VER >= (version))
+
+// We require at least MSVC 2015.
+#if !LLVM_MSC_PREREQ(1900)
+#error LLVM requires at least MSVC 2015.
+#endif
+
+#else
+#define LLVM_MSC_PREREQ(version) 0
+#endif
+
+/// \brief Does the compiler support ref-qualifiers for *this?
+///
+/// Sadly, this is separate from just rvalue reference support because GCC
+/// and MSVC implemented this later than everything else.
+#if __has_feature(cxx_rvalue_references) || LLVM_GNUC_PREREQ(4, 8, 1)
+#define LLVM_HAS_RVALUE_REFERENCE_THIS 1
+#else
+#define LLVM_HAS_RVALUE_REFERENCE_THIS 0
+#endif
+
+/// Expands to '&' if ref-qualifiers for *this are supported.
+///
+/// This can be used to provide lvalue/rvalue overrides of member functions.
+/// The rvalue override should be guarded by LLVM_HAS_RVALUE_REFERENCE_THIS
+#if LLVM_HAS_RVALUE_REFERENCE_THIS
+#define LLVM_LVALUE_FUNCTION &
+#else
+#define LLVM_LVALUE_FUNCTION
+#endif
+
+/// LLVM_LIBRARY_VISIBILITY - If a class marked with this attribute is linked
+/// into a shared library, then the class should be private to the library and
+/// not accessible from outside it. Can also be used to mark variables and
+/// functions, making them private to any shared library they are linked into.
+/// On PE/COFF targets, library visibility is the default, so this isn't needed.
+#if (__has_attribute(visibility) || LLVM_GNUC_PREREQ(4, 0, 0)) && \
+ !defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(LLVM_ON_WIN32)
+#define LLVM_LIBRARY_VISIBILITY __attribute__ ((visibility("hidden")))
+#else
+#define LLVM_LIBRARY_VISIBILITY
+#endif
+
+#if defined(__GNUC__)
+#define LLVM_PREFETCH(addr, rw, locality) __builtin_prefetch(addr, rw, locality)
+#else
+#define LLVM_PREFETCH(addr, rw, locality)
+#endif
+
+#if __has_attribute(used) || LLVM_GNUC_PREREQ(3, 1, 0)
+#define LLVM_ATTRIBUTE_USED __attribute__((__used__))
+#else
+#define LLVM_ATTRIBUTE_USED
+#endif
+
+/// LLVM_NODISCARD - Warn if a type or return value is discarded.
+#if __cplusplus > 201402L && __has_cpp_attribute(nodiscard)
+#define LLVM_NODISCARD [[nodiscard]]
+#elif !__cplusplus
+// Workaround for llvm.org/PR23435, since clang 3.6 and below emit a spurious
+// error when __has_cpp_attribute is given a scoped attribute in C mode.
+#define LLVM_NODISCARD
+#elif __has_cpp_attribute(clang::warn_unused_result)
+#define LLVM_NODISCARD [[clang::warn_unused_result]]
+#else
+#define LLVM_NODISCARD
+#endif
+
+// Some compilers warn about unused functions. When a function is sometimes
+// used or not depending on build settings (e.g. a function only called from
+// within "assert"), this attribute can be used to suppress such warnings.
+//
+// However, it shouldn't be used for unused *variables*, as those have a much
+// more portable solution:
+// (void)unused_var_name;
+// Prefer cast-to-void wherever it is sufficient.
+#if __has_attribute(unused) || LLVM_GNUC_PREREQ(3, 1, 0)
+#define LLVM_ATTRIBUTE_UNUSED __attribute__((__unused__))
+#else
+#define LLVM_ATTRIBUTE_UNUSED
+#endif
+
+// FIXME: Provide this for PE/COFF targets.
+#if (__has_attribute(weak) || LLVM_GNUC_PREREQ(4, 0, 0)) && \
+ (!defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(LLVM_ON_WIN32))
+#define LLVM_ATTRIBUTE_WEAK __attribute__((__weak__))
+#else
+#define LLVM_ATTRIBUTE_WEAK
+#endif
+
+// Prior to clang 3.2, clang did not accept any spelling of
+// __has_attribute(const), so assume it is supported.
+#if defined(__clang__) || defined(__GNUC__)
+// aka 'CONST' but following LLVM Conventions.
+#define LLVM_READNONE __attribute__((__const__))
+#else
+#define LLVM_READNONE
+#endif
+
+#if __has_attribute(pure) || defined(__GNUC__)
+// aka 'PURE' but following LLVM Conventions.
+#define LLVM_READONLY __attribute__((__pure__))
+#else
+#define LLVM_READONLY
+#endif
+
+#if __has_builtin(__builtin_expect) || LLVM_GNUC_PREREQ(4, 0, 0)
+#define LLVM_LIKELY(EXPR) __builtin_expect((bool)(EXPR), true)
+#define LLVM_UNLIKELY(EXPR) __builtin_expect((bool)(EXPR), false)
+#else
+#define LLVM_LIKELY(EXPR) (EXPR)
+#define LLVM_UNLIKELY(EXPR) (EXPR)
+#endif
+
+/// LLVM_ATTRIBUTE_NOINLINE - On compilers where we have a directive to do so,
+/// mark a method "not for inlining".
+#if __has_attribute(noinline) || LLVM_GNUC_PREREQ(3, 4, 0)
+#define LLVM_ATTRIBUTE_NOINLINE __attribute__((noinline))
+#elif defined(_MSC_VER)
+#define LLVM_ATTRIBUTE_NOINLINE __declspec(noinline)
+#else
+#define LLVM_ATTRIBUTE_NOINLINE
+#endif
+
+/// LLVM_ATTRIBUTE_ALWAYS_INLINE - On compilers where we have a directive to do
+/// so, mark a method "always inline" because it is performance sensitive. GCC
+/// 3.4 supported this but is buggy in various cases and produces unimplemented
+/// errors, just use it in GCC 4.0 and later.
+#if __has_attribute(always_inline) || LLVM_GNUC_PREREQ(4, 0, 0)
+#define LLVM_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline))
+#elif defined(_MSC_VER)
+#define LLVM_ATTRIBUTE_ALWAYS_INLINE __forceinline
+#else
+#define LLVM_ATTRIBUTE_ALWAYS_INLINE
+#endif
+
+#ifdef __GNUC__
+#define LLVM_ATTRIBUTE_NORETURN __attribute__((noreturn))
+#elif defined(_MSC_VER)
+#define LLVM_ATTRIBUTE_NORETURN __declspec(noreturn)
+#else
+#define LLVM_ATTRIBUTE_NORETURN
+#endif
+
+#if __has_attribute(returns_nonnull) || LLVM_GNUC_PREREQ(4, 9, 0)
+#define LLVM_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull))
+#elif defined(_MSC_VER)
+#define LLVM_ATTRIBUTE_RETURNS_NONNULL _Ret_notnull_
+#else
+#define LLVM_ATTRIBUTE_RETURNS_NONNULL
+#endif
+
+/// \macro LLVM_ATTRIBUTE_RETURNS_NOALIAS Used to mark a function as returning a
+/// pointer that does not alias any other valid pointer.
+#ifdef __GNUC__
+#define LLVM_ATTRIBUTE_RETURNS_NOALIAS __attribute__((__malloc__))
+#elif defined(_MSC_VER)
+#define LLVM_ATTRIBUTE_RETURNS_NOALIAS __declspec(restrict)
+#else
+#define LLVM_ATTRIBUTE_RETURNS_NOALIAS
+#endif
+
+/// LLVM_FALLTHROUGH - Mark fallthrough cases in switch statements.
+#if __cplusplus > 201402L && __has_cpp_attribute(fallthrough)
+#define LLVM_FALLTHROUGH [[fallthrough]]
+#elif __has_cpp_attribute(gnu::fallthrough)
+#define LLVM_FALLTHROUGH [[gnu::fallthrough]]
+#elif !__cplusplus
+// Workaround for llvm.org/PR23435, since clang 3.6 and below emit a spurious
+// error when __has_cpp_attribute is given a scoped attribute in C mode.
+#define LLVM_FALLTHROUGH
+#elif __has_cpp_attribute(clang::fallthrough)
+#define LLVM_FALLTHROUGH [[clang::fallthrough]]
+#else
+#define LLVM_FALLTHROUGH
+#endif
+
+/// LLVM_EXTENSION - Support compilers where we have a keyword to suppress
+/// pedantic diagnostics.
+#ifdef __GNUC__
+#define LLVM_EXTENSION __extension__
+#else
+#define LLVM_EXTENSION
+#endif
+
+// LLVM_ATTRIBUTE_DEPRECATED(decl, "message")
+#if __has_feature(attribute_deprecated_with_message)
+# define LLVM_ATTRIBUTE_DEPRECATED(decl, message) \
+ decl __attribute__((deprecated(message)))
+#elif defined(__GNUC__)
+# define LLVM_ATTRIBUTE_DEPRECATED(decl, message) \
+ decl __attribute__((deprecated))
+#elif defined(_MSC_VER)
+# define LLVM_ATTRIBUTE_DEPRECATED(decl, message) \
+ __declspec(deprecated(message)) decl
+#else
+# define LLVM_ATTRIBUTE_DEPRECATED(decl, message) \
+ decl
+#endif
+
+/// LLVM_BUILTIN_UNREACHABLE - On compilers which support it, expands
+/// to an expression which states that it is undefined behavior for the
+/// compiler to reach this point. Otherwise is not defined.
+#if __has_builtin(__builtin_unreachable) || LLVM_GNUC_PREREQ(4, 5, 0)
+# define LLVM_BUILTIN_UNREACHABLE __builtin_unreachable()
+#elif defined(_MSC_VER)
+# define LLVM_BUILTIN_UNREACHABLE __assume(false)
+#endif
+
+/// LLVM_BUILTIN_TRAP - On compilers which support it, expands to an expression
+/// which causes the program to exit abnormally.
+#if __has_builtin(__builtin_trap) || LLVM_GNUC_PREREQ(4, 3, 0)
+# define LLVM_BUILTIN_TRAP __builtin_trap()
+#elif defined(_MSC_VER)
+// The __debugbreak intrinsic is supported by MSVC, does not require forward
+// declarations involving platform-specific typedefs (unlike RaiseException),
+// results in a call to vectored exception handlers, and encodes to a short
+// instruction that still causes the trapping behavior we want.
+# define LLVM_BUILTIN_TRAP __debugbreak()
+#else
+# define LLVM_BUILTIN_TRAP *(volatile int*)0x11 = 0
+#endif
+
+/// LLVM_BUILTIN_DEBUGTRAP - On compilers which support it, expands to
+/// an expression which causes the program to break while running
+/// under a debugger.
+#if __has_builtin(__builtin_debugtrap)
+# define LLVM_BUILTIN_DEBUGTRAP __builtin_debugtrap()
+#elif defined(_MSC_VER)
+// The __debugbreak intrinsic is supported by MSVC and breaks while
+// running under the debugger, and also supports invoking a debugger
+// when the OS is configured appropriately.
+# define LLVM_BUILTIN_DEBUGTRAP __debugbreak()
+#else
+// Just continue execution when built with compilers that have no
+// support. This is a debugging aid and not intended to force the
+// program to abort if encountered.
+# define LLVM_BUILTIN_DEBUGTRAP
+#endif
+
+/// \macro LLVM_ASSUME_ALIGNED
+/// \brief Returns a pointer with an assumed alignment.
+#if __has_builtin(__builtin_assume_aligned) || LLVM_GNUC_PREREQ(4, 7, 0)
+# define LLVM_ASSUME_ALIGNED(p, a) __builtin_assume_aligned(p, a)
+#elif defined(LLVM_BUILTIN_UNREACHABLE)
+// As of today, clang does not support __builtin_assume_aligned.
+# define LLVM_ASSUME_ALIGNED(p, a) \
+ (((uintptr_t(p) % (a)) == 0) ? (p) : (LLVM_BUILTIN_UNREACHABLE, (p)))
+#else
+# define LLVM_ASSUME_ALIGNED(p, a) (p)
+#endif
+
+/// \macro LLVM_ALIGNAS
+/// \brief Used to specify a minimum alignment for a structure or variable.
+#if __GNUC__ && !__has_feature(cxx_alignas) && !LLVM_GNUC_PREREQ(4, 8, 1)
+# define LLVM_ALIGNAS(x) __attribute__((aligned(x)))
+#else
+# define LLVM_ALIGNAS(x) alignas(x)
+#endif
+
+/// \macro LLVM_PACKED
+/// \brief Used to specify a packed structure.
+/// LLVM_PACKED(
+/// struct A {
+/// int i;
+/// int j;
+/// int k;
+/// long long l;
+/// });
+///
+/// LLVM_PACKED_START
+/// struct B {
+/// int i;
+/// int j;
+/// int k;
+/// long long l;
+/// };
+/// LLVM_PACKED_END
+#ifdef _MSC_VER
+# define LLVM_PACKED(d) __pragma(pack(push, 1)) d __pragma(pack(pop))
+# define LLVM_PACKED_START __pragma(pack(push, 1))
+# define LLVM_PACKED_END __pragma(pack(pop))
+#else
+# define LLVM_PACKED(d) d __attribute__((packed))
+# define LLVM_PACKED_START _Pragma("pack(push, 1)")
+# define LLVM_PACKED_END _Pragma("pack(pop)")
+#endif
+
+/// \macro LLVM_PTR_SIZE
+/// \brief A constant integer equivalent to the value of sizeof(void*).
+/// Generally used in combination with LLVM_ALIGNAS or when doing computation in
+/// the preprocessor.
+#ifdef __SIZEOF_POINTER__
+# define LLVM_PTR_SIZE __SIZEOF_POINTER__
+#elif defined(_WIN64)
+# define LLVM_PTR_SIZE 8
+#elif defined(_WIN32)
+# define LLVM_PTR_SIZE 4
+#elif defined(_MSC_VER)
+# error "could not determine LLVM_PTR_SIZE as a constant int for MSVC"
+#else
+# define LLVM_PTR_SIZE sizeof(void *)
+#endif
+
+/// \macro LLVM_MEMORY_SANITIZER_BUILD
+/// \brief Whether LLVM itself is built with MemorySanitizer instrumentation.
+#if __has_feature(memory_sanitizer)
+# define LLVM_MEMORY_SANITIZER_BUILD 1
+# include <sanitizer/msan_interface.h>
+#else
+# define LLVM_MEMORY_SANITIZER_BUILD 0
+# define __msan_allocated_memory(p, size)
+# define __msan_unpoison(p, size)
+#endif
+
+/// \macro LLVM_ADDRESS_SANITIZER_BUILD
+/// \brief Whether LLVM itself is built with AddressSanitizer instrumentation.
+#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+# define LLVM_ADDRESS_SANITIZER_BUILD 1
+# include <sanitizer/asan_interface.h>
+#else
+# define LLVM_ADDRESS_SANITIZER_BUILD 0
+# define __asan_poison_memory_region(p, size)
+# define __asan_unpoison_memory_region(p, size)
+#endif
+
+/// \macro LLVM_THREAD_SANITIZER_BUILD
+/// \brief Whether LLVM itself is built with ThreadSanitizer instrumentation.
+#if __has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__)
+# define LLVM_THREAD_SANITIZER_BUILD 1
+#else
+# define LLVM_THREAD_SANITIZER_BUILD 0
+#endif
+
+#if LLVM_THREAD_SANITIZER_BUILD
+// Thread Sanitizer is a tool that finds races in code.
+// See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations .
+// tsan detects these exact functions by name.
+#ifdef __cplusplus
+extern "C" {
+#endif
+void AnnotateHappensAfter(const char *file, int line, const volatile void *cv);
+void AnnotateHappensBefore(const char *file, int line, const volatile void *cv);
+void AnnotateIgnoreWritesBegin(const char *file, int line);
+void AnnotateIgnoreWritesEnd(const char *file, int line);
+#ifdef __cplusplus
+}
+#endif
+
+// This marker is used to define a happens-before arc. The race detector will
+// infer an arc from the begin to the end when they share the same pointer
+// argument.
+# define TsanHappensBefore(cv) AnnotateHappensBefore(__FILE__, __LINE__, cv)
+
+// This marker defines the destination of a happens-before arc.
+# define TsanHappensAfter(cv) AnnotateHappensAfter(__FILE__, __LINE__, cv)
+
+// Ignore any races on writes between here and the next TsanIgnoreWritesEnd.
+# define TsanIgnoreWritesBegin() AnnotateIgnoreWritesBegin(__FILE__, __LINE__)
+
+// Resume checking for racy writes.
+# define TsanIgnoreWritesEnd() AnnotateIgnoreWritesEnd(__FILE__, __LINE__)
+#else
+# define TsanHappensBefore(cv)
+# define TsanHappensAfter(cv)
+# define TsanIgnoreWritesBegin()
+# define TsanIgnoreWritesEnd()
+#endif
+
+/// \macro LLVM_NO_SANITIZE
+/// \brief Disable a particular sanitizer for a function.
+#if __has_attribute(no_sanitize)
+#define LLVM_NO_SANITIZE(KIND) __attribute__((no_sanitize(KIND)))
+#else
+#define LLVM_NO_SANITIZE(KIND)
+#endif
+
+/// \brief Mark debug helper function definitions like dump() that should not be
+/// stripped from debug builds.
+/// Note that you should also surround dump() functions with
+/// `#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)` so they do always
+/// get stripped in release builds.
+// FIXME: Move this to a private config.h as it's not usable in public headers.
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+#define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE LLVM_ATTRIBUTE_USED
+#else
+#define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE
+#endif
+
+/// \macro LLVM_PRETTY_FUNCTION
+/// \brief Gets a user-friendly looking function signature for the current scope
+/// using the best available method on each platform. The exact format of the
+/// resulting string is implementation specific and non-portable, so this should
+/// only be used, for example, for logging or diagnostics.
+#if defined(_MSC_VER)
+#define LLVM_PRETTY_FUNCTION __FUNCSIG__
+#elif defined(__GNUC__) || defined(__clang__)
+#define LLVM_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#else
+#define LLVM_PRETTY_FUNCTION __func__
+#endif
+
+/// \macro LLVM_THREAD_LOCAL
+/// \brief A thread-local storage specifier which can be used with globals,
+/// extern globals, and static globals.
+///
+/// This is essentially an extremely restricted analog to C++11's thread_local
+/// support, and uses that when available. However, it falls back on
+/// platform-specific or vendor-provided extensions when necessary. These
+/// extensions don't support many of the C++11 thread_local's features. You
+/// should only use this for PODs that you can statically initialize to
+/// some constant value. In almost all circumstances this is most appropriate
+/// for use with a pointer, integer, or small aggregation of pointers and
+/// integers.
+#if LLVM_ENABLE_THREADS
+#if __has_feature(cxx_thread_local)
+#define LLVM_THREAD_LOCAL thread_local
+#elif defined(_MSC_VER)
+// MSVC supports this with a __declspec.
+#define LLVM_THREAD_LOCAL __declspec(thread)
+#else
+// Clang, GCC, and other compatible compilers used __thread prior to C++11 and
+// we only need the restricted functionality that provides.
+#define LLVM_THREAD_LOCAL __thread
+#endif
+#else // !LLVM_ENABLE_THREADS
+// If threading is disabled entirely, this compiles to nothing and you get
+// a normal global variable.
+#define LLVM_THREAD_LOCAL
+#endif
+
+/// \macro LLVM_ENABLE_EXCEPTIONS
+/// \brief Whether LLVM is built with exception support.
+#if __has_feature(cxx_exceptions)
+#define LLVM_ENABLE_EXCEPTIONS 1
+#elif defined(__GNUC__) && defined(__EXCEPTIONS)
+#define LLVM_ENABLE_EXCEPTIONS 1
+#elif defined(_MSC_VER) && defined(_CPPUNWIND)
+#define LLVM_ENABLE_EXCEPTIONS 1
+#endif
+
+/// \macro LLVM_PLUGIN_IMPORT
+/// \brief Used to import the well-known entry point for registering loaded pass
+/// plugins
+#ifdef WIN32
+#define LLVM_PLUGIN_IMPORT __declspec(dllimport)
+#else
+#define LLVM_PLUGIN_IMPORT
+#endif
+
+/// \macro LLVM_PLUGIN_EXPORT
+/// \brief Used to export the well-known entry point for registering loaded pass
+/// plugins
+#ifdef WIN32
+#define LLVM_PLUGIN_EXPORT __declspec(dllexport)
+#else
+#define LLVM_PLUGIN_EXPORT
+#endif
+
+#endif
diff --git a/src/3rdparty/llvm/include/llvm/Support/Compiler.h b/src/3rdparty/llvm/include/llvm/Support/Compiler.h
new file mode 100644
index 0000000000..43a96e49ce
--- /dev/null
+++ b/src/3rdparty/llvm/include/llvm/Support/Compiler.h
@@ -0,0 +1,19 @@
+//===-- llvm/Support/Compiler.h - Compiler abstraction support --*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Due to layering constraints (Support depends on Demangler) this is a thin
+// wrapper around the implementation that lives in llvm-c, though most clients
+// can/should think of this as being provided by Support for simplicity (not
+// many clients are aware of their dependency on Demangler/it's a weird place to
+// own this - but didn't seem to justify splitting Support into "lower support"
+// and "upper support").
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Demangle/Compiler.h"
diff --git a/src/3rdparty/llvm/include/llvm/Support/DataTypes.h b/src/3rdparty/llvm/include/llvm/Support/DataTypes.h
new file mode 100644
index 0000000000..ad60a5b3f3
--- /dev/null
+++ b/src/3rdparty/llvm/include/llvm/Support/DataTypes.h
@@ -0,0 +1,17 @@
+//===-- llvm/Support/DataTypes.h - Define fixed size types ------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Due to layering constraints (Support depends on llvm-c) this is a thin
+// wrapper around the implementation that lives in llvm-c, though most clients
+// can/should think of this as being provided by Support for simplicity (not
+// many clients are aware of their dependency on llvm-c).
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-c/DataTypes.h"
diff --git a/src/3rdparty/llvm/include/llvm/Support/PointerLikeTypeTraits.h b/src/3rdparty/llvm/include/llvm/Support/PointerLikeTypeTraits.h
new file mode 100644
index 0000000000..794230d606
--- /dev/null
+++ b/src/3rdparty/llvm/include/llvm/Support/PointerLikeTypeTraits.h
@@ -0,0 +1,116 @@
+//===- llvm/Support/PointerLikeTypeTraits.h - Pointer Traits ----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the PointerLikeTypeTraits class. This allows data
+// structures to reason about pointers and other things that are pointer sized.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_POINTERLIKETYPETRAITS_H
+#define LLVM_SUPPORT_POINTERLIKETYPETRAITS_H
+
+#include "llvm/Support/DataTypes.h"
+#include <type_traits>
+
+namespace llvm {
+
+/// A traits type that is used to handle pointer types and things that are just
+/// wrappers for pointers as a uniform entity.
+template <typename T> struct PointerLikeTypeTraits;
+
+namespace detail {
+/// A tiny meta function to compute the log2 of a compile time constant.
+template <size_t N>
+struct ConstantLog2
+ : std::integral_constant<size_t, ConstantLog2<N / 2>::value + 1> {};
+template <> struct ConstantLog2<1> : std::integral_constant<size_t, 0> {};
+
+// Provide a trait to check if T is pointer-like.
+template <typename T, typename U = void> struct HasPointerLikeTypeTraits {
+ static const bool value = false;
+};
+
+// sizeof(T) is valid only for a complete T.
+template <typename T> struct HasPointerLikeTypeTraits<
+ T, decltype((sizeof(PointerLikeTypeTraits<T>) + sizeof(T)), void())> {
+ static const bool value = true;
+};
+
+template <typename T> struct IsPointerLike {
+ static const bool value = HasPointerLikeTypeTraits<T>::value;
+};
+
+template <typename T> struct IsPointerLike<T *> {
+ static const bool value = true;
+};
+} // namespace detail
+
+// Provide PointerLikeTypeTraits for non-cvr pointers.
+template <typename T> struct PointerLikeTypeTraits<T *> {
+ static inline void *getAsVoidPointer(T *P) { return P; }
+ static inline T *getFromVoidPointer(void *P) { return static_cast<T *>(P); }
+
+ enum { NumLowBitsAvailable = detail::ConstantLog2<alignof(T)>::value };
+};
+
+template <> struct PointerLikeTypeTraits<void *> {
+ static inline void *getAsVoidPointer(void *P) { return P; }
+ static inline void *getFromVoidPointer(void *P) { return P; }
+
+ /// Note, we assume here that void* is related to raw malloc'ed memory and
+ /// that malloc returns objects at least 4-byte aligned. However, this may be
+ /// wrong, or pointers may be from something other than malloc. In this case,
+ /// you should specify a real typed pointer or avoid this template.
+ ///
+ /// All clients should use assertions to do a run-time check to ensure that
+ /// this is actually true.
+ enum { NumLowBitsAvailable = 2 };
+};
+
+// Provide PointerLikeTypeTraits for const things.
+template <typename T> struct PointerLikeTypeTraits<const T> {
+ typedef PointerLikeTypeTraits<T> NonConst;
+
+ static inline const void *getAsVoidPointer(const T P) {
+ return NonConst::getAsVoidPointer(P);
+ }
+ static inline const T getFromVoidPointer(const void *P) {
+ return NonConst::getFromVoidPointer(const_cast<void *>(P));
+ }
+ enum { NumLowBitsAvailable = NonConst::NumLowBitsAvailable };
+};
+
+// Provide PointerLikeTypeTraits for const pointers.
+template <typename T> struct PointerLikeTypeTraits<const T *> {
+ typedef PointerLikeTypeTraits<T *> NonConst;
+
+ static inline const void *getAsVoidPointer(const T *P) {
+ return NonConst::getAsVoidPointer(const_cast<T *>(P));
+ }
+ static inline const T *getFromVoidPointer(const void *P) {
+ return NonConst::getFromVoidPointer(const_cast<void *>(P));
+ }
+ enum { NumLowBitsAvailable = NonConst::NumLowBitsAvailable };
+};
+
+// Provide PointerLikeTypeTraits for uintptr_t.
+template <> struct PointerLikeTypeTraits<uintptr_t> {
+ static inline void *getAsVoidPointer(uintptr_t P) {
+ return reinterpret_cast<void *>(P);
+ }
+ static inline uintptr_t getFromVoidPointer(void *P) {
+ return reinterpret_cast<uintptr_t>(P);
+ }
+ // No bits are available!
+ enum { NumLowBitsAvailable = 0 };
+};
+
+} // end namespace llvm
+
+#endif
diff --git a/src/3rdparty/llvm/llvm.pri b/src/3rdparty/llvm/llvm.pri
new file mode 100644
index 0000000000..6b3b0689ec
--- /dev/null
+++ b/src/3rdparty/llvm/llvm.pri
@@ -0,0 +1,18 @@
+INCLUDEPATH += $$PWD/include
+
+HEADERS += \
+ $$PWD/include/llvm/ADT/ilist_node.h \
+ $$PWD/include/llvm/ADT/iterator_range.h \
+ $$PWD/include/llvm/ADT/simple_ilist.h \
+ $$PWD/include/llvm/ADT/ilist_base.h \
+ $$PWD/include/llvm/ADT/ilist_node_options.h \
+ $$PWD/include/llvm/ADT/ilist_iterator.h \
+ $$PWD/include/llvm/ADT/ilist_node_base.h \
+ $$PWD/include/llvm/ADT/PointerIntPair.h \
+ $$PWD/include/llvm/ADT/iterator.h \
+ $$PWD/include/llvm/ADT/ilist.h \
+ $$PWD/include/llvm/Demangle/Compiler.h \
+ $$PWD/include/llvm/Support/Compiler.h \
+ $$PWD/include/llvm/Support/PointerLikeTypeTraits.h \
+ $$PWD/include/llvm/Support/DataTypes.h \
+ $$PWD/include/llvm-c/DataTypes.h
diff --git a/src/3rdparty/llvm/qt_attribution.json b/src/3rdparty/llvm/qt_attribution.json
new file mode 100644
index 0000000000..5924f16ac3
--- /dev/null
+++ b/src/3rdparty/llvm/qt_attribution.json
@@ -0,0 +1,14 @@
+[
+ {
+ "Id": "llvm-adt",
+ "Name": "LLVM: ADT",
+ "QDocModule": "qtqml",
+ "QtUsage": "An intrusively linked list, used in the tracing JIT to hold lists of instructions",
+
+ "Path": "src/3rdparty/llvm",
+ "License": "UIUC 3-clause \"New\" or \"Revised\" License",
+ "LicenseId": "BSD-3-Clause",
+ "LicenseFile": "LICENSE.TXT",
+ "Copyright": "Copyright (c) 2003-2017 University of Illinois at Urbana-Champaign."
+ }
+]
diff --git a/src/3rdparty/masm/assembler/ARM64Assembler.h b/src/3rdparty/masm/assembler/ARM64Assembler.h
index a9166e83a2..ca6b33d39a 100644
--- a/src/3rdparty/masm/assembler/ARM64Assembler.h
+++ b/src/3rdparty/masm/assembler/ARM64Assembler.h
@@ -26,7 +26,7 @@
#ifndef ARM64Assembler_h
#define ARM64Assembler_h
-#if ENABLE(ASSEMBLER) && (CPU(ARM64) || defined(V4_BOOTSTRAP))
+#if ENABLE(ASSEMBLER) && CPU(ARM64)
#include "AssemblerBuffer.h"
#include "AbstractMacroAssembler.h"
@@ -3021,10 +3021,7 @@ public:
static void cacheFlush(void* code, size_t size)
{
-#if defined(V4_BOOTSTRAP)
- UNUSED_PARAM(code)
- UNUSED_PARAM(size)
-#elif OS(IOS)
+#if OS(IOS)
sys_cache_control(kCacheFunctionPrepareForExecution, code, size);
#elif OS(LINUX)
size_t page = pageSize();
diff --git a/src/3rdparty/masm/assembler/ARMv7Assembler.h b/src/3rdparty/masm/assembler/ARMv7Assembler.h
index d57e5a7c78..03cb9f42f8 100644
--- a/src/3rdparty/masm/assembler/ARMv7Assembler.h
+++ b/src/3rdparty/masm/assembler/ARMv7Assembler.h
@@ -27,7 +27,7 @@
#ifndef ARMAssembler_h
#define ARMAssembler_h
-#if ENABLE(ASSEMBLER) && (CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP))
+#if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2)
#include "AssemblerBuffer.h"
#include "MacroAssemblerCodeRef.h"
@@ -40,6 +40,10 @@
#include <libkern/OSCacheControl.h>
#endif
+#if OS(RTEMS)
+#include <rtems/rtems/cache.h>
+#endif
+
namespace JSC {
namespace ARMRegisters {
@@ -2166,7 +2170,6 @@ public:
linkJumpAbsolute(location, to);
}
-#if !defined(V4_BOOTSTRAP)
static void linkCall(void* code, AssemblerLabel from, void* to)
{
ASSERT(!(reinterpret_cast<intptr_t>(code) & 1));
@@ -2175,14 +2178,12 @@ public:
setPointer(reinterpret_cast<uint16_t*>(reinterpret_cast<intptr_t>(code) + from.m_offset) - 1, to, false);
}
-#endif
static void linkPointer(void* code, AssemblerLabel where, void* value)
{
setPointer(reinterpret_cast<char*>(code) + where.m_offset, value, false);
}
-#if !defined(V4_BOOTSTRAP)
static void relinkJump(void* from, void* to)
{
ASSERT(!(reinterpret_cast<intptr_t>(from) & 1));
@@ -2205,7 +2206,6 @@ public:
{
return readPointer(reinterpret_cast<uint16_t*>(from) - 1);
}
-#endif
static void repatchInt32(void* where, int32_t value)
{
@@ -2234,7 +2234,6 @@ public:
cacheFlush(location, sizeof(uint16_t) * 2);
}
-#if !defined(V4_BOOTSTRAP)
static void repatchPointer(void* where, void* value)
{
ASSERT(!(reinterpret_cast<intptr_t>(where) & 1));
@@ -2246,7 +2245,6 @@ public:
{
return reinterpret_cast<void*>(readInt32(where));
}
-#endif
static void replaceWithJump(void* instructionStart, void* to)
{
@@ -2321,7 +2319,7 @@ public:
unsigned debugOffset() { return m_formatter.debugOffset(); }
-#if OS(LINUX) && !defined(V4_BOOTSTRAP)
+#if OS(LINUX)
static inline void linuxPageFlush(uintptr_t begin, uintptr_t end)
{
asm volatile(
@@ -2341,10 +2339,7 @@ public:
static void cacheFlush(void* code, size_t size)
{
-#if defined(V4_BOOTSTRAP)
- UNUSED_PARAM(code)
- UNUSED_PARAM(size)
-#elif OS(IOS)
+#if OS(IOS)
sys_cache_control(kCacheFunctionPrepareForExecution, code, size);
#elif OS(LINUX)
size_t page = pageSize();
@@ -2368,6 +2363,8 @@ public:
#elif OS(QNX)
#if !ENABLE(ASSEMBLER_WX_EXCLUSIVE)
msync(code, size, MS_INVALIDATE_ICACHE);
+#elif OS(RTEMS)
+ rtems_cache_flush_multiple_data_lines(code, size);
#else
UNUSED_PARAM(code);
UNUSED_PARAM(size);
@@ -2662,11 +2659,6 @@ private:
static void linkBX(uint16_t* instruction, void* target)
{
-#if defined(V4_BOOTSTRAP)
- UNUSED_PARAM(instruction);
- UNUSED_PARAM(target);
- RELEASE_ASSERT_NOT_REACHED();
-#else
// FIMXE: this should be up in the MacroAssembler layer. :-(
ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1));
ASSERT(!(reinterpret_cast<intptr_t>(target) & 1));
@@ -2679,7 +2671,6 @@ private:
instruction[-3] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16);
instruction[-2] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, hi16);
instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3);
-#endif
}
void linkConditionalBX(Condition cond, uint16_t* instruction, void* target)
@@ -2712,9 +2703,6 @@ private:
instruction[-3] = OP_NOP_T2b;
linkJumpT4(instruction, target);
} else {
-#if defined(V4_BOOTSTRAP)
- RELEASE_ASSERT_NOT_REACHED();
-#else
const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip;
ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast<uint16_t>(reinterpret_cast<uint32_t>(target) + 1));
ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast<uint16_t>(reinterpret_cast<uint32_t>(target) >> 16));
@@ -2723,7 +2711,6 @@ private:
instruction[-3] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16);
instruction[-2] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, hi16);
instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3);
-#endif
}
}
diff --git a/src/3rdparty/masm/assembler/AbstractMacroAssembler.h b/src/3rdparty/masm/assembler/AbstractMacroAssembler.h
index d0c1c4613e..14644a4193 100644
--- a/src/3rdparty/masm/assembler/AbstractMacroAssembler.h
+++ b/src/3rdparty/masm/assembler/AbstractMacroAssembler.h
@@ -66,7 +66,7 @@ public:
typedef MacroAssemblerCodePtr CodePtr;
typedef MacroAssemblerCodeRef CodeRef;
-#if !CPU(ARM_THUMB2) && !CPU(ARM64) && !defined(V4_BOOTSTRAP)
+#if !CPU(ARM_THUMB2) && !CPU(ARM64)
class Jump;
#endif
@@ -328,7 +328,7 @@ public:
friend class AbstractMacroAssembler;
friend struct DFG::OSRExit;
-#if CPU(ARM_THUMB2) || CPU(ARM64) || defined(V4_BOOTSTRAP)
+#if CPU(ARM_THUMB2) || CPU(ARM64)
using Jump = typename AssemblerType::template Jump<Label>;
friend Jump;
#else
@@ -461,7 +461,7 @@ public:
AssemblerLabel m_label;
};
-#if CPU(ARM_THUMB2) || CPU(ARM64) || defined(V4_BOOTSTRAP)
+#if CPU(ARM_THUMB2) || CPU(ARM64)
using Jump = typename AssemblerType::template Jump<Label>;
friend Jump;
#endif
@@ -516,7 +516,7 @@ public:
// into the code buffer - it is typically used to link the jump, setting the
// relative offset such that when executed it will jump to the desired
// destination.
-#if !CPU(ARM_THUMB2) && !CPU(ARM64) && !defined(V4_BOOTSTRAP)
+#if !CPU(ARM_THUMB2) && !CPU(ARM64)
class Jump {
template<class TemplateAssemblerType>
friend class AbstractMacroAssembler;
@@ -528,7 +528,7 @@ public:
{
}
-#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP)
+#if CPU(ARM_THUMB2)
// Fixme: this information should be stored in the instruction stream, not in the Jump object.
Jump(AssemblerLabel jmp, ARMv7Assembler::JumpType type = ARMv7Assembler::JumpNoCondition, ARMv7Assembler::Condition condition = ARMv7Assembler::ConditionInvalid)
: m_label(jmp)
@@ -621,7 +621,7 @@ public:
private:
AssemblerLabel m_label;
-#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP)
+#if CPU(ARM_THUMB2)
ARMv7Assembler::JumpType m_type;
ARMv7Assembler::Condition m_condition;
#endif
@@ -878,12 +878,10 @@ protected:
AssemblerType::repatchPointer(dataLabelPtr.dataLocation(), value);
}
-#if !defined(V4_BOOTSTRAP)
static void* readPointer(CodeLocationDataLabelPtr dataLabelPtr)
{
return AssemblerType::readPointer(dataLabelPtr.dataLocation());
}
-#endif
static void replaceWithLoad(CodeLocationConvertibleLoad label)
{
diff --git a/src/3rdparty/masm/assembler/LinkBuffer.h b/src/3rdparty/masm/assembler/LinkBuffer.h
index a1bb046d43..8e9a3d9c7a 100644
--- a/src/3rdparty/masm/assembler/LinkBuffer.h
+++ b/src/3rdparty/masm/assembler/LinkBuffer.h
@@ -374,7 +374,7 @@ public:
}
};
-#if CPU(ARM_THUMB2) || CPU(ARM64) || defined(V4_BOOTSTRAP)
+#if CPU(ARM_THUMB2) || CPU(ARM64)
template <typename T>
struct BranchCompactingExecutableOffsetCalculator {
@@ -509,7 +509,7 @@ inline void BranchCompactingLinkBuffer<MacroAssembler>::linkCode(void* ownerUID,
m_executableMemory->shrink(m_size);
}
-#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP)
+#if CPU(ARM_THUMB2)
template <>
class LinkBuffer<JSC::MacroAssembler<MacroAssemblerARMv7>> : public BranchCompactingLinkBuffer<JSC::MacroAssembler<MacroAssemblerARMv7>>
{
@@ -520,7 +520,7 @@ public:
};
#endif
-#if CPU(ARM64) || defined(V4_BOOTSTRAP)
+#if CPU(ARM64)
template <>
class LinkBuffer<JSC::MacroAssembler<MacroAssemblerARM64>> : public BranchCompactingLinkBuffer<JSC::MacroAssembler<MacroAssemblerARM64>>
{
diff --git a/src/3rdparty/masm/assembler/MacroAssembler.h b/src/3rdparty/masm/assembler/MacroAssembler.h
index b442a81bd0..aada47303f 100644
--- a/src/3rdparty/masm/assembler/MacroAssembler.h
+++ b/src/3rdparty/masm/assembler/MacroAssembler.h
@@ -97,10 +97,7 @@ public:
using MacroAssemblerBase::load32;
-#if defined(V4_BOOTSTRAP)
- using MacroAssemblerBase::loadPtr;
- using MacroAssemblerBase::storePtr;
-#elif CPU(X86_64) || CPU(ARM64)
+#if CPU(X86_64) || CPU(ARM64)
using MacroAssemblerBase::add64;
using MacroAssemblerBase::sub64;
using MacroAssemblerBase::xor64;
@@ -214,14 +211,12 @@ public:
store32(value, addressForPoke(index));
}
-#if !defined(V4_BOOTSTRAP)
void poke(TrustedImmPtr imm, int index = 0)
{
storePtr(imm, addressForPoke(index));
}
-#endif
-#if (CPU(X86_64) || CPU(ARM64)) && !defined(V4_BOOTSTRAP)
+#if CPU(X86_64) || CPU(ARM64)
void peek64(RegisterID dest, int index = 0)
{
load64(Address(MacroAssemblerBase::stackPointerRegister, (index * sizeof(void*))), dest);
@@ -352,7 +347,6 @@ public:
return !(this->random() & (BlindingModulus - 1));
}
-#if !defined(V4_BOOTSTRAP)
// Ptr methods
// On 32-bit platforms (i.e. x86), these methods directly map onto their 32-bit equivalents.
// FIXME: should this use a test for 32-bitness instead of this specific exception?
@@ -877,7 +871,7 @@ public:
{
return branchSub64(cond, src1, src2, dest);
}
-#endif // !defined(V4_BOOTSTRAP)
+#endif // !CPU(X86_64) && !CPU(ARM64)
#if ENABLE(JIT_CONSTANT_BLINDING)
using MacroAssemblerBase::and64;
@@ -1101,8 +1095,6 @@ public:
#endif
-#endif // !CPU(X86_64)
-
#if ENABLE(JIT_CONSTANT_BLINDING)
bool shouldBlind(Imm32 imm)
{
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARM64.h b/src/3rdparty/masm/assembler/MacroAssemblerARM64.h
index e5a704292d..c0c68f6393 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerARM64.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerARM64.h
@@ -26,7 +26,7 @@
#ifndef MacroAssemblerARM64_h
#define MacroAssemblerARM64_h
-#if ENABLE(ASSEMBLER) && (CPU(ARM64) || defined(V4_BOOTSTRAP))
+#if ENABLE(ASSEMBLER) && CPU(ARM64)
#include "ARM64Assembler.h"
#include "AbstractMacroAssembler.h"
@@ -211,33 +211,6 @@ public:
static bool shouldBlindForSpecificArch(uint32_t value) { return value >= 0x00ffffff; }
static bool shouldBlindForSpecificArch(uint64_t value) { return value >= 0x00ffffff; }
-#if defined(V4_BOOTSTRAP)
- void loadPtr(ImplicitAddress address, RegisterID dest)
- {
- load64(address, dest);
- }
-
- void subPtr(TrustedImm32 imm, RegisterID dest)
- {
- sub64(imm, dest);
- }
-
- void addPtr(TrustedImm32 imm, RegisterID dest)
- {
- add64(imm, dest);
- }
-
- void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest)
- {
- add64(imm, src, dest);
- }
-
- void storePtr(RegisterID src, ImplicitAddress address)
- {
- store64(src, address);
- }
-#endif
-
// Integer operations:
void add32(RegisterID a, RegisterID b, RegisterID dest)
@@ -1126,6 +1099,14 @@ public:
m_assembler.ldrh(dest, address.base, memoryTempRegister);
}
+ void load16(ExtendedAddress address, RegisterID dest)
+ {
+ moveToCachedReg(TrustedImmPtr(reinterpret_cast<void*>(address.offset)), m_cachedMemoryTempRegister);
+ m_assembler.ldrh(dest, memoryTempRegister, address.base, ARM64Assembler::UXTX, 1);
+ if (dest == memoryTempRegister)
+ m_cachedMemoryTempRegister.invalidate();
+ }
+
void load16Unaligned(ImplicitAddress address, RegisterID dest)
{
load16(address, dest);
@@ -2814,7 +2795,6 @@ public:
return branch32(cond, left, dataTempRegister);
}
-#if !defined(V4_BOOTSTRAP)
PatchableJump patchableBranchPtr(RelationalCondition cond, Address left, TrustedImmPtr right)
{
m_makeJumpPatchable = true;
@@ -2822,7 +2802,6 @@ public:
m_makeJumpPatchable = false;
return PatchableJump(result);
}
-#endif
PatchableJump patchableBranchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1))
{
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h b/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h
index 99801a0e3b..6232834fde 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h
@@ -27,7 +27,7 @@
#ifndef MacroAssemblerARMv7_h
#define MacroAssemblerARMv7_h
-#if ENABLE(ASSEMBLER) && (CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP))
+#if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2)
#include "ARMv7Assembler.h"
#include "AbstractMacroAssembler.h"
@@ -162,41 +162,12 @@ public:
{
add32(imm, dest, dest);
}
-
-#if defined(V4_BOOTSTRAP)
- void loadPtr(ImplicitAddress address, RegisterID dest)
- {
- load32(address, dest);
- }
-
- void subPtr(TrustedImm32 imm, RegisterID dest)
- {
- sub32(imm, dest);
- }
-
- void addPtr(TrustedImm32 imm, RegisterID dest)
- {
- add32(imm, dest);
- }
-
- void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest)
- {
- add32(imm, src, dest);
- }
-
- void storePtr(RegisterID src, ImplicitAddress address)
- {
- store32(src, address);
- }
-#endif
-#if !defined(V4_BOOTSTRAP)
void add32(AbsoluteAddress src, RegisterID dest)
{
load32(src.m_ptr, dataTempRegister);
add32(dataTempRegister, dest);
}
-#endif
void add32(TrustedImm32 imm, RegisterID src, RegisterID dest)
{
@@ -237,7 +208,6 @@ public:
add32(dataTempRegister, dest);
}
-#if !defined(V4_BOOTSTRAP)
void add32(TrustedImm32 imm, AbsoluteAddress address)
{
load32(address.m_ptr, dataTempRegister);
@@ -282,7 +252,6 @@ public:
m_assembler.adc(dataTempRegister, dataTempRegister, ARMThumbImmediate::makeEncodedImm(imm.m_value >> 31));
m_assembler.str(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(4));
}
-#endif
void and32(RegisterID op1, RegisterID op2, RegisterID dest)
{
@@ -384,7 +353,6 @@ public:
or32(dataTempRegister, dest);
}
-#if !defined(V4_BOOTSTRAP)
void or32(RegisterID src, AbsoluteAddress dest)
{
move(TrustedImmPtr(dest.m_ptr), addressTempRegister);
@@ -392,7 +360,6 @@ public:
or32(src, dataTempRegister);
store32(dataTempRegister, addressTempRegister);
}
-#endif
void or32(TrustedImm32 imm, RegisterID dest)
{
@@ -504,7 +471,6 @@ public:
sub32(dataTempRegister, dest);
}
-#if !defined(V4_BOOTSTRAP)
void sub32(TrustedImm32 imm, AbsoluteAddress address)
{
load32(address.m_ptr, dataTempRegister);
@@ -521,7 +487,6 @@ public:
store32(dataTempRegister, address.m_ptr);
}
-#endif
void xor32(Address src, RegisterID dest)
{
@@ -698,13 +663,11 @@ public:
load16(setupArmAddress(address), dest);
}
-#if !defined(V4_BOOTSTRAP)
void load32(const void* address, RegisterID dest)
{
move(TrustedImmPtr(address), addressTempRegister);
m_assembler.ldr(dest, addressTempRegister, ARMThumbImmediate::makeUInt16(0));
}
-#endif
ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest)
{
@@ -809,7 +772,6 @@ public:
store32(dataTempRegister, setupArmAddress(address));
}
-#if !defined(V4_BOOTSTRAP)
void store32(RegisterID src, const void* address)
{
move(TrustedImmPtr(address), addressTempRegister);
@@ -821,7 +783,6 @@ public:
move(imm, dataTempRegister);
store32(dataTempRegister, address);
}
-#endif
void store8(RegisterID src, BaseIndex address)
{
@@ -839,7 +800,6 @@ public:
store8(dataTempRegister, address);
}
-#if !defined(V4_BOOTSTRAP)
void store8(RegisterID src, void* address)
{
move(TrustedImmPtr(address), addressTempRegister);
@@ -851,7 +811,6 @@ public:
move(imm, dataTempRegister);
store8(dataTempRegister, address);
}
-#endif
void store16(RegisterID src, BaseIndex address)
{
@@ -949,13 +908,11 @@ public:
m_assembler.vmov(dest, src);
}
-#if !defined(V4_BOOTSTRAP)
void loadDouble(const void* address, FPRegisterID dest)
{
move(TrustedImmPtr(address), addressTempRegister);
m_assembler.vldr(dest, addressTempRegister, 0);
}
-#endif
void storeDouble(FPRegisterID src, ImplicitAddress address)
{
@@ -987,13 +944,11 @@ public:
m_assembler.fsts(ARMRegisters::asSingle(src), base, offset);
}
-#if !defined(V4_BOOTSTRAP)
void storeDouble(FPRegisterID src, const void* address)
{
move(TrustedImmPtr(address), addressTempRegister);
storeDouble(src, addressTempRegister);
}
-#endif
void storeDouble(FPRegisterID src, BaseIndex address)
{
@@ -1027,13 +982,11 @@ public:
m_assembler.vadd(dest, op1, op2);
}
-#if !defined(V4_BOOTSTRAP)
void addDouble(AbsoluteAddress address, FPRegisterID dest)
{
loadDouble(address.m_ptr, fpTempRegister);
m_assembler.vadd(dest, dest, fpTempRegister);
}
-#endif
void divDouble(FPRegisterID src, FPRegisterID dest)
{
@@ -1112,7 +1065,6 @@ public:
m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle());
}
-#if !defined(V4_BOOTSTRAP)
void convertInt32ToDouble(AbsoluteAddress address, FPRegisterID dest)
{
// Fixme: load directly into the fpr!
@@ -1120,7 +1072,6 @@ public:
m_assembler.vmov(fpTempRegister, dataTempRegister, dataTempRegister);
m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle());
}
-#endif
void convertUInt32ToDouble(RegisterID src, FPRegisterID dest, RegisterID /*scratch*/)
{
@@ -1316,12 +1267,10 @@ public:
m_assembler.mov(dest, src);
}
-#if !defined(V4_BOOTSTRAP)
void move(TrustedImmPtr imm, RegisterID dest)
{
move(TrustedImm32(imm), dest);
}
-#endif
void swap(RegisterID reg1, RegisterID reg2)
{
@@ -1462,7 +1411,6 @@ public:
return branch32(cond, addressTempRegister, right);
}
-#if !defined(V4_BOOTSTRAP)
Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right)
{
load32(left.m_ptr, dataTempRegister);
@@ -1475,7 +1423,6 @@ public:
load32(left.m_ptr, addressTempRegister);
return branch32(cond, addressTempRegister, right);
}
-#endif
Jump branch8(RelationalCondition cond, RegisterID left, TrustedImm32 right)
{
@@ -1532,7 +1479,6 @@ public:
return branchTest32(cond, addressTempRegister, mask);
}
-#if !defined(V4_BOOTSTRAP)
Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1))
{
// use addressTempRegister incase the branchTest8 we call uses dataTempRegister. :-/
@@ -1540,7 +1486,6 @@ public:
load8(Address(addressTempRegister), addressTempRegister);
return branchTest32(cond, addressTempRegister, mask);
}
-#endif
void jump(RegisterID target)
{
@@ -1554,14 +1499,12 @@ public:
m_assembler.bx(dataTempRegister);
}
-#if !defined(V4_BOOTSTRAP)
void jump(AbsoluteAddress address)
{
move(TrustedImmPtr(address.m_ptr), dataTempRegister);
load32(Address(dataTempRegister), dataTempRegister);
m_assembler.bx(dataTempRegister);
}
-#endif
// Arithmetic control flow operations:
@@ -1602,7 +1545,6 @@ public:
return branchAdd32(cond, dest, imm, dest);
}
-#if !defined(V4_BOOTSTRAP)
Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest)
{
// Move the high bits of the address into addressTempRegister,
@@ -1628,7 +1570,6 @@ public:
return Jump(makeBranch(cond));
}
-#endif
Jump branchMul32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest)
{
@@ -1799,7 +1740,6 @@ public:
return DataLabel32(this);
}
-#if !defined(V4_BOOTSTRAP)
ALWAYS_INLINE DataLabelPtr moveWithPatch(TrustedImmPtr imm, RegisterID dst)
{
padBeforePatch();
@@ -1827,7 +1767,6 @@ public:
m_makeJumpPatchable = false;
return PatchableJump(result);
}
-#endif
PatchableJump patchableBranchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1))
{
@@ -1845,7 +1784,6 @@ public:
return PatchableJump(result);
}
-#if !defined(V4_BOOTSTRAP)
PatchableJump patchableBranchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0))
{
m_makeJumpPatchable = true;
@@ -1853,7 +1791,6 @@ public:
m_makeJumpPatchable = false;
return PatchableJump(result);
}
-#endif
PatchableJump patchableJump()
{
@@ -1864,7 +1801,6 @@ public:
return PatchableJump(result);
}
-#if !defined(V4_BOOTSTRAP)
ALWAYS_INLINE DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address)
{
DataLabelPtr label = moveWithPatch(initialValue, dataTempRegister);
@@ -1872,7 +1808,6 @@ public:
return label;
}
ALWAYS_INLINE DataLabelPtr storePtrWithPatch(ImplicitAddress address) { return storePtrWithPatch(TrustedImmPtr(0), address); }
-#endif
ALWAYS_INLINE Call tailRecursiveCall()
{
@@ -1893,7 +1828,6 @@ public:
return m_assembler.executableOffsetFor(location);
}
-#if !defined(V4_BOOTSTRAP)
static FunctionPtr readCallTarget(CodeLocationCall call)
{
return FunctionPtr(reinterpret_cast<void(*)()>(ARMv7Assembler::readCallTarget(call.dataLocation())));
@@ -1906,7 +1840,6 @@ public:
const unsigned twoWordOpSize = 4;
return label.labelAtOffset(-twoWordOpSize * 2);
}
-#endif
static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID rd, void* initialValue)
{
@@ -2024,7 +1957,6 @@ private:
template <typename, template <typename> class> friend class LinkBufferBase;
friend class RepatchBuffer;
-#if !defined(V4_BOOTSTRAP)
static void linkCall(void* code, Call call, FunctionPtr function)
{
ARMv7Assembler::linkCall(code, call.m_label, function.value());
@@ -2039,7 +1971,6 @@ private:
{
ARMv7Assembler::relinkCall(call.dataLocation(), destination.executableAddress());
}
-#endif
bool m_makeJumpPatchable;
};
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerX86.h b/src/3rdparty/masm/assembler/MacroAssemblerX86.h
index e3e0bfe5e1..5cffa787ec 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerX86.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerX86.h
@@ -55,38 +55,6 @@ public:
using MacroAssemblerX86Common::convertInt32ToDouble;
using MacroAssemblerX86Common::branchTest8;
-#if defined(V4_BOOTSTRAP)
- void loadPtr(ImplicitAddress address, RegisterID dest)
- {
- load32(address, dest);
- }
-
- void subPtr(TrustedImm32 imm, RegisterID dest)
- {
- sub32(imm, dest);
- }
-
- void addPtr(TrustedImm32 imm, RegisterID dest)
- {
- add32(imm, dest);
- }
-
- void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest)
- {
- add32(imm, src, dest);
- }
-
- void storePtr(RegisterID src, ImplicitAddress address)
- {
- store32(src, address);
- }
-
- Jump branchTest8(ResultCondition cond, ExtendedAddress address, TrustedImm32 mask = TrustedImm32(-1))
- {
- return branchTest8(cond, Address(address.base, address.offset), mask);
- }
-#endif
-
void add32(TrustedImm32 imm, RegisterID src, RegisterID dest)
{
m_assembler.leal_mr(imm.m_value, src, dest);
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h b/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h
index f4349e1f93..0a6db0805b 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h
@@ -53,33 +53,6 @@ public:
using MacroAssemblerX86Common::loadDouble;
using MacroAssemblerX86Common::convertInt32ToDouble;
-#if defined(V4_BOOTSTRAP)
- void loadPtr(ImplicitAddress address, RegisterID dest)
- {
- load64(address, dest);
- }
-
- void subPtr(TrustedImm32 imm, RegisterID dest)
- {
- sub64(imm, dest);
- }
-
- void addPtr(TrustedImm32 imm, RegisterID dest)
- {
- add64(imm, dest);
- }
-
- void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest)
- {
- add64(imm, src, dest);
- }
-
- void storePtr(RegisterID src, ImplicitAddress address)
- {
- store64(src, address);
- }
-#endif
-
void add32(TrustedImm32 imm, AbsoluteAddress address)
{
move(TrustedImmPtr(address.m_ptr), scratchRegister);
@@ -116,6 +89,23 @@ public:
sub32(imm, Address(scratchRegister));
}
+ void load16(ExtendedAddress address, RegisterID dest)
+ {
+ TrustedImmPtr addr(reinterpret_cast<void*>(address.offset));
+ MacroAssemblerX86Common::move(addr, scratchRegister);
+ MacroAssemblerX86Common::load16(BaseIndex(scratchRegister, address.base, TimesTwo), dest);
+ }
+
+ void load16(BaseIndex address, RegisterID dest)
+ {
+ MacroAssemblerX86Common::load16(address, dest);
+ }
+
+ void load16(Address address, RegisterID dest)
+ {
+ MacroAssemblerX86Common::load16(address, dest);
+ }
+
void load32(const void* address, RegisterID dest)
{
if (dest == X86Registers::eax)
diff --git a/src/3rdparty/masm/assembler/X86Assembler.h b/src/3rdparty/masm/assembler/X86Assembler.h
index 2257cb2b9a..e8ae687036 100644
--- a/src/3rdparty/masm/assembler/X86Assembler.h
+++ b/src/3rdparty/masm/assembler/X86Assembler.h
@@ -255,47 +255,6 @@ public:
{
}
-#if defined(V4_BOOTSTRAP)
- template <typename LabelType>
- class Jump {
- template<class TemplateAssemblerType>
- friend class AbstractMacroAssembler;
- friend class Call;
- template <typename, template <typename> class> friend class LinkBufferBase;
- public:
- Jump()
- {
- }
-
- Jump(AssemblerLabel jmp)
- : m_label(jmp)
- {
- }
-
- LabelType label() const
- {
- LabelType result;
- result.m_label = m_label;
- return result;
- }
-
- void link(AbstractMacroAssembler<X86Assembler>* masm) const
- {
- masm->m_assembler.linkJump(m_label, masm->m_assembler.label());
- }
-
- void linkTo(LabelType label, AbstractMacroAssembler<X86Assembler>* masm) const
- {
- masm->m_assembler.linkJump(m_label, label.label());
- }
-
- bool isSet() const { return m_label.isSet(); }
-
- private:
- AssemblerLabel m_label;
- };
-#endif
-
// Stack operations:
void push_r(RegisterID reg)
diff --git a/src/3rdparty/masm/masm-defs.pri b/src/3rdparty/masm/masm-defs.pri
index 08c46a7ac2..90a795c6ce 100644
--- a/src/3rdparty/masm/masm-defs.pri
+++ b/src/3rdparty/masm/masm-defs.pri
@@ -33,10 +33,6 @@ disassembler {
DEFINES += WTF_USE_UDIS86=0
}
-force-compile-jit {
- DEFINES += V4_FORCE_COMPILE_JIT
-}
-
INCLUDEPATH += $$PWD/disassembler
INCLUDEPATH += $$PWD/disassembler/udis86
INCLUDEPATH += $$_OUT_PWD
diff --git a/src/3rdparty/masm/masm.pri b/src/3rdparty/masm/masm.pri
index 0e63ac2ce5..1df4585aae 100644
--- a/src/3rdparty/masm/masm.pri
+++ b/src/3rdparty/masm/masm.pri
@@ -77,7 +77,6 @@ SOURCES += $$PWD/disassembler/ARM64Disassembler.cpp
SOURCES += $$PWD/disassembler/ARM64/A64DOpcode.cpp
HEADERS += $$PWD/disassembler/ARM64/A64DOpcode.h
-!qmldevtools_build {
SOURCES += $$PWD/yarr/YarrCanonicalizeUCS2.cpp \
$$PWD/yarr/YarrCanonicalizeUnicode.cpp \
$$PWD/yarr/YarrInterpreter.cpp \
@@ -94,7 +93,6 @@ HEADERS += $$PWD/yarr/Yarr.h \
$$PWD/yarr/YarrPattern.h \
$$PWD/yarr/YarrSyntaxChecker.h \
$$PWD/yarr/YarrUnicodeProperties.h
-}
#
# Generate RegExpJitTables.h
@@ -128,8 +126,3 @@ QMAKE_EXTRA_COMPILERS += retgen
}
}
}
-
-linux {
- requires(qtConfig(dlopen))
- QMAKE_USE_PRIVATE += libdl
-}
diff --git a/src/3rdparty/masm/stubs/wtf/Vector.h b/src/3rdparty/masm/stubs/wtf/Vector.h
index f4f4dc5cf4..2fead9f6ba 100644
--- a/src/3rdparty/masm/stubs/wtf/Vector.h
+++ b/src/3rdparty/masm/stubs/wtf/Vector.h
@@ -109,6 +109,15 @@ public:
inline bool isEmpty() const { return this->empty(); }
inline T &last() { return *(this->begin() + this->size() - 1); }
+
+ bool contains(const T &value) const
+ {
+ for (const T &inVector : *this) {
+ if (inVector == value)
+ return true;
+ }
+ return false;
+ }
};
template <typename T, int capacity>
diff --git a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp
index 3b2a73a39a..d59fdcd675 100644
--- a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp
+++ b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp
@@ -74,21 +74,9 @@ static int memfdForUsage(size_t bytes, OSAllocator::Usage usage)
break;
}
- // try to get our own library name by giving dladdr a pointer pointing to
- // something we know to be in it (using a pointer to string data)
- static const char *libname = [=]() {
- Dl_info info;
- if (dladdr(type, &info) == 0)
- info.dli_fname = nullptr;
- return info.dli_fname;
- }();
-
char buf[PATH_MAX];
strcpy(buf, type);
- if (libname)
- strcat(buf, libname);
- else
- strcat(buf, "QtQml");
+ strcat(buf, "QtQml");
int fd = syscall(SYS_memfd_create, buf, MFD_CLOEXEC);
if (fd != -1) {
diff --git a/src/3rdparty/masm/wtf/Platform.h b/src/3rdparty/masm/wtf/Platform.h
index d5f69927db..ab1da2198a 100644
--- a/src/3rdparty/masm/wtf/Platform.h
+++ b/src/3rdparty/masm/wtf/Platform.h
@@ -438,6 +438,10 @@
#define WTF_OS_WINDOWS 1
#endif
+#ifdef __rtems__
+#define WTF_OS_RTEMS 1
+#endif
+
#define WTF_OS_WIN ERROR "USE WINDOWS WITH OS NOT WIN"
#define WTF_OS_MAC ERROR "USE MAC_OS_X WITH OS NOT MAC"
@@ -451,6 +455,7 @@
|| OS(NETBSD) \
|| OS(OPENBSD) \
|| OS(QNX) \
+ || OS(RTEMS) \
|| OS(SOLARIS) \
|| defined(unix) \
|| defined(__unix) \
@@ -1051,6 +1056,7 @@
#if CPU(ARM64) || (CPU(X86_64) && !OS(WINDOWS))
/* Enable JIT'ing Regular Expressions that have nested parenthesis. */
#define ENABLE_YARR_JIT_ALL_PARENS_EXPRESSIONS 1
+#define ENABLE_YARR_JIT_BACKREFERENCES 1
#endif
#endif
diff --git a/src/3rdparty/masm/yarr/YarrCanonicalize.h b/src/3rdparty/masm/yarr/YarrCanonicalize.h
index fb5e0231ac..cbd279edca 100644
--- a/src/3rdparty/masm/yarr/YarrCanonicalize.h
+++ b/src/3rdparty/masm/yarr/YarrCanonicalize.h
@@ -53,6 +53,7 @@ struct CanonicalizationRange {
extern const size_t UCS2_CANONICALIZATION_RANGES;
extern const UChar32* const ucs2CharacterSetInfo[];
extern const CanonicalizationRange ucs2RangeInfo[];
+extern const uint16_t canonicalTableLChar[256];
extern const size_t UNICODE_CANONICALIZATION_RANGES;
extern const UChar32* const unicodeCharacterSetInfo[];
diff --git a/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp b/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp
index d91c771590..0eb59f38d2 100644
--- a/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp
+++ b/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013, 2015-2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -44,9 +44,17 @@ const UChar32 ucs2CharacterSet10[] = { 0x03a0, 0x03c0, 0x03d6, 0 };
const UChar32 ucs2CharacterSet11[] = { 0x03a1, 0x03c1, 0x03f1, 0 };
const UChar32 ucs2CharacterSet12[] = { 0x03a3, 0x03c2, 0x03c3, 0 };
const UChar32 ucs2CharacterSet13[] = { 0x03a6, 0x03c6, 0x03d5, 0 };
-const UChar32 ucs2CharacterSet14[] = { 0x1e60, 0x1e61, 0x1e9b, 0 };
+const UChar32 ucs2CharacterSet14[] = { 0x0412, 0x0432, 0x1c80, 0 };
+const UChar32 ucs2CharacterSet15[] = { 0x0414, 0x0434, 0x1c81, 0 };
+const UChar32 ucs2CharacterSet16[] = { 0x041e, 0x043e, 0x1c82, 0 };
+const UChar32 ucs2CharacterSet17[] = { 0x0421, 0x0441, 0x1c83, 0 };
+const UChar32 ucs2CharacterSet18[] = { 0x0422, 0x0442, 0x1c84, 0x1c85, 0 };
+const UChar32 ucs2CharacterSet19[] = { 0x042a, 0x044a, 0x1c86, 0 };
+const UChar32 ucs2CharacterSet20[] = { 0x0462, 0x0463, 0x1c87, 0 };
+const UChar32 ucs2CharacterSet21[] = { 0x1e60, 0x1e61, 0x1e9b, 0 };
+const UChar32 ucs2CharacterSet22[] = { 0x1c88, 0xa64a, 0xa64b, 0 };
-static const size_t UCS2_CANONICALIZATION_SETS = 15;
+static const size_t UCS2_CANONICALIZATION_SETS = 23;
const UChar32* const ucs2CharacterSetInfo[UCS2_CANONICALIZATION_SETS] = {
ucs2CharacterSet0,
ucs2CharacterSet1,
@@ -63,9 +71,17 @@ const UChar32* const ucs2CharacterSetInfo[UCS2_CANONICALIZATION_SETS] = {
ucs2CharacterSet12,
ucs2CharacterSet13,
ucs2CharacterSet14,
+ ucs2CharacterSet15,
+ ucs2CharacterSet16,
+ ucs2CharacterSet17,
+ ucs2CharacterSet18,
+ ucs2CharacterSet19,
+ ucs2CharacterSet20,
+ ucs2CharacterSet21,
+ ucs2CharacterSet22,
};
-const size_t UCS2_CANONICALIZATION_RANGES = 391;
+const size_t UCS2_CANONICALIZATION_RANGES = 448;
const CanonicalizationRange ucs2RangeInfo[UCS2_CANONICALIZATION_RANGES] = {
{ 0x0000, 0x0040, 0x0000, CanonicalizeUnique },
{ 0x0041, 0x005a, 0x0020, CanonicalizeRangeLo },
@@ -182,7 +198,7 @@ const CanonicalizationRange ucs2RangeInfo[UCS2_CANONICALIZATION_RANGES] = {
{ 0x0267, 0x0267, 0x0000, CanonicalizeUnique },
{ 0x0268, 0x0268, 0x00d1, CanonicalizeRangeHi },
{ 0x0269, 0x0269, 0x00d3, CanonicalizeRangeHi },
- { 0x026a, 0x026a, 0x0000, CanonicalizeUnique },
+ { 0x026a, 0x026a, 0xa544, CanonicalizeRangeLo },
{ 0x026b, 0x026b, 0x29f7, CanonicalizeRangeLo },
{ 0x026c, 0x026c, 0xa541, CanonicalizeRangeLo },
{ 0x026d, 0x026e, 0x0000, CanonicalizeUnique },
@@ -206,7 +222,8 @@ const CanonicalizationRange ucs2RangeInfo[UCS2_CANONICALIZATION_RANGES] = {
{ 0x028c, 0x028c, 0x0047, CanonicalizeRangeHi },
{ 0x028d, 0x0291, 0x0000, CanonicalizeUnique },
{ 0x0292, 0x0292, 0x00db, CanonicalizeRangeHi },
- { 0x0293, 0x029d, 0x0000, CanonicalizeUnique },
+ { 0x0293, 0x029c, 0x0000, CanonicalizeUnique },
+ { 0x029d, 0x029d, 0xa515, CanonicalizeRangeLo },
{ 0x029e, 0x029e, 0xa512, CanonicalizeRangeLo },
{ 0x029f, 0x0344, 0x0000, CanonicalizeUnique },
{ 0x0345, 0x0345, 0x0007, CanonicalizeSet },
@@ -288,10 +305,34 @@ const CanonicalizationRange ucs2RangeInfo[UCS2_CANONICALIZATION_RANGES] = {
{ 0x03fc, 0x03fc, 0x0000, CanonicalizeUnique },
{ 0x03fd, 0x03ff, 0x0082, CanonicalizeRangeHi },
{ 0x0400, 0x040f, 0x0050, CanonicalizeRangeLo },
- { 0x0410, 0x042f, 0x0020, CanonicalizeRangeLo },
- { 0x0430, 0x044f, 0x0020, CanonicalizeRangeHi },
+ { 0x0410, 0x0411, 0x0020, CanonicalizeRangeLo },
+ { 0x0412, 0x0412, 0x000e, CanonicalizeSet },
+ { 0x0413, 0x0413, 0x0020, CanonicalizeRangeLo },
+ { 0x0414, 0x0414, 0x000f, CanonicalizeSet },
+ { 0x0415, 0x041d, 0x0020, CanonicalizeRangeLo },
+ { 0x041e, 0x041e, 0x0010, CanonicalizeSet },
+ { 0x041f, 0x0420, 0x0020, CanonicalizeRangeLo },
+ { 0x0421, 0x0421, 0x0011, CanonicalizeSet },
+ { 0x0422, 0x0422, 0x0012, CanonicalizeSet },
+ { 0x0423, 0x0429, 0x0020, CanonicalizeRangeLo },
+ { 0x042a, 0x042a, 0x0013, CanonicalizeSet },
+ { 0x042b, 0x042f, 0x0020, CanonicalizeRangeLo },
+ { 0x0430, 0x0431, 0x0020, CanonicalizeRangeHi },
+ { 0x0432, 0x0432, 0x000e, CanonicalizeSet },
+ { 0x0433, 0x0433, 0x0020, CanonicalizeRangeHi },
+ { 0x0434, 0x0434, 0x000f, CanonicalizeSet },
+ { 0x0435, 0x043d, 0x0020, CanonicalizeRangeHi },
+ { 0x043e, 0x043e, 0x0010, CanonicalizeSet },
+ { 0x043f, 0x0440, 0x0020, CanonicalizeRangeHi },
+ { 0x0441, 0x0441, 0x0011, CanonicalizeSet },
+ { 0x0442, 0x0442, 0x0012, CanonicalizeSet },
+ { 0x0443, 0x0449, 0x0020, CanonicalizeRangeHi },
+ { 0x044a, 0x044a, 0x0013, CanonicalizeSet },
+ { 0x044b, 0x044f, 0x0020, CanonicalizeRangeHi },
{ 0x0450, 0x045f, 0x0050, CanonicalizeRangeHi },
- { 0x0460, 0x0481, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0460, 0x0461, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0462, 0x0463, 0x0014, CanonicalizeSet },
+ { 0x0464, 0x0481, 0x0000, CanonicalizeAlternatingAligned },
{ 0x0482, 0x0489, 0x0000, CanonicalizeUnique },
{ 0x048a, 0x04bf, 0x0000, CanonicalizeAlternatingAligned },
{ 0x04c0, 0x04c0, 0x000f, CanonicalizeRangeLo },
@@ -308,16 +349,38 @@ const CanonicalizationRange ucs2RangeInfo[UCS2_CANONICALIZATION_RANGES] = {
{ 0x10c7, 0x10c7, 0x1c60, CanonicalizeRangeLo },
{ 0x10c8, 0x10cc, 0x0000, CanonicalizeUnique },
{ 0x10cd, 0x10cd, 0x1c60, CanonicalizeRangeLo },
- { 0x10ce, 0x1d78, 0x0000, CanonicalizeUnique },
+ { 0x10ce, 0x10cf, 0x0000, CanonicalizeUnique },
+ { 0x10d0, 0x10fa, 0x0bc0, CanonicalizeRangeLo },
+ { 0x10fb, 0x10fc, 0x0000, CanonicalizeUnique },
+ { 0x10fd, 0x10ff, 0x0bc0, CanonicalizeRangeLo },
+ { 0x1100, 0x139f, 0x0000, CanonicalizeUnique },
+ { 0x13a0, 0x13ef, 0x97d0, CanonicalizeRangeLo },
+ { 0x13f0, 0x13f5, 0x0008, CanonicalizeRangeLo },
+ { 0x13f6, 0x13f7, 0x0000, CanonicalizeUnique },
+ { 0x13f8, 0x13fd, 0x0008, CanonicalizeRangeHi },
+ { 0x13fe, 0x1c7f, 0x0000, CanonicalizeUnique },
+ { 0x1c80, 0x1c80, 0x000e, CanonicalizeSet },
+ { 0x1c81, 0x1c81, 0x000f, CanonicalizeSet },
+ { 0x1c82, 0x1c82, 0x0010, CanonicalizeSet },
+ { 0x1c83, 0x1c83, 0x0011, CanonicalizeSet },
+ { 0x1c84, 0x1c85, 0x0012, CanonicalizeSet },
+ { 0x1c86, 0x1c86, 0x0013, CanonicalizeSet },
+ { 0x1c87, 0x1c87, 0x0014, CanonicalizeSet },
+ { 0x1c88, 0x1c88, 0x0016, CanonicalizeSet },
+ { 0x1c89, 0x1c8f, 0x0000, CanonicalizeUnique },
+ { 0x1c90, 0x1cba, 0x0bc0, CanonicalizeRangeHi },
+ { 0x1cbb, 0x1cbc, 0x0000, CanonicalizeUnique },
+ { 0x1cbd, 0x1cbf, 0x0bc0, CanonicalizeRangeHi },
+ { 0x1cc0, 0x1d78, 0x0000, CanonicalizeUnique },
{ 0x1d79, 0x1d79, 0x8a04, CanonicalizeRangeLo },
{ 0x1d7a, 0x1d7c, 0x0000, CanonicalizeUnique },
{ 0x1d7d, 0x1d7d, 0x0ee6, CanonicalizeRangeLo },
{ 0x1d7e, 0x1dff, 0x0000, CanonicalizeUnique },
{ 0x1e00, 0x1e5f, 0x0000, CanonicalizeAlternatingAligned },
- { 0x1e60, 0x1e61, 0x000e, CanonicalizeSet },
+ { 0x1e60, 0x1e61, 0x0015, CanonicalizeSet },
{ 0x1e62, 0x1e95, 0x0000, CanonicalizeAlternatingAligned },
{ 0x1e96, 0x1e9a, 0x0000, CanonicalizeUnique },
- { 0x1e9b, 0x1e9b, 0x000e, CanonicalizeSet },
+ { 0x1e9b, 0x1e9b, 0x0015, CanonicalizeSet },
{ 0x1e9c, 0x1e9f, 0x0000, CanonicalizeUnique },
{ 0x1ea0, 0x1eff, 0x0000, CanonicalizeAlternatingAligned },
{ 0x1f00, 0x1f07, 0x0008, CanonicalizeRangeLo },
@@ -428,7 +491,9 @@ const CanonicalizationRange ucs2RangeInfo[UCS2_CANONICALIZATION_RANGES] = {
{ 0x2d28, 0x2d2c, 0x0000, CanonicalizeUnique },
{ 0x2d2d, 0x2d2d, 0x1c60, CanonicalizeRangeHi },
{ 0x2d2e, 0xa63f, 0x0000, CanonicalizeUnique },
- { 0xa640, 0xa66d, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa640, 0xa649, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa64a, 0xa64b, 0x0016, CanonicalizeSet },
+ { 0xa64c, 0xa66d, 0x0000, CanonicalizeAlternatingAligned },
{ 0xa66e, 0xa67f, 0x0000, CanonicalizeUnique },
{ 0xa680, 0xa69b, 0x0000, CanonicalizeAlternatingAligned },
{ 0xa69c, 0xa721, 0x0000, CanonicalizeUnique },
@@ -450,15 +515,42 @@ const CanonicalizationRange ucs2RangeInfo[UCS2_CANONICALIZATION_RANGES] = {
{ 0xa7ab, 0xa7ab, 0xa54f, CanonicalizeRangeHi },
{ 0xa7ac, 0xa7ac, 0xa54b, CanonicalizeRangeHi },
{ 0xa7ad, 0xa7ad, 0xa541, CanonicalizeRangeHi },
- { 0xa7ae, 0xa7af, 0x0000, CanonicalizeUnique },
+ { 0xa7ae, 0xa7ae, 0xa544, CanonicalizeRangeHi },
+ { 0xa7af, 0xa7af, 0x0000, CanonicalizeUnique },
{ 0xa7b0, 0xa7b0, 0xa512, CanonicalizeRangeHi },
{ 0xa7b1, 0xa7b1, 0xa52a, CanonicalizeRangeHi },
- { 0xa7b2, 0xff20, 0x0000, CanonicalizeUnique },
+ { 0xa7b2, 0xa7b2, 0xa515, CanonicalizeRangeHi },
+ { 0xa7b3, 0xa7b3, 0x03a0, CanonicalizeRangeLo },
+ { 0xa7b4, 0xa7b9, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa7ba, 0xab52, 0x0000, CanonicalizeUnique },
+ { 0xab53, 0xab53, 0x03a0, CanonicalizeRangeHi },
+ { 0xab54, 0xab6f, 0x0000, CanonicalizeUnique },
+ { 0xab70, 0xabbf, 0x97d0, CanonicalizeRangeHi },
+ { 0xabc0, 0xff20, 0x0000, CanonicalizeUnique },
{ 0xff21, 0xff3a, 0x0020, CanonicalizeRangeLo },
{ 0xff3b, 0xff40, 0x0000, CanonicalizeUnique },
{ 0xff41, 0xff5a, 0x0020, CanonicalizeRangeHi },
{ 0xff5b, 0xffff, 0x0000, CanonicalizeUnique },
};
+const uint16_t canonicalTableLChar[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0x39c, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x178
+};
+
} } // JSC::Yarr
diff --git a/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js b/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js
index dc578cfece..b92d8bdd4f 100644
--- a/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js
+++ b/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -27,7 +27,7 @@ function printHeader()
{
var copyright = (
"/*" + "\n" +
- " * Copyright (C) 2012-2013, 2015-2016 Apple Inc. All rights reserved." + "\n" +
+ " * Copyright (C) 2012-2018 Apple Inc. All rights reserved." + "\n" +
" *" + "\n" +
" * Redistribution and use in source and binary forms, with or without" + "\n" +
" * modification, are permitted provided that the following conditions" + "\n" +
@@ -183,6 +183,23 @@ function createTables(prefix, maxValue, canonicalGroups)
}
print("};");
print();
+ // Create canonical table for LChar domain
+ let line = "const uint16_t canonicalTableLChar[256] = {";
+ for (let i = 0; i < 256; i++) {
+ if (!(i % 16)) {
+ print(line);
+ line = " ";
+ }
+ let canonicalChar = canonicalize(i);
+ line = line + (canonicalChar < 16 ? "0x0" : "0x") + canonicalChar.toString(16);
+ if ((i % 16) != 15)
+ line += ", ";
+ else if (i != 255)
+ line += ",";
+ }
+ print(line);
+ print("};");
+ print();
}
printHeader();
diff --git a/src/3rdparty/masm/yarr/YarrErrorCode.h b/src/3rdparty/masm/yarr/YarrErrorCode.h
index 48f2bb7900..3f06a6bff1 100644
--- a/src/3rdparty/masm/yarr/YarrErrorCode.h
+++ b/src/3rdparty/masm/yarr/YarrErrorCode.h
@@ -60,6 +60,13 @@ inline bool hasError(ErrorCode errorCode)
{
return errorCode != ErrorCode::NoError;
}
+
+inline bool hasHardError(ErrorCode errorCode)
+{
+ // TooManyDisjunctions means that we ran out stack compiling.
+ // All other errors are due to problems in the expression.
+ return hasError(errorCode) && errorCode != ErrorCode::TooManyDisjunctions;
+}
JS_EXPORT_PRIVATE JSObject* errorToThrow(ExecState*, ErrorCode);
} } // namespace JSC::Yarr
diff --git a/src/3rdparty/masm/yarr/YarrInterpreter.cpp b/src/3rdparty/masm/yarr/YarrInterpreter.cpp
index 4d3652fcbc..cdcd16af64 100644
--- a/src/3rdparty/masm/yarr/YarrInterpreter.cpp
+++ b/src/3rdparty/masm/yarr/YarrInterpreter.cpp
@@ -32,12 +32,12 @@
#include "Yarr.h"
#include "YarrCanonicalize.h"
#include <wtf/BumpPointerAllocator.h>
+#include <wtf/CheckedArithmetic.h>
#include <wtf/DataLog.h>
+#include <wtf/StdLibExtras.h>
#include <wtf/text/CString.h>
#include <wtf/text/WTFString.h>
-using namespace WTF;
-
namespace JSC { namespace Yarr {
template<typename CharType>
@@ -67,17 +67,23 @@ public:
struct DisjunctionContext
{
- DisjunctionContext()
- : term(0)
- {
- }
+ DisjunctionContext() = default;
void* operator new(size_t, void* where)
{
return where;
}
- int term;
+ static size_t allocationSize(unsigned numberOfFrames)
+ {
+ static_assert(alignof(DisjunctionContext) <= sizeof(void*), "");
+ size_t rawSize = (sizeof(DisjunctionContext) - sizeof(uintptr_t) + Checked<size_t>(numberOfFrames) * sizeof(uintptr_t)).unsafeGet();
+ size_t roundedSize = WTF::roundUpToMultipleOf<sizeof(void*)>(rawSize);
+ RELEASE_ASSERT(roundedSize >= rawSize);
+ return roundedSize;
+ }
+
+ int term { 0 };
unsigned matchBegin;
unsigned matchEnd;
uintptr_t frame[1];
@@ -85,7 +91,7 @@ public:
DisjunctionContext* allocDisjunctionContext(ByteDisjunction* disjunction)
{
- size_t size = sizeof(DisjunctionContext) - sizeof(uintptr_t) + disjunction->m_frameSize * sizeof(uintptr_t);
+ size_t size = DisjunctionContext::allocationSize(disjunction->m_frameSize);
allocatorPool = allocatorPool->ensureCapacity(size);
RELEASE_ASSERT(allocatorPool);
return new (allocatorPool->alloc(size)) DisjunctionContext();
@@ -99,7 +105,6 @@ public:
struct ParenthesesDisjunctionContext
{
ParenthesesDisjunctionContext(unsigned* output, ByteTerm& term)
- : next(0)
{
unsigned firstSubpatternId = term.atom.subpatternId;
unsigned numNestedSubpatterns = term.atom.parenthesesDisjunction->m_numSubpatterns;
@@ -125,16 +130,25 @@ public:
DisjunctionContext* getDisjunctionContext(ByteTerm& term)
{
- return reinterpret_cast<DisjunctionContext*>(&(subpatternBackup[term.atom.parenthesesDisjunction->m_numSubpatterns << 1]));
+ return bitwise_cast<DisjunctionContext*>(bitwise_cast<uintptr_t>(this) + allocationSize(term.atom.parenthesesDisjunction->m_numSubpatterns));
}
- ParenthesesDisjunctionContext* next;
+ static size_t allocationSize(unsigned numberOfSubpatterns)
+ {
+ static_assert(alignof(ParenthesesDisjunctionContext) <= sizeof(void*), "");
+ size_t rawSize = (sizeof(ParenthesesDisjunctionContext) - sizeof(unsigned) + (Checked<size_t>(numberOfSubpatterns) * 2U) * sizeof(unsigned)).unsafeGet();
+ size_t roundedSize = WTF::roundUpToMultipleOf<sizeof(void*)>(rawSize);
+ RELEASE_ASSERT(roundedSize >= rawSize);
+ return roundedSize;
+ }
+
+ ParenthesesDisjunctionContext* next { nullptr };
unsigned subpatternBackup[1];
};
ParenthesesDisjunctionContext* allocParenthesesDisjunctionContext(ByteDisjunction* disjunction, unsigned* output, ByteTerm& term)
{
- size_t size = sizeof(ParenthesesDisjunctionContext) - sizeof(unsigned) + (term.atom.parenthesesDisjunction->m_numSubpatterns << 1) * sizeof(unsigned) + sizeof(DisjunctionContext) - sizeof(uintptr_t) + static_cast<size_t>(disjunction->m_frameSize) * sizeof(uintptr_t);
+ size_t size = (Checked<size_t>(ParenthesesDisjunctionContext::allocationSize(term.atom.parenthesesDisjunction->m_numSubpatterns)) + DisjunctionContext::allocationSize(disjunction->m_frameSize)).unsafeGet();
allocatorPool = allocatorPool->ensureCapacity(size);
RELEASE_ASSERT(allocatorPool);
return new (allocatorPool->alloc(size)) ParenthesesDisjunctionContext(output, term);
@@ -1630,7 +1644,6 @@ public:
, unicode(pattern->unicode())
, output(output)
, input(input, start, length, pattern->unicode())
- , allocatorPool(0)
, startOffset(start)
, remainingMatchCount(matchLimit)
{
@@ -1641,7 +1654,7 @@ private:
bool unicode;
unsigned* output;
InputStream input;
- BumpPointerPool* allocatorPool;
+ WTF::BumpPointerPool* allocatorPool { nullptr };
unsigned startOffset;
unsigned remainingMatchCount;
};
@@ -1740,7 +1753,7 @@ public:
void atomParenthesesOnceBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation)
{
- unsigned beginTerm = m_bodyDisjunction->terms.size();
+ int beginTerm = m_bodyDisjunction->terms.size();
m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternOnceBegin, subpatternId, capture, false, inputPosition));
m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation;
diff --git a/src/3rdparty/masm/yarr/YarrJIT.cpp b/src/3rdparty/masm/yarr/YarrJIT.cpp
index da65b772f7..1c8138c66e 100644
--- a/src/3rdparty/masm/yarr/YarrJIT.cpp
+++ b/src/3rdparty/masm/yarr/YarrJIT.cpp
@@ -37,15 +37,12 @@
#if ENABLE(YARR_JIT)
-using namespace WTF;
-
namespace JSC { namespace Yarr {
template<YarrJITCompileMode compileMode>
class YarrGenerator : private DefaultMacroAssembler {
- friend void jitCompile(VM*, YarrCodeBlock&, const String& pattern, unsigned& numSubpatterns, const char*& error, bool ignoreCase, bool multiline);
-#if CPU(ARM)
+#if CPU(ARM_THUMB2)
static const RegisterID input = ARMRegisters::r0;
static const RegisterID index = ARMRegisters::r1;
static const RegisterID length = ARMRegisters::r2;
@@ -477,6 +474,12 @@ class YarrGenerator : private DefaultMacroAssembler {
return branch32(BelowOrEqual, index, length);
}
+ Jump checkNotEnoughInput(RegisterID additionalAmount)
+ {
+ add32(index, additionalAmount);
+ return branch32(Above, additionalAmount, length);
+ }
+
Jump checkInput()
{
return branch32(BelowOrEqual, index, length);
@@ -559,6 +562,16 @@ class YarrGenerator : private DefaultMacroAssembler {
}
#endif
+ void readCharacterDontDecodeSurrogates(Checked<unsigned> negativeCharacterOffset, RegisterID resultReg, RegisterID indexReg = index)
+ {
+ BaseIndex address = negativeOffsetIndexedAddress(negativeCharacterOffset, resultReg, indexReg);
+
+ if (m_charSize == Char8)
+ load8(address, resultReg);
+ else
+ load16Unaligned(address, resultReg);
+ }
+
void readCharacter(Checked<unsigned> negativeCharacterOffset, RegisterID resultReg, RegisterID indexReg = index)
{
BaseIndex address = negativeOffsetIndexedAddress(negativeCharacterOffset, resultReg, indexReg);
@@ -809,16 +822,16 @@ class YarrGenerator : private DefaultMacroAssembler {
// The operation, as a YarrOpCode, and also a reference to the PatternTerm.
YarrOpCode m_op;
- PatternTerm* m_term;
+ PatternTerm* m_term = nullptr;
// For alternatives, this holds the PatternAlternative and doubly linked
// references to this alternative's siblings. In the case of the
// OpBodyAlternativeEnd node at the end of a section of repeating nodes,
// m_nextOp will reference the OpBodyAlternativeBegin node of the first
// repeating alternative.
- PatternAlternative* m_alternative;
- size_t m_previousOp;
- size_t m_nextOp;
+ PatternAlternative* m_alternative = nullptr;
+ size_t m_previousOp = 0;
+ size_t m_nextOp = 0;
// Used to record a set of Jumps out of the generated code, typically
// used for jumps out to backtracking code, and a single reentry back
@@ -1119,6 +1132,228 @@ class YarrGenerator : private DefaultMacroAssembler {
backtrackTermDefault(opIndex);
}
+#if ENABLE(YARR_JIT_BACKREFERENCES)
+ void matchBackreference(size_t opIndex, JumpList& characterMatchFails, RegisterID character, RegisterID patternIndex, RegisterID patternCharacter)
+ {
+ YarrOp& op = m_ops[opIndex];
+ PatternTerm* term = op.m_term;
+ unsigned subpatternId = term->backReferenceSubpatternId;
+
+ Label loop(this);
+
+ readCharacterDontDecodeSurrogates(0, patternCharacter, patternIndex);
+ readCharacterDontDecodeSurrogates(m_checkedOffset - term->inputPosition, character);
+
+ if (!m_pattern.ignoreCase())
+ characterMatchFails.append(branch32(NotEqual, character, patternCharacter));
+ else {
+ Jump charactersMatch = branch32(Equal, character, patternCharacter);
+ ExtendedAddress characterTableEntry(character, reinterpret_cast<intptr_t>(&canonicalTableLChar));
+ load16(characterTableEntry, character);
+ ExtendedAddress patternTableEntry(patternCharacter, reinterpret_cast<intptr_t>(&canonicalTableLChar));
+ load16(patternTableEntry, patternCharacter);
+ characterMatchFails.append(branch32(NotEqual, character, patternCharacter));
+ charactersMatch.link(this);
+ }
+
+
+ add32(TrustedImm32(1), index);
+ add32(TrustedImm32(1), patternIndex);
+
+ branch32(NotEqual, patternIndex, Address(output, ((subpatternId << 1) + 1) * sizeof(int))).linkTo(loop, this);
+ }
+
+ void generateBackReference(size_t opIndex)
+ {
+ YarrOp& op = m_ops[opIndex];
+ PatternTerm* term = op.m_term;
+
+ if (m_pattern.ignoreCase() && m_charSize != Char8) {
+ m_failureReason = JITFailureReason::BackReference;
+ return;
+ }
+
+ unsigned subpatternId = term->backReferenceSubpatternId;
+ unsigned parenthesesFrameLocation = term->frameLocation;
+
+ const RegisterID characterOrTemp = regT0;
+ const RegisterID patternIndex = regT1;
+ const RegisterID patternTemp = regT2;
+
+ storeToFrame(index, parenthesesFrameLocation + BackTrackInfoBackReference::beginIndex());
+ if (term->quantityType != QuantifierFixedCount || term->quantityMaxCount != 1)
+ storeToFrame(TrustedImm32(0), parenthesesFrameLocation + BackTrackInfoBackReference::matchAmountIndex());
+
+ JumpList matches;
+
+ if (term->quantityType != QuantifierNonGreedy) {
+ load32(Address(output, (subpatternId << 1) * sizeof(int)), patternIndex);
+ load32(Address(output, ((subpatternId << 1) + 1) * sizeof(int)), patternTemp);
+
+ // An empty match is successful without consuming characters
+ if (term->quantityType != QuantifierFixedCount || term->quantityMaxCount != 1) {
+ matches.append(branch32(Equal, TrustedImm32(-1), patternIndex));
+ matches.append(branch32(Equal, patternIndex, patternTemp));
+ } else {
+ Jump zeroLengthMatch = branch32(Equal, TrustedImm32(-1), patternIndex);
+ Jump tryNonZeroMatch = branch32(NotEqual, patternIndex, patternTemp);
+ zeroLengthMatch.link(this);
+ storeToFrame(TrustedImm32(1), parenthesesFrameLocation + BackTrackInfoBackReference::matchAmountIndex());
+ matches.append(jump());
+ tryNonZeroMatch.link(this);
+ }
+ }
+
+ switch (term->quantityType) {
+ case QuantifierFixedCount: {
+ Label outerLoop(this);
+
+ // PatternTemp should contain pattern end index at this point
+ sub32(patternIndex, patternTemp);
+ if (m_checkedOffset - term->inputPosition)
+ sub32(Imm32((m_checkedOffset - term->inputPosition).unsafeGet()), patternTemp);
+ op.m_jumps.append(checkNotEnoughInput(patternTemp));
+
+ matchBackreference(opIndex, op.m_jumps, characterOrTemp, patternIndex, patternTemp);
+
+ if (term->quantityMaxCount != 1) {
+ loadFromFrame(parenthesesFrameLocation + BackTrackInfoBackReference::matchAmountIndex(), characterOrTemp);
+ add32(TrustedImm32(1), characterOrTemp);
+ storeToFrame(characterOrTemp, parenthesesFrameLocation + BackTrackInfoBackReference::matchAmountIndex());
+ matches.append(branch32(Equal, Imm32(term->quantityMaxCount.unsafeGet()), characterOrTemp));
+ load32(Address(output, (subpatternId << 1) * sizeof(int)), patternIndex);
+ load32(Address(output, ((subpatternId << 1) + 1) * sizeof(int)), patternTemp);
+ jump(outerLoop);
+ }
+ matches.link(this);
+ break;
+ }
+
+ case QuantifierGreedy: {
+ JumpList incompleteMatches;
+
+ Label outerLoop(this);
+
+ // PatternTemp should contain pattern end index at this point
+ sub32(patternIndex, patternTemp);
+ if (m_checkedOffset - term->inputPosition)
+ sub32(Imm32((m_checkedOffset - term->inputPosition).unsafeGet()), patternTemp);
+ matches.append(checkNotEnoughInput(patternTemp));
+
+ matchBackreference(opIndex, incompleteMatches, characterOrTemp, patternIndex, patternTemp);
+
+ loadFromFrame(parenthesesFrameLocation + BackTrackInfoBackReference::matchAmountIndex(), characterOrTemp);
+ add32(TrustedImm32(1), characterOrTemp);
+ storeToFrame(characterOrTemp, parenthesesFrameLocation + BackTrackInfoBackReference::matchAmountIndex());
+ if (term->quantityMaxCount != quantifyInfinite)
+ matches.append(branch32(Equal, Imm32(term->quantityMaxCount.unsafeGet()), characterOrTemp));
+ load32(Address(output, (subpatternId << 1) * sizeof(int)), patternIndex);
+ load32(Address(output, ((subpatternId << 1) + 1) * sizeof(int)), patternTemp);
+
+ // Store current index in frame for restoring after a partial match
+ storeToFrame(index, parenthesesFrameLocation + BackTrackInfoBackReference::beginIndex());
+ jump(outerLoop);
+
+ incompleteMatches.link(this);
+ loadFromFrame(parenthesesFrameLocation + BackTrackInfoBackReference::beginIndex(), index);
+
+ matches.link(this);
+ op.m_reentry = label();
+ break;
+ }
+
+ case QuantifierNonGreedy: {
+ JumpList incompleteMatches;
+
+ matches.append(jump());
+
+ op.m_reentry = label();
+
+ load32(Address(output, (subpatternId << 1) * sizeof(int)), patternIndex);
+ load32(Address(output, ((subpatternId << 1) + 1) * sizeof(int)), patternTemp);
+
+ // An empty match is successful without consuming characters
+ Jump zeroLengthMatch = branch32(Equal, TrustedImm32(-1), patternIndex);
+ Jump tryNonZeroMatch = branch32(NotEqual, patternIndex, patternTemp);
+ zeroLengthMatch.link(this);
+ storeToFrame(TrustedImm32(1), parenthesesFrameLocation + BackTrackInfoBackReference::matchAmountIndex());
+ matches.append(jump());
+ tryNonZeroMatch.link(this);
+
+ // Check if we have input remaining to match
+ sub32(patternIndex, patternTemp);
+ if (m_checkedOffset - term->inputPosition)
+ sub32(Imm32((m_checkedOffset - term->inputPosition).unsafeGet()), patternTemp);
+ matches.append(checkNotEnoughInput(patternTemp));
+
+ storeToFrame(index, parenthesesFrameLocation + BackTrackInfoBackReference::beginIndex());
+
+ matchBackreference(opIndex, incompleteMatches, characterOrTemp, patternIndex, patternTemp);
+
+ matches.append(jump());
+
+ incompleteMatches.link(this);
+ loadFromFrame(parenthesesFrameLocation + BackTrackInfoBackReference::beginIndex(), index);
+
+ matches.link(this);
+ break;
+ }
+ }
+ }
+ void backtrackBackReference(size_t opIndex)
+ {
+ YarrOp& op = m_ops[opIndex];
+ PatternTerm* term = op.m_term;
+
+ unsigned subpatternId = term->backReferenceSubpatternId;
+
+ m_backtrackingState.link(this);
+ op.m_jumps.link(this);
+
+ JumpList failures;
+
+ unsigned parenthesesFrameLocation = term->frameLocation;
+ switch (term->quantityType) {
+ case QuantifierFixedCount:
+ loadFromFrame(parenthesesFrameLocation + BackTrackInfoBackReference::beginIndex(), index);
+ break;
+
+ case QuantifierGreedy: {
+ const RegisterID matchAmount = regT0;
+ const RegisterID patternStartIndex = regT1;
+ const RegisterID patternEndIndexOrLen = regT2;
+
+ loadFromFrame(parenthesesFrameLocation + BackTrackInfoBackReference::matchAmountIndex(), matchAmount);
+ failures.append(branchTest32(Zero, matchAmount));
+
+ load32(Address(output, (subpatternId << 1) * sizeof(int)), patternStartIndex);
+ load32(Address(output, ((subpatternId << 1) + 1) * sizeof(int)), patternEndIndexOrLen);
+ sub32(patternStartIndex, patternEndIndexOrLen);
+ sub32(patternEndIndexOrLen, index);
+
+ sub32(TrustedImm32(1), matchAmount);
+ storeToFrame(matchAmount, parenthesesFrameLocation + BackTrackInfoBackReference::matchAmountIndex());
+ jump(op.m_reentry);
+ break;
+ }
+
+ case QuantifierNonGreedy: {
+ const RegisterID matchAmount = regT0;
+
+ loadFromFrame(parenthesesFrameLocation + BackTrackInfoBackReference::matchAmountIndex(), matchAmount);
+ if (term->quantityMaxCount != quantifyInfinite)
+ failures.append(branch32(AboveOrEqual, Imm32(term->quantityMaxCount.unsafeGet()), matchAmount));
+ add32(TrustedImm32(1), matchAmount);
+ storeToFrame(matchAmount, parenthesesFrameLocation + BackTrackInfoBackReference::matchAmountIndex());
+ jump(op.m_reentry);
+ break;
+ }
+ }
+ failures.link(this);
+ m_backtrackingState.fallthrough();
+ }
+#endif
+
void generatePatternCharacterOnce(size_t opIndex)
{
YarrOp& op = m_ops[opIndex];
@@ -1141,12 +1376,16 @@ class YarrGenerator : private DefaultMacroAssembler {
}
const RegisterID character = regT0;
+#if CPU(X86_64) || CPU(ARM64)
+ unsigned maxCharactersAtOnce = m_charSize == Char8 ? 8 : 4;
+#else
unsigned maxCharactersAtOnce = m_charSize == Char8 ? 4 : 2;
- unsigned ignoreCaseMask = 0;
+#endif
+ uint64_t ignoreCaseMask = 0;
#if CPU(BIG_ENDIAN)
- int allCharacters = ch << (m_charSize == Char8 ? 24 : 16);
+ uint64_t allCharacters = ch << (m_charSize == Char8 ? 24 : 16);
#else
- int allCharacters = ch;
+ uint64_t allCharacters = ch;
#endif
unsigned numberCharacters;
unsigned startTermPosition = term->inputPosition;
@@ -1155,16 +1394,19 @@ class YarrGenerator : private DefaultMacroAssembler {
// upper & lower case representations are converted to a character class.
ASSERT(!m_pattern.ignoreCase() || isASCIIAlpha(ch) || isCanonicallyUnique(ch, m_canonicalMode));
- if (m_pattern.ignoreCase() && isASCIIAlpha(ch))
+ if (m_pattern.ignoreCase() && isASCIIAlpha(ch)) {
#if CPU(BIG_ENDIAN)
ignoreCaseMask |= 32 << (m_charSize == Char8 ? 24 : 16);
#else
ignoreCaseMask |= 32;
#endif
+ }
for (numberCharacters = 1; numberCharacters < maxCharactersAtOnce && nextOp->m_op == OpTerm; ++numberCharacters, nextOp = &m_ops[opIndex + numberCharacters]) {
PatternTerm* nextTerm = nextOp->m_term;
-
+
+ // YarrJIT handles decoded surrogate pair as one character if unicode flag is enabled.
+ // Note that the numberCharacters become 1 while the width of the pattern character becomes 32bit in this case.
if (nextTerm->type != PatternTerm::TypePatternCharacter
|| nextTerm->quantityType != QuantifierFixedCount
|| nextTerm->quantityMaxCount != 1
@@ -1192,49 +1434,132 @@ class YarrGenerator : private DefaultMacroAssembler {
// upper & lower case representations are converted to a character class.
ASSERT(!m_pattern.ignoreCase() || isASCIIAlpha(currentCharacter) || isCanonicallyUnique(currentCharacter, m_canonicalMode));
- allCharacters |= (currentCharacter << shiftAmount);
+ allCharacters |= (static_cast<uint64_t>(currentCharacter) << shiftAmount);
if ((m_pattern.ignoreCase()) && (isASCIIAlpha(currentCharacter)))
- ignoreCaseMask |= 32 << shiftAmount;
+ ignoreCaseMask |= 32ULL << shiftAmount;
}
+ if (m_decodeSurrogatePairs)
+ op.m_jumps.append(jumpIfNoAvailableInput());
+
if (m_charSize == Char8) {
+ auto check1 = [&] (Checked<unsigned> offset, UChar32 characters) {
+ op.m_jumps.append(jumpIfCharNotEquals(characters, offset, character));
+ };
+
+ auto check2 = [&] (Checked<unsigned> offset, uint16_t characters, uint16_t mask) {
+ load16Unaligned(negativeOffsetIndexedAddress(offset, character), character);
+ if (mask)
+ or32(Imm32(mask), character);
+ op.m_jumps.append(branch32(NotEqual, character, Imm32(characters | mask)));
+ };
+
+ auto check4 = [&] (Checked<unsigned> offset, unsigned characters, unsigned mask) {
+ if (mask) {
+ load32WithUnalignedHalfWords(negativeOffsetIndexedAddress(offset, character), character);
+ if (mask)
+ or32(Imm32(mask), character);
+ op.m_jumps.append(branch32(NotEqual, character, Imm32(characters | mask)));
+ return;
+ }
+ op.m_jumps.append(branch32WithUnalignedHalfWords(NotEqual, negativeOffsetIndexedAddress(offset, character), TrustedImm32(characters)));
+ };
+
+#if CPU(X86_64) || CPU(ARM64)
+ auto check8 = [&] (Checked<unsigned> offset, uint64_t characters, uint64_t mask) {
+ load64(negativeOffsetIndexedAddress(offset, character), character);
+ if (mask)
+ or64(TrustedImm64(mask), character);
+ op.m_jumps.append(branch64(NotEqual, character, TrustedImm64(characters | mask)));
+ };
+#endif
+
switch (numberCharacters) {
case 1:
- op.m_jumps.append(jumpIfCharNotEquals(ch, m_checkedOffset - startTermPosition, character));
+ // Use 32bit width of allCharacters since Yarr counts surrogate pairs as one character with unicode flag.
+ check1(m_checkedOffset - startTermPosition, allCharacters & 0xffffffff);
return;
case 2: {
- load16Unaligned(negativeOffsetIndexedAddress(m_checkedOffset - startTermPosition, character), character);
- break;
+ check2(m_checkedOffset - startTermPosition, allCharacters & 0xffff, ignoreCaseMask & 0xffff);
+ return;
}
case 3: {
- load16Unaligned(negativeOffsetIndexedAddress(m_checkedOffset - startTermPosition, character), character);
- if (ignoreCaseMask)
- or32(Imm32(ignoreCaseMask), character);
- op.m_jumps.append(branch32(NotEqual, character, Imm32((allCharacters & 0xffff) | ignoreCaseMask)));
- op.m_jumps.append(jumpIfCharNotEquals(allCharacters >> 16, m_checkedOffset - startTermPosition - 2, character));
+ check2(m_checkedOffset - startTermPosition, allCharacters & 0xffff, ignoreCaseMask & 0xffff);
+ check1(m_checkedOffset - startTermPosition - 2, (allCharacters >> 16) & 0xff);
return;
}
case 4: {
- load32WithUnalignedHalfWords(negativeOffsetIndexedAddress(m_checkedOffset- startTermPosition, character), character);
- break;
+ check4(m_checkedOffset - startTermPosition, allCharacters & 0xffffffff, ignoreCaseMask & 0xffffffff);
+ return;
+ }
+#if CPU(X86_64) || CPU(ARM64)
+ case 5: {
+ check4(m_checkedOffset - startTermPosition, allCharacters & 0xffffffff, ignoreCaseMask & 0xffffffff);
+ check1(m_checkedOffset - startTermPosition - 4, (allCharacters >> 32) & 0xff);
+ return;
+ }
+ case 6: {
+ check4(m_checkedOffset - startTermPosition, allCharacters & 0xffffffff, ignoreCaseMask & 0xffffffff);
+ check2(m_checkedOffset - startTermPosition - 4, (allCharacters >> 32) & 0xffff, (ignoreCaseMask >> 32) & 0xffff);
+ return;
+ }
+ case 7: {
+ check4(m_checkedOffset - startTermPosition, allCharacters & 0xffffffff, ignoreCaseMask & 0xffffffff);
+ check2(m_checkedOffset - startTermPosition - 4, (allCharacters >> 32) & 0xffff, (ignoreCaseMask >> 32) & 0xffff);
+ check1(m_checkedOffset - startTermPosition - 6, (allCharacters >> 48) & 0xff);
+ return;
+ }
+ case 8: {
+ check8(m_checkedOffset - startTermPosition, allCharacters, ignoreCaseMask);
+ return;
}
+#endif
}
} else {
+ auto check1 = [&] (Checked<unsigned> offset, UChar32 characters) {
+ op.m_jumps.append(jumpIfCharNotEquals(characters, offset, character));
+ };
+
+ auto check2 = [&] (Checked<unsigned> offset, unsigned characters, unsigned mask) {
+ if (mask) {
+ load32WithUnalignedHalfWords(negativeOffsetIndexedAddress(offset, character), character);
+ if (mask)
+ or32(Imm32(mask), character);
+ op.m_jumps.append(branch32(NotEqual, character, Imm32(characters | mask)));
+ return;
+ }
+ op.m_jumps.append(branch32WithUnalignedHalfWords(NotEqual, negativeOffsetIndexedAddress(offset, character), TrustedImm32(characters)));
+ };
+
+#if CPU(X86_64) || CPU(ARM64)
+ auto check4 = [&] (Checked<unsigned> offset, uint64_t characters, uint64_t mask) {
+ load64(negativeOffsetIndexedAddress(offset, character), character);
+ if (mask)
+ or64(TrustedImm64(mask), character);
+ op.m_jumps.append(branch64(NotEqual, character, TrustedImm64(characters | mask)));
+ };
+#endif
+
switch (numberCharacters) {
case 1:
- op.m_jumps.append(jumpIfCharNotEquals(ch, m_checkedOffset - term->inputPosition, character));
+ // Use 32bit width of allCharacters since Yarr counts surrogate pairs as one character with unicode flag.
+ check1(m_checkedOffset - startTermPosition, allCharacters & 0xffffffff);
return;
case 2:
- load32WithUnalignedHalfWords(negativeOffsetIndexedAddress(m_checkedOffset- term->inputPosition, character), character);
- break;
+ check2(m_checkedOffset - startTermPosition, allCharacters & 0xffffffff, ignoreCaseMask & 0xffffffff);
+ return;
+#if CPU(X86_64) || CPU(ARM64)
+ case 3:
+ check2(m_checkedOffset - startTermPosition, allCharacters & 0xffffffff, ignoreCaseMask & 0xffffffff);
+ check1(m_checkedOffset - startTermPosition - 2, (allCharacters >> 32) & 0xffff);
+ return;
+ case 4:
+ check4(m_checkedOffset - startTermPosition, allCharacters, ignoreCaseMask);
+ return;
+#endif
}
}
-
- if (ignoreCaseMask)
- or32(Imm32(ignoreCaseMask), character);
- op.m_jumps.append(branch32(NotEqual, character, Imm32(allCharacters | ignoreCaseMask)));
- return;
}
void backtrackPatternCharacterOnce(size_t opIndex)
{
@@ -1250,6 +1575,9 @@ class YarrGenerator : private DefaultMacroAssembler {
const RegisterID character = regT0;
const RegisterID countRegister = regT1;
+ if (m_decodeSurrogatePairs)
+ op.m_jumps.append(jumpIfNoAvailableInput());
+
move(index, countRegister);
Checked<unsigned> scaledMaxCount = term->quantityMaxCount;
scaledMaxCount *= U_IS_BMP(ch) ? 1 : 2;
@@ -1403,8 +1731,10 @@ class YarrGenerator : private DefaultMacroAssembler {
const RegisterID character = regT0;
- if (m_decodeSurrogatePairs)
+ if (m_decodeSurrogatePairs) {
+ op.m_jumps.append(jumpIfNoAvailableInput());
storeToFrame(index, term->frameLocation + BackTrackInfoCharacterClass::beginIndex());
+ }
JumpList matchDest;
readCharacter(m_checkedOffset - term->inputPosition, character);
@@ -1451,6 +1781,9 @@ class YarrGenerator : private DefaultMacroAssembler {
const RegisterID character = regT0;
const RegisterID countRegister = regT1;
+ if (m_decodeSurrogatePairs)
+ op.m_jumps.append(jumpIfNoAvailableInput());
+
move(index, countRegister);
sub32(Imm32(term->quantityMaxCount.unsafeGet()), countRegister);
@@ -1780,13 +2113,19 @@ class YarrGenerator : private DefaultMacroAssembler {
break;
case PatternTerm::TypeForwardReference:
+ m_failureReason = JITFailureReason::ForwardReference;
break;
case PatternTerm::TypeParenthesesSubpattern:
case PatternTerm::TypeParentheticalAssertion:
RELEASE_ASSERT_NOT_REACHED();
+
case PatternTerm::TypeBackReference:
+#if ENABLE(YARR_JIT_BACKREFERENCES)
+ generateBackReference(opIndex);
+#else
m_failureReason = JITFailureReason::BackReference;
+#endif
break;
case PatternTerm::TypeDotStarEnclosure:
generateDotStarEnclosure(opIndex);
@@ -1846,18 +2185,23 @@ class YarrGenerator : private DefaultMacroAssembler {
break;
case PatternTerm::TypeForwardReference:
+ m_failureReason = JITFailureReason::ForwardReference;
break;
case PatternTerm::TypeParenthesesSubpattern:
case PatternTerm::TypeParentheticalAssertion:
RELEASE_ASSERT_NOT_REACHED();
- case PatternTerm::TypeDotStarEnclosure:
- backtrackDotStarEnclosure(opIndex);
- break;
-
case PatternTerm::TypeBackReference:
+#if ENABLE(YARR_JIT_BACKREFERENCES)
+ backtrackBackReference(opIndex);
+#else
m_failureReason = JITFailureReason::BackReference;
+#endif
+ break;
+
+ case PatternTerm::TypeDotStarEnclosure:
+ backtrackDotStarEnclosure(opIndex);
break;
}
}
@@ -2157,7 +2501,7 @@ class YarrGenerator : private DefaultMacroAssembler {
}
// If the parentheses are quantified Greedy then add a label to jump back
- // to if get a failed match from after the parentheses. For NonGreedy
+ // to if we get a failed match from after the parentheses. For NonGreedy
// parentheses, link the jump from before the subpattern to here.
if (term->quantityType == QuantifierGreedy)
op.m_reentry = label();
@@ -2221,11 +2565,11 @@ class YarrGenerator : private DefaultMacroAssembler {
// match within the parentheses, or the second having skipped over them.
// - To check for empty matches, which must be rejected.
//
- // At the head of a NonGreedy set of parentheses we'll immediately set the
- // value on the stack to -1 (indicating a match skipping the subpattern),
+ // At the head of a NonGreedy set of parentheses we'll immediately set 'begin'
+ // in the backtrack info to -1 (indicating a match skipping the subpattern),
// and plant a jump to the end. We'll also plant a label to backtrack to
- // to reenter the subpattern later, with a store to set up index on the
- // second iteration.
+ // to reenter the subpattern later, with a store to set 'begin' to current index
+ // on the second iteration.
//
// FIXME: for capturing parens, could use the index in the capture array?
if (term->quantityType == QuantifierGreedy || term->quantityType == QuantifierNonGreedy) {
@@ -2312,7 +2656,7 @@ class YarrGenerator : private DefaultMacroAssembler {
}
// If the parentheses are quantified Greedy then add a label to jump back
- // to if get a failed match from after the parentheses. For NonGreedy
+ // to if we get a failed match from after the parentheses. For NonGreedy
// parentheses, link the jump from before the subpattern to here.
if (term->quantityType == QuantifierGreedy) {
if (term->quantityMaxCount != quantifyInfinite)
@@ -2324,6 +2668,7 @@ class YarrGenerator : private DefaultMacroAssembler {
} else if (term->quantityType == QuantifierNonGreedy) {
YarrOp& beginOp = m_ops[op.m_previousOp];
beginOp.m_jumps.link(this);
+ op.m_reentry = label();
}
#else // !YARR_JIT_ALL_PARENS_EXPRESSIONS
RELEASE_ASSERT_NOT_REACHED();
@@ -2385,6 +2730,7 @@ class YarrGenerator : private DefaultMacroAssembler {
do {
--opIndex;
+
YarrOp& op = m_ops[opIndex];
switch (op.m_op) {
@@ -2881,32 +3227,32 @@ class YarrGenerator : private DefaultMacroAssembler {
if (term->quantityType != QuantifierFixedCount) {
m_backtrackingState.link(this);
- if (term->quantityType == QuantifierGreedy) {
- RegisterID currParenContextReg = regT0;
- RegisterID newParenContextReg = regT1;
+ RegisterID currParenContextReg = regT0;
+ RegisterID newParenContextReg = regT1;
- loadFromFrame(parenthesesFrameLocation + BackTrackInfoParentheses::parenContextHeadIndex(), currParenContextReg);
+ loadFromFrame(parenthesesFrameLocation + BackTrackInfoParentheses::parenContextHeadIndex(), currParenContextReg);
- restoreParenContext(currParenContextReg, regT2, term->parentheses.subpatternId, term->parentheses.lastSubpatternId, parenthesesFrameLocation);
+ restoreParenContext(currParenContextReg, regT2, term->parentheses.subpatternId, term->parentheses.lastSubpatternId, parenthesesFrameLocation);
- freeParenContext(currParenContextReg, newParenContextReg);
- storeToFrame(newParenContextReg, parenthesesFrameLocation + BackTrackInfoParentheses::parenContextHeadIndex());
- const RegisterID countTemporary = regT0;
- loadFromFrame(parenthesesFrameLocation + BackTrackInfoParentheses::matchAmountIndex(), countTemporary);
- Jump zeroLengthMatch = branchTest32(Zero, countTemporary);
+ freeParenContext(currParenContextReg, newParenContextReg);
+ storeToFrame(newParenContextReg, parenthesesFrameLocation + BackTrackInfoParentheses::parenContextHeadIndex());
- sub32(TrustedImm32(1), countTemporary);
- storeToFrame(countTemporary, parenthesesFrameLocation + BackTrackInfoParentheses::matchAmountIndex());
+ const RegisterID countTemporary = regT0;
+ loadFromFrame(parenthesesFrameLocation + BackTrackInfoParentheses::matchAmountIndex(), countTemporary);
+ Jump zeroLengthMatch = branchTest32(Zero, countTemporary);
- jump(m_ops[op.m_nextOp].m_reentry);
+ sub32(TrustedImm32(1), countTemporary);
+ storeToFrame(countTemporary, parenthesesFrameLocation + BackTrackInfoParentheses::matchAmountIndex());
- zeroLengthMatch.link(this);
+ jump(m_ops[op.m_nextOp].m_reentry);
- // Clear the flag in the stackframe indicating we didn't run through the subpattern.
- storeToFrame(TrustedImm32(-1), parenthesesFrameLocation + BackTrackInfoParentheses::beginIndex());
+ zeroLengthMatch.link(this);
+ // Clear the flag in the stackframe indicating we didn't run through the subpattern.
+ storeToFrame(TrustedImm32(-1), parenthesesFrameLocation + BackTrackInfoParentheses::beginIndex());
+
+ if (term->quantityType == QuantifierGreedy)
jump(m_ops[op.m_nextOp].m_reentry);
- }
// If Greedy, jump to the end.
if (term->quantityType == QuantifierGreedy) {
@@ -2929,13 +3275,14 @@ class YarrGenerator : private DefaultMacroAssembler {
if (term->quantityType != QuantifierFixedCount) {
m_backtrackingState.link(this);
- // Check whether we should backtrack back into the parentheses, or if we
- // are currently in a state where we had skipped over the subpattern
- // (in which case the flag value on the stack will be -1).
unsigned parenthesesFrameLocation = term->frameLocation;
- Jump hadSkipped = branch32(Equal, Address(stackPointerRegister, (parenthesesFrameLocation + BackTrackInfoParentheses::beginIndex()) * sizeof(void*)), TrustedImm32(-1));
if (term->quantityType == QuantifierGreedy) {
+ // Check whether we should backtrack back into the parentheses, or if we
+ // are currently in a state where we had skipped over the subpattern
+ // (in which case the flag value on the stack will be -1).
+ Jump hadSkipped = branch32(Equal, Address(stackPointerRegister, (parenthesesFrameLocation + BackTrackInfoParentheses::beginIndex()) * sizeof(void*)), TrustedImm32(-1));
+
// For Greedy parentheses, we skip after having already tried going
// through the subpattern, so if we get here we're done.
YarrOp& beginOp = m_ops[op.m_previousOp];
@@ -2946,8 +3293,25 @@ class YarrGenerator : private DefaultMacroAssembler {
// next. Jump back to the start of the parentheses in the forwards
// matching path.
ASSERT(term->quantityType == QuantifierNonGreedy);
+
+ const RegisterID beginTemporary = regT0;
+ const RegisterID countTemporary = regT1;
+
YarrOp& beginOp = m_ops[op.m_previousOp];
- hadSkipped.linkTo(beginOp.m_reentry, this);
+
+ loadFromFrame(parenthesesFrameLocation + BackTrackInfoParentheses::beginIndex(), beginTemporary);
+ branch32(Equal, beginTemporary, TrustedImm32(-1)).linkTo(beginOp.m_reentry, this);
+
+ JumpList exceededMatchLimit;
+
+ if (term->quantityMaxCount != quantifyInfinite) {
+ loadFromFrame(parenthesesFrameLocation + BackTrackInfoParentheses::matchAmountIndex(), countTemporary);
+ exceededMatchLimit.append(branch32(AboveOrEqual, countTemporary, Imm32(term->quantityMaxCount.unsafeGet())));
+ }
+
+ branch32(Above, index, beginTemporary).linkTo(beginOp.m_reentry, this);
+
+ exceededMatchLimit.link(this);
}
m_backtrackingState.fallthrough();
@@ -3021,7 +3385,7 @@ class YarrGenerator : private DefaultMacroAssembler {
// the parentheses.
// Supported types of parentheses are 'Once' (quantityMaxCount == 1),
// 'Terminal' (non-capturing parentheses quantified as greedy
- // and infinite), and 0 based greedy quantified parentheses.
+ // and infinite), and 0 based greedy / non-greedy quantified parentheses.
// Alternatives will use the 'Simple' set of ops if either the
// subpattern is terminal (in which case we will never need to
// backtrack), or if the subpattern only contains one alternative.
@@ -3043,7 +3407,9 @@ class YarrGenerator : private DefaultMacroAssembler {
if (term->quantityMinCount && term->quantityMinCount != term->quantityMaxCount) {
m_failureReason = JITFailureReason::VariableCountedParenthesisWithNonZeroMinimum;
return;
- } if (term->quantityMaxCount == 1 && !term->parentheses.isCopy) {
+ }
+
+ if (term->quantityMaxCount == 1 && !term->parentheses.isCopy) {
// Select the 'Once' nodes.
parenthesesBeginOpCode = OpParenthesesSubpatternOnceBegin;
parenthesesEndOpCode = OpParenthesesSubpatternOnceEnd;
@@ -3060,10 +3426,10 @@ class YarrGenerator : private DefaultMacroAssembler {
parenthesesEndOpCode = OpParenthesesSubpatternTerminalEnd;
} else {
#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
- // We only handle generic parenthesis with greedy counts.
- if (term->quantityType != QuantifierGreedy) {
+ // We only handle generic parenthesis with non-fixed counts.
+ if (term->quantityType == QuantifierFixedCount) {
// This subpattern is not supported by the JIT.
- m_failureReason = JITFailureReason::NonGreedyParenthesizedSubpattern;
+ m_failureReason = JITFailureReason::FixedCountParenthesizedSubpattern;
return;
}
@@ -3369,7 +3735,7 @@ class YarrGenerator : private DefaultMacroAssembler {
// The ABI doesn't guarantee the upper bits are zero on unsigned arguments, so clear them ourselves.
zeroExtend32ToPtr(index, index);
zeroExtend32ToPtr(length, length);
-#elif CPU(ARM)
+#elif CPU(ARM_THUMB2)
push(ARMRegisters::r4);
push(ARMRegisters::r5);
push(ARMRegisters::r6);
@@ -3422,7 +3788,7 @@ class YarrGenerator : private DefaultMacroAssembler {
#elif CPU(ARM64)
if (m_decodeSurrogatePairs)
popPair(framePointerRegister, linkRegister);
-#elif CPU(ARM)
+#elif CPU(ARM_THUMB2)
pop(ARMRegisters::r8);
pop(ARMRegisters::r6);
pop(ARMRegisters::r5);
@@ -3460,10 +3826,14 @@ public:
}
#endif
-#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
- if (m_containsNestedSubpatterns)
- codeBlock.setUsesPaternContextBuffer();
+ if (m_pattern.m_containsBackreferences
+#if ENABLE(YARR_JIT_BACKREFERENCES)
+ && (compileMode == MatchOnly || (m_pattern.ignoreCase() && m_charSize != Char8))
#endif
+ ) {
+ codeBlock.setFallBackWithFailureReason(JITFailureReason::BackReference);
+ return;
+ }
// We need to compile before generating code since we set flags based on compilation that
// are used during generation.
@@ -3473,7 +3843,12 @@ public:
codeBlock.setFallBackWithFailureReason(*m_failureReason);
return;
}
-
+
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ if (m_containsNestedSubpatterns)
+ codeBlock.setUsesPatternContextBuffer();
+#endif
+
generateEnter();
Jump hasInput = checkInput();
@@ -3618,7 +3993,10 @@ static void dumpCompileFailure(JITFailureReason failure)
dataLog("Can't JIT a pattern decoding surrogate pairs\n");
break;
case JITFailureReason::BackReference:
- dataLog("Can't JIT a pattern containing back references\n");
+ dataLog("Can't JIT some patterns containing back references\n");
+ break;
+ case JITFailureReason::ForwardReference:
+ dataLog("Can't JIT a pattern containing forward references\n");
break;
case JITFailureReason::VariableCountedParenthesisWithNonZeroMinimum:
dataLog("Can't JIT a pattern containing a variable counted parenthesis with a non-zero minimum\n");
@@ -3626,8 +4004,8 @@ static void dumpCompileFailure(JITFailureReason failure)
case JITFailureReason::ParenthesizedSubpattern:
dataLog("Can't JIT a pattern containing parenthesized subpatterns\n");
break;
- case JITFailureReason::NonGreedyParenthesizedSubpattern:
- dataLog("Can't JIT a pattern containing non-greedy parenthesized subpatterns\n");
+ case JITFailureReason::FixedCountParenthesizedSubpattern:
+ dataLog("Can't JIT a pattern containing fixed count parenthesized subpatterns\n");
break;
case JITFailureReason::ExecutableMemoryAllocationFailure:
dataLog("Can't JIT because of failure of allocation of executable memory\n");
diff --git a/src/3rdparty/masm/yarr/YarrJIT.h b/src/3rdparty/masm/yarr/YarrJIT.h
index 35a0690f6e..c6410d3c44 100644
--- a/src/3rdparty/masm/yarr/YarrJIT.h
+++ b/src/3rdparty/masm/yarr/YarrJIT.h
@@ -54,9 +54,10 @@ namespace Yarr {
enum class JITFailureReason : uint8_t {
DecodeSurrogatePair,
BackReference,
+ ForwardReference,
VariableCountedParenthesisWithNonZeroMinimum,
ParenthesizedSubpattern,
- NonGreedyParenthesizedSubpattern,
+ FixedCountParenthesizedSubpattern,
ExecutableMemoryAllocationFailure,
};
@@ -107,7 +108,7 @@ public:
#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
bool usesPatternContextBuffer() { return m_usesPatternContextBuffer; }
- void setUsesPaternContextBuffer() { m_usesPatternContextBuffer = true; }
+ void setUsesPatternContextBuffer() { m_usesPatternContextBuffer = true; }
MatchResult execute(const LChar* input, unsigned start, unsigned length, int* output, void* freeParenContext, unsigned parenContextSize)
{
diff --git a/src/3rdparty/masm/yarr/YarrParser.h b/src/3rdparty/masm/yarr/YarrParser.h
index edc6beb1f0..a18b553ef0 100644
--- a/src/3rdparty/masm/yarr/YarrParser.h
+++ b/src/3rdparty/masm/yarr/YarrParser.h
@@ -194,7 +194,9 @@ private:
// invoked with inCharacterClass set.
NO_RETURN_DUE_TO_ASSERT void assertionWordBoundary(bool) { RELEASE_ASSERT_NOT_REACHED(); }
NO_RETURN_DUE_TO_ASSERT void atomBackReference(unsigned) { RELEASE_ASSERT_NOT_REACHED(); }
- NO_RETURN_DUE_TO_ASSERT void atomNamedBackReference(String) { RELEASE_ASSERT_NOT_REACHED(); }
+ NO_RETURN_DUE_TO_ASSERT void atomNamedBackReference(const String&) { RELEASE_ASSERT_NOT_REACHED(); }
+ bool isValidNamedForwardReference(const String&) { RELEASE_ASSERT_NOT_REACHED(); return false; }
+ NO_RETURN_DUE_TO_ASSERT void atomNamedForwardReference(const String&) { RELEASE_ASSERT_NOT_REACHED(); }
private:
Delegate& m_delegate;
@@ -421,9 +423,16 @@ private:
if (!atEndOfPattern() && !inCharacterClass) {
if (consume() == '<') {
auto groupName = tryConsumeGroupName();
- if (groupName && m_captureGroupNames.contains(groupName.value())) {
- delegate.atomNamedBackReference(groupName.value());
- break;
+ if (groupName) {
+ if (m_captureGroupNames.contains(groupName.value())) {
+ delegate.atomNamedBackReference(groupName.value());
+ break;
+ }
+
+ if (delegate.isValidNamedForwardReference(groupName.value())) {
+ delegate.atomNamedForwardReference(groupName.value());
+ break;
+ }
}
if (m_isUnicode) {
m_errorCode = ErrorCode::InvalidBackreference;
@@ -1133,11 +1142,13 @@ private:
* void atomCharacterClassRange(UChar32 begin, UChar32 end)
* void atomCharacterClassBuiltIn(BuiltInCharacterClassID classID, bool invert)
* void atomCharacterClassEnd()
- * void atomParenthesesSubpatternBegin(bool capture = true, std::optional<String> groupName);
+ * void atomParenthesesSubpatternBegin(bool capture = true, Optional<String> groupName);
* void atomParentheticalAssertionBegin(bool invert = false);
* void atomParenthesesEnd();
* void atomBackReference(unsigned subpatternId);
- * void atomNamedBackReference(String subpatternName);
+ * void atomNamedBackReference(const String& subpatternName);
+ * bool isValidNamedForwardReference(const String& subpatternName);
+ * void atomNamedForwardReference(const String& subpatternName);
*
* void quantifyAtom(unsigned min, unsigned max, bool greedy);
*
diff --git a/src/3rdparty/masm/yarr/YarrPattern.cpp b/src/3rdparty/masm/yarr/YarrPattern.cpp
index ac66ea1b9a..9c1cdadf3f 100644
--- a/src/3rdparty/masm/yarr/YarrPattern.cpp
+++ b/src/3rdparty/masm/yarr/YarrPattern.cpp
@@ -33,12 +33,9 @@
#include "YarrParser.h"
#include <wtf/DataLog.h>
#include <wtf/Optional.h>
-//#include <wtf/Threading.h>
#include <wtf/Vector.h>
#include <wtf/text/WTFString.h>
-using namespace WTF;
-
namespace JSC { namespace Yarr {
#include "RegExpJitTables.h"
@@ -334,7 +331,7 @@ private:
ranges.insert(i, CharacterRange(lo, hi));
return;
}
- // Okay, since we didn't hit the last case, the end of the new range is definitely at or after the begining
+ // Okay, since we didn't hit the last case, the end of the new range is definitely at or after the beginning
// If the new range start at or before the end of the last range, then the overlap (if it starts one after the
// end of the last range they concatenate, which is just as good.
if (lo <= (ranges[i].end + 1)) {
@@ -446,9 +443,9 @@ public:
{
}
- void reset()
+ void resetForReparsing()
{
- m_pattern.reset();
+ m_pattern.resetForReparsing();
m_characterClassConstructor.reset();
auto body = std::make_unique<PatternDisjunction>();
@@ -456,7 +453,17 @@ public:
m_alternative = body->addNewAlternative();
m_pattern.m_disjunctions.append(WTFMove(body));
}
-
+
+ void saveUnmatchedNamedForwardReferences()
+ {
+ m_unmatchedNamedForwardReferences.shrink(0);
+
+ for (auto& entry : m_pattern.m_namedForwardReferences) {
+ if (!m_pattern.m_captureGroupNames.contains(entry))
+ m_unmatchedNamedForwardReferences.append(entry);
+ }
+ }
+
void assertionBOL()
{
if (!m_alternative->m_terms.size() && !m_invertParentheticalAssertion) {
@@ -666,12 +673,24 @@ public:
m_alternative->m_terms.append(PatternTerm(subpatternId));
}
- void atomNamedBackReference(String subpatternName)
+ void atomNamedBackReference(const String& subpatternName)
{
ASSERT(m_pattern.m_namedGroupToParenIndex.find(subpatternName) != m_pattern.m_namedGroupToParenIndex.end());
atomBackReference(m_pattern.m_namedGroupToParenIndex.get(subpatternName));
}
+ bool isValidNamedForwardReference(const String& subpatternName)
+ {
+ return !m_unmatchedNamedForwardReferences.contains(subpatternName);
+ }
+
+ void atomNamedForwardReference(const String& subpatternName)
+ {
+ if (!m_pattern.m_namedForwardReferences.contains(subpatternName))
+ m_pattern.m_namedForwardReferences.append(subpatternName);
+ m_alternative->m_terms.append(PatternTerm::ForwardReference());
+ }
+
// deep copy the argument disjunction. If filterStartsWithBOL is true,
// skip alternatives with m_startsWithBOL set true.
PatternDisjunction* copyDisjunction(PatternDisjunction* disjunction, bool filterStartsWithBOL = false)
@@ -1079,6 +1098,7 @@ private:
YarrPattern& m_pattern;
PatternAlternative* m_alternative;
CharacterClassConstructor m_characterClassConstructor;
+ Vector<String> m_unmatchedNamedForwardReferences;
void* m_stackLimit;
bool m_invertCharacterClass;
bool m_invertParentheticalAssertion { false };
@@ -1101,13 +1121,14 @@ ErrorCode YarrPattern::compile(const String& patternString, void* stackLimit)
// Quoting Netscape's "What's new in JavaScript 1.2",
// "Note: if the number of left parentheses is less than the number specified
// in \#, the \# is taken as an octal escape as described in the next row."
- if (containsIllegalBackReference()) {
+ if (containsIllegalBackReference() || containsIllegalNamedForwardReferences()) {
if (unicode())
return ErrorCode::InvalidBackreference;
unsigned numSubpatterns = m_numSubpatterns;
- constructor.reset();
+ constructor.saveUnmatchedNamedForwardReferences();
+ constructor.resetForReparsing();
ErrorCode error = parse(constructor, patternString, unicode(), numSubpatterns);
ASSERT_UNUSED(error, !hasError(error));
ASSERT(numSubpatterns == m_numSubpatterns);
@@ -1168,7 +1189,7 @@ void dumpCharacterClass(PrintStream& out, YarrPattern* pattern, CharacterClass*
else if (characterClass == pattern->wordcharCharacterClass())
out.print("<word>");
else if (characterClass == pattern->wordUnicodeIgnoreCaseCharCharacterClass())
- out.print("<unicode ignore case>");
+ out.print("<unicode word ignore case>");
else if (characterClass == pattern->nondigitsCharacterClass())
out.print("<non-digits>");
else if (characterClass == pattern->nonspacesCharacterClass())
@@ -1176,7 +1197,7 @@ void dumpCharacterClass(PrintStream& out, YarrPattern* pattern, CharacterClass*
else if (characterClass == pattern->nonwordcharCharacterClass())
out.print("<non-word>");
else if (characterClass == pattern->nonwordUnicodeIgnoreCaseCharCharacterClass())
- out.print("<unicode non-ignore case>");
+ out.print("<unicode non-word ignore case>");
else {
bool needMatchesRangesSeperator = false;
@@ -1298,75 +1319,7 @@ void PatternTerm::dump(PrintStream& out, YarrPattern* thisPattern, unsigned nest
break;
case TypeCharacterClass:
out.print("character class ");
- if (characterClass->m_anyCharacter)
- out.print("<any character>");
- else if (characterClass == thisPattern->newlineCharacterClass())
- out.print("<newline>");
- else if (characterClass == thisPattern->digitsCharacterClass())
- out.print("<digits>");
- else if (characterClass == thisPattern->spacesCharacterClass())
- out.print("<whitespace>");
- else if (characterClass == thisPattern->wordcharCharacterClass())
- out.print("<word>");
- else if (characterClass == thisPattern->wordUnicodeIgnoreCaseCharCharacterClass())
- out.print("<unicode ignore case>");
- else if (characterClass == thisPattern->nondigitsCharacterClass())
- out.print("<non-digits>");
- else if (characterClass == thisPattern->nonspacesCharacterClass())
- out.print("<non-whitespace>");
- else if (characterClass == thisPattern->nonwordcharCharacterClass())
- out.print("<non-word>");
- else if (characterClass == thisPattern->nonwordUnicodeIgnoreCaseCharCharacterClass())
- out.print("<unicode non-ignore case>");
- else {
- bool needMatchesRangesSeperator = false;
-
- auto dumpMatches = [&] (const char* prefix, Vector<UChar32> matches) {
- size_t matchesSize = matches.size();
- if (matchesSize) {
- if (needMatchesRangesSeperator)
- out.print(",");
- needMatchesRangesSeperator = true;
-
- out.print(prefix, ":(");
- for (size_t i = 0; i < matchesSize; ++i) {
- if (i)
- out.print(",");
- dumpUChar32(out, matches[i]);
- }
- out.print(")");
- }
- };
-
- auto dumpRanges = [&] (const char* prefix, Vector<CharacterRange> ranges) {
- size_t rangeSize = ranges.size();
- if (rangeSize) {
- if (needMatchesRangesSeperator)
- out.print(",");
- needMatchesRangesSeperator = true;
-
- out.print(prefix, " ranges:(");
- for (size_t i = 0; i < rangeSize; ++i) {
- if (i)
- out.print(",");
- CharacterRange range = ranges[i];
- out.print("(");
- dumpUChar32(out, range.begin);
- out.print("..");
- dumpUChar32(out, range.end);
- out.print(")");
- }
- out.print(")");
- }
- };
-
- out.print("[");
- dumpMatches("ASCII", characterClass->m_matches);
- dumpRanges("ASCII", characterClass->m_ranges);
- dumpMatches("Unicode", characterClass->m_matchesUnicode);
- dumpRanges("Unicode", characterClass->m_rangesUnicode);
- out.print("]");
- }
+ dumpCharacterClass(out, thisPattern, characterClass);
dumpQuantifier(out);
if (quantityType != QuantifierFixedCount || thisPattern->unicode())
out.print(",frame location ", frameLocation);
@@ -1439,16 +1392,10 @@ void PatternDisjunction::dump(PrintStream& out, YarrPattern* thisPattern, unsign
}
}
-void YarrPattern::dumpPattern(const String& patternString)
+void YarrPattern::dumpPatternString(PrintStream& out, const String& patternString)
{
- dumpPattern(WTF::dataFile(), patternString);
-}
+ out.print("/", patternString, "/");
-void YarrPattern::dumpPattern(PrintStream& out, const String& patternString)
-{
- out.print("RegExp pattern for /");
- out.print(patternString);
- out.print("/");
if (global())
out.print("g");
if (ignoreCase())
@@ -1459,6 +1406,18 @@ void YarrPattern::dumpPattern(PrintStream& out, const String& patternString)
out.print("u");
if (sticky())
out.print("y");
+}
+
+void YarrPattern::dumpPattern(const String& patternString)
+{
+ dumpPattern(WTF::dataFile(), patternString);
+}
+
+void YarrPattern::dumpPattern(PrintStream& out, const String& patternString)
+{
+ out.print("RegExp pattern for ");
+ dumpPatternString(out, patternString);
+
if (m_flags != NoFlags) {
bool printSeperator = false;
out.print(" (");
diff --git a/src/3rdparty/masm/yarr/YarrPattern.h b/src/3rdparty/masm/yarr/YarrPattern.h
index f7ddf861ba..10ea2c5b94 100644
--- a/src/3rdparty/masm/yarr/YarrPattern.h
+++ b/src/3rdparty/masm/yarr/YarrPattern.h
@@ -355,7 +355,7 @@ struct TermChain {
struct YarrPattern {
JS_EXPORT_PRIVATE YarrPattern(const String& pattern, RegExpFlags, ErrorCode&, void* stackLimit = nullptr);
- void reset()
+ void resetForReparsing()
{
m_numSubpatterns = 0;
m_maxBackReference = 0;
@@ -382,6 +382,7 @@ struct YarrPattern {
m_disjunctions.clear();
m_userCharacterClasses.clear();
m_captureGroupNames.shrink(0);
+ m_namedForwardReferences.shrink(0);
}
bool containsIllegalBackReference()
@@ -389,6 +390,19 @@ struct YarrPattern {
return m_maxBackReference > m_numSubpatterns;
}
+ bool containsIllegalNamedForwardReferences()
+ {
+ if (m_namedForwardReferences.isEmpty())
+ return false;
+
+ for (auto& entry : m_namedForwardReferences) {
+ if (m_captureGroupNames.contains(entry))
+ return true;
+ }
+
+ return false;
+ }
+
bool containsUnsignedLengthPattern()
{
return m_containsUnsignedLengthPattern;
@@ -490,6 +504,7 @@ struct YarrPattern {
return unicodePropertiesCached.get(classID);
}
+ void dumpPatternString(PrintStream& out, const String& patternString);
void dumpPattern(const String& pattern);
void dumpPattern(PrintStream& out, const String& pattern);
@@ -513,6 +528,7 @@ struct YarrPattern {
Vector<std::unique_ptr<PatternDisjunction>, 4> m_disjunctions;
Vector<std::unique_ptr<CharacterClass>> m_userCharacterClasses;
Vector<String> m_captureGroupNames;
+ Vector<String> m_namedForwardReferences;
HashMap<String, unsigned> m_namedGroupToParenIndex;
private:
@@ -555,8 +571,8 @@ private:
uintptr_t begin; // Not really needed for greedy quantifiers.
uintptr_t matchAmount; // Not really needed for fixed quantifiers.
- unsigned beginIndex() { return offsetof(BackTrackInfoBackReference, begin) / sizeof(uintptr_t); }
- unsigned matchAmountIndex() { return offsetof(BackTrackInfoBackReference, matchAmount) / sizeof(uintptr_t); }
+ static unsigned beginIndex() { return offsetof(BackTrackInfoBackReference, begin) / sizeof(uintptr_t); }
+ static unsigned matchAmountIndex() { return offsetof(BackTrackInfoBackReference, matchAmount) / sizeof(uintptr_t); }
};
struct BackTrackInfoAlternative {
diff --git a/src/3rdparty/masm/yarr/YarrSyntaxChecker.cpp b/src/3rdparty/masm/yarr/YarrSyntaxChecker.cpp
index 9f05f22852..358cc94d6b 100644
--- a/src/3rdparty/masm/yarr/YarrSyntaxChecker.cpp
+++ b/src/3rdparty/masm/yarr/YarrSyntaxChecker.cpp
@@ -48,7 +48,9 @@ public:
void atomParentheticalAssertionBegin(bool = false) {}
void atomParenthesesEnd() {}
void atomBackReference(unsigned) {}
- void atomNamedBackReference(String) {}
+ void atomNamedBackReference(const String&) {}
+ bool isValidNamedForwardReference(const String&) { return true; }
+ void atomNamedForwardReference(const String&) {}
void quantifyAtom(unsigned, unsigned, bool) {}
void disjunction() {}
};
diff --git a/src/3rdparty/masm/yarr/create_regex_tables b/src/3rdparty/masm/yarr/create_regex_tables
index 4c3dbbe3fb..992566db77 100644
--- a/src/3rdparty/masm/yarr/create_regex_tables
+++ b/src/3rdparty/masm/yarr/create_regex_tables
@@ -32,7 +32,7 @@ types = {
"nonwordchar": { "UseTable" : True, "Inverse": "wordchar", "data": ['`', (0, ord('0') - 1), (ord('9') + 1, ord('A') - 1), (ord('Z') + 1, ord('_') - 1), (ord('z') + 1, 0x10ffff)]},
"nonwordUnicodeIgnoreCaseChar": { "UseTable" : False, "Inverse": "wordUnicodeIgnoreCaseChar", "data": ['`', (0, ord('0') - 1), (ord('9') + 1, ord('A') - 1), (ord('Z') + 1, ord('_') - 1), (ord('z') + 1, 0x017e), (0x0180, 0x2129), (0x212b, 0x10ffff)]},
"newline": { "UseTable" : False, "data": ['\n', '\r', 0x2028, 0x2029]},
- "spaces": { "UseTable" : True, "data": [' ', ('\t', '\r'), 0xa0, 0x1680, 0x180e, 0x2028, 0x2029, 0x202f, 0x205f, 0x3000, (0x2000, 0x200a), 0xfeff]},
+ "spaces": { "UseTable" : True, "data": [' ', ('\t', '\r'), 0xa0, 0x1680, 0x2028, 0x2029, 0x202f, 0x205f, 0x3000, (0x2000, 0x200a), 0xfeff]},
"nonspaces": { "UseTable" : True, "Inverse": "spaces", "data": [(0, ord('\t') - 1), (ord('\r') + 1, ord(' ') - 1), (ord(' ') + 1, 0x009f), (0x00a1, 0x167f), (0x1681, 0x180d), (0x180f, 0x1fff), (0x200b, 0x2027), (0x202a, 0x202e), (0x2030, 0x205e), (0x2060, 0x2fff), (0x3001, 0xfefe), (0xff00, 0x10ffff)]},
"digits": { "UseTable" : False, "data": [('0', '9')]},
"nondigits": { "UseTable" : False, "Inverse": "digits", "data": [(0, ord('0') - 1), (ord('9') + 1, 0x10ffff)] }
diff --git a/src/3rdparty/masm/yarr/generateYarrCanonicalizeUnicode b/src/3rdparty/masm/yarr/generateYarrCanonicalizeUnicode
index a103bcdf16..95549c7eb5 100644
--- a/src/3rdparty/masm/yarr/generateYarrCanonicalizeUnicode
+++ b/src/3rdparty/masm/yarr/generateYarrCanonicalizeUnicode
@@ -31,7 +31,6 @@ import optparse
import os
import re
import sys
-from sets import Set
header = """/*
* Copyright (C) 2016 Apple Inc. All rights reserved.
@@ -78,9 +77,12 @@ def openOrExit(path, mode):
dirname = os.path.dirname(path)
if not os.path.isdir(dirname):
os.makedirs(dirname)
- return open(path, mode)
+ if sys.version_info.major >= 3:
+ return open(path, mode, encoding="UTF-8")
+ else:
+ return open(path, mode)
except IOError as e:
- print "I/O error opening {0}, ({1}): {2}".format(path, e.errno, e.strerror)
+ print("I/O error opening {0}, ({1}): {2}".format(path, e.errno, e.strerror))
exit(1)
class Canonicalize:
@@ -93,7 +95,7 @@ class Canonicalize:
self.canonicalGroups[mapping].append(code)
def readCaseFolding(self, file):
- codesSeen = Set()
+ codesSeen = set()
for line in file:
line = line.split('#', 1)[0]
line = line.rstrip()
@@ -154,8 +156,8 @@ class Canonicalize:
for i in range(len(characterSets)):
characters = ""
- set = characterSets[i]
- for ch in set:
+ cur_set = characterSets[i]
+ for ch in cur_set:
characters = characters + "0x{character:04x}, ".format(character=ch)
file.write("const UChar32 unicodeCharacterSet{index:d}[] = {{ {characters}0 }};\n".format(index=i, characters=characters))
@@ -189,7 +191,7 @@ if __name__ == "__main__":
caseFoldingTxtPath = args[0]
canonicalizeHPath = args[1]
caseFoldingTxtFile = openOrExit(caseFoldingTxtPath, "r")
- canonicalizeHFile = openOrExit(canonicalizeHPath, "wb")
+ canonicalizeHFile = openOrExit(canonicalizeHPath, "w")
canonicalize = Canonicalize()
canonicalize.readCaseFolding(caseFoldingTxtFile)
diff --git a/src/imports/builtins/builtins.qmltypes b/src/imports/builtins/builtins.qmltypes
index 4ad103f8de..765c92fcb9 100644
--- a/src/imports/builtins/builtins.qmltypes
+++ b/src/imports/builtins/builtins.qmltypes
@@ -131,6 +131,13 @@ Module {
}
}
Enum {
+ name: "SplitBehavior"
+ values: {
+ "KeepEmptyParts": 0,
+ "SkipEmptyParts": 1
+ }
+ }
+ Enum {
name: "Alignment"
values: {
"AlignLeft": 1,
@@ -466,7 +473,8 @@ Module {
"AA_DontShowShortcutsInContextMenus": 28,
"AA_CompressTabletEvents": 29,
"AA_DisableWindowContextHelpButton": 30,
- "AA_AttributeCount": 31
+ "AA_DisableSessionManager": 31,
+ "AA_AttributeCount": 32
}
}
Enum {
@@ -1081,7 +1089,8 @@ Module {
values: {
"PlainText": 0,
"RichText": 1,
- "AutoText": 2
+ "AutoText": 2,
+ "MarkdownText": 3
}
}
Enum {
@@ -1676,6 +1685,17 @@ Module {
"ChecksumItuV41": 1
}
}
+ Enum {
+ name: "HighDpiScaleFactorRoundingPolicy"
+ values: {
+ "Unset": 0,
+ "Round": 1,
+ "Ceil": 2,
+ "Floor": 3,
+ "RoundPreferFloor": 4,
+ "PassThrough": 5
+ }
+ }
}
Component { name: "QEasingCurve"; prototype: "QQmlEasingValueType" }
}
diff --git a/src/imports/folderlistmodel/plugins.qmltypes b/src/imports/folderlistmodel/plugins.qmltypes
index 6f5466dbda..7c53c99665 100644
--- a/src/imports/folderlistmodel/plugins.qmltypes
+++ b/src/imports/folderlistmodel/plugins.qmltypes
@@ -4,11 +4,285 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable Qt.labs.folderlistmodel 2.13'
+// 'qmlplugindump -nonrelocatable Qt.labs.folderlistmodel 2.14'
Module {
dependencies: ["QtQuick 2.0"]
Component {
+ name: "QAbstractItemModel"
+ prototype: "QObject"
+ Enum {
+ name: "LayoutChangeHint"
+ values: {
+ "NoLayoutChangeHint": 0,
+ "VerticalSortHint": 1,
+ "HorizontalSortHint": 2
+ }
+ }
+ Enum {
+ name: "CheckIndexOption"
+ values: {
+ "NoOption": 0,
+ "IndexIsValid": 1,
+ "DoNotUseParent": 2,
+ "ParentIsInvalid": 4
+ }
+ }
+ Signal {
+ name: "dataChanged"
+ Parameter { name: "topLeft"; type: "QModelIndex" }
+ Parameter { name: "bottomRight"; type: "QModelIndex" }
+ Parameter { name: "roles"; type: "QVector<int>" }
+ }
+ Signal {
+ name: "dataChanged"
+ Parameter { name: "topLeft"; type: "QModelIndex" }
+ Parameter { name: "bottomRight"; type: "QModelIndex" }
+ }
+ Signal {
+ name: "headerDataChanged"
+ Parameter { name: "orientation"; type: "Qt::Orientation" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "layoutChanged"
+ Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
+ Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" }
+ }
+ Signal {
+ name: "layoutChanged"
+ Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
+ }
+ Signal { name: "layoutChanged" }
+ Signal {
+ name: "layoutAboutToBeChanged"
+ Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
+ Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" }
+ }
+ Signal {
+ name: "layoutAboutToBeChanged"
+ Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
+ }
+ Signal { name: "layoutAboutToBeChanged" }
+ Signal {
+ name: "rowsAboutToBeInserted"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "rowsInserted"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "rowsAboutToBeRemoved"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "rowsRemoved"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "columnsAboutToBeInserted"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "columnsInserted"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "columnsAboutToBeRemoved"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "columnsRemoved"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal { name: "modelAboutToBeReset" }
+ Signal { name: "modelReset" }
+ Signal {
+ name: "rowsAboutToBeMoved"
+ Parameter { name: "sourceParent"; type: "QModelIndex" }
+ Parameter { name: "sourceStart"; type: "int" }
+ Parameter { name: "sourceEnd"; type: "int" }
+ Parameter { name: "destinationParent"; type: "QModelIndex" }
+ Parameter { name: "destinationRow"; type: "int" }
+ }
+ Signal {
+ name: "rowsMoved"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "start"; type: "int" }
+ Parameter { name: "end"; type: "int" }
+ Parameter { name: "destination"; type: "QModelIndex" }
+ Parameter { name: "row"; type: "int" }
+ }
+ Signal {
+ name: "columnsAboutToBeMoved"
+ Parameter { name: "sourceParent"; type: "QModelIndex" }
+ Parameter { name: "sourceStart"; type: "int" }
+ Parameter { name: "sourceEnd"; type: "int" }
+ Parameter { name: "destinationParent"; type: "QModelIndex" }
+ Parameter { name: "destinationColumn"; type: "int" }
+ }
+ Signal {
+ name: "columnsMoved"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "start"; type: "int" }
+ Parameter { name: "end"; type: "int" }
+ Parameter { name: "destination"; type: "QModelIndex" }
+ Parameter { name: "column"; type: "int" }
+ }
+ Method { name: "submit"; type: "bool" }
+ Method { name: "revert" }
+ Method {
+ name: "hasIndex"
+ type: "bool"
+ Parameter { name: "row"; type: "int" }
+ Parameter { name: "column"; type: "int" }
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method {
+ name: "hasIndex"
+ type: "bool"
+ Parameter { name: "row"; type: "int" }
+ Parameter { name: "column"; type: "int" }
+ }
+ Method {
+ name: "index"
+ type: "QModelIndex"
+ Parameter { name: "row"; type: "int" }
+ Parameter { name: "column"; type: "int" }
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method {
+ name: "index"
+ type: "QModelIndex"
+ Parameter { name: "row"; type: "int" }
+ Parameter { name: "column"; type: "int" }
+ }
+ Method {
+ name: "parent"
+ type: "QModelIndex"
+ Parameter { name: "child"; type: "QModelIndex" }
+ }
+ Method {
+ name: "sibling"
+ type: "QModelIndex"
+ Parameter { name: "row"; type: "int" }
+ Parameter { name: "column"; type: "int" }
+ Parameter { name: "idx"; type: "QModelIndex" }
+ }
+ Method {
+ name: "rowCount"
+ type: "int"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method { name: "rowCount"; type: "int" }
+ Method {
+ name: "columnCount"
+ type: "int"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method { name: "columnCount"; type: "int" }
+ Method {
+ name: "hasChildren"
+ type: "bool"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method { name: "hasChildren"; type: "bool" }
+ Method {
+ name: "data"
+ type: "QVariant"
+ Parameter { name: "index"; type: "QModelIndex" }
+ Parameter { name: "role"; type: "int" }
+ }
+ Method {
+ name: "data"
+ type: "QVariant"
+ Parameter { name: "index"; type: "QModelIndex" }
+ }
+ Method {
+ name: "setData"
+ type: "bool"
+ Parameter { name: "index"; type: "QModelIndex" }
+ Parameter { name: "value"; type: "QVariant" }
+ Parameter { name: "role"; type: "int" }
+ }
+ Method {
+ name: "setData"
+ type: "bool"
+ Parameter { name: "index"; type: "QModelIndex" }
+ Parameter { name: "value"; type: "QVariant" }
+ }
+ Method {
+ name: "headerData"
+ type: "QVariant"
+ Parameter { name: "section"; type: "int" }
+ Parameter { name: "orientation"; type: "Qt::Orientation" }
+ Parameter { name: "role"; type: "int" }
+ }
+ Method {
+ name: "headerData"
+ type: "QVariant"
+ Parameter { name: "section"; type: "int" }
+ Parameter { name: "orientation"; type: "Qt::Orientation" }
+ }
+ Method {
+ name: "fetchMore"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method {
+ name: "canFetchMore"
+ type: "bool"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method {
+ name: "flags"
+ type: "Qt::ItemFlags"
+ Parameter { name: "index"; type: "QModelIndex" }
+ }
+ Method {
+ name: "match"
+ type: "QModelIndexList"
+ Parameter { name: "start"; type: "QModelIndex" }
+ Parameter { name: "role"; type: "int" }
+ Parameter { name: "value"; type: "QVariant" }
+ Parameter { name: "hits"; type: "int" }
+ Parameter { name: "flags"; type: "Qt::MatchFlags" }
+ }
+ Method {
+ name: "match"
+ type: "QModelIndexList"
+ Parameter { name: "start"; type: "QModelIndex" }
+ Parameter { name: "role"; type: "int" }
+ Parameter { name: "value"; type: "QVariant" }
+ Parameter { name: "hits"; type: "int" }
+ }
+ Method {
+ name: "match"
+ type: "QModelIndexList"
+ Parameter { name: "start"; type: "QModelIndex" }
+ Parameter { name: "role"; type: "int" }
+ Parameter { name: "value"; type: "QVariant" }
+ }
+ }
+ Component { name: "QAbstractListModel"; prototype: "QAbstractItemModel" }
+ Component {
name: "QQuickFolderListModel"
prototype: "QAbstractListModel"
exports: [
diff --git a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp
index 51931097f3..f5acfd86b7 100644
--- a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp
+++ b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp
@@ -733,6 +733,7 @@ void QQuickFolderListModel::setShowDotAndDotDot(bool on)
if (on != d->showDotAndDotDot) {
d->fileInfoThread.setShowDotAndDotDot(on);
+ d->showDotAndDotDot = on;
}
}
@@ -758,6 +759,7 @@ void QQuickFolderListModel::setShowHidden(bool on)
if (on != d->showHidden) {
d->fileInfoThread.setShowHidden(on);
+ d->showHidden = on;
}
}
@@ -783,6 +785,7 @@ void QQuickFolderListModel::setShowOnlyReadable(bool on)
if (on != d->showOnlyReadable) {
d->fileInfoThread.setShowOnlyReadable(on);
+ d->showOnlyReadable = on;
}
}
@@ -807,6 +810,7 @@ void QQuickFolderListModel::setCaseSensitive(bool on)
if (on != d->caseSensitive) {
d->fileInfoThread.setCaseSensitive(on);
+ d->caseSensitive = on;
}
}
diff --git a/src/imports/imports.pro b/src/imports/imports.pro
index 9714e4697b..9973883024 100644
--- a/src/imports/imports.pro
+++ b/src/imports/imports.pro
@@ -1,4 +1,5 @@
TEMPLATE = subdirs
+QT_FOR_CONFIG += qml-private
SUBDIRS += \
builtins \
@@ -6,7 +7,8 @@ SUBDIRS += \
models \
labsmodels
-SUBDIRS += folderlistmodel
+qtConfig(qml-itemmodel): SUBDIRS += folderlistmodel
+qtConfig(qml-worker-script): SUBDIRS += workerscript
qtHaveModule(sql): SUBDIRS += localstorage
qtConfig(settings): SUBDIRS += settings
qtConfig(statemachine): SUBDIRS += statemachine
@@ -15,6 +17,7 @@ qtHaveModule(quick) {
QT_FOR_CONFIG += quick-private
SUBDIRS += \
+ labsanimation \
layouts \
qtquick2 \
window \
diff --git a/src/imports/labsanimation/dependencies.json b/src/imports/labsanimation/dependencies.json
new file mode 100644
index 0000000000..0d4f101c7a
--- /dev/null
+++ b/src/imports/labsanimation/dependencies.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/src/imports/labsanimation/labsanimation.pro b/src/imports/labsanimation/labsanimation.pro
new file mode 100644
index 0000000000..64e076401f
--- /dev/null
+++ b/src/imports/labsanimation/labsanimation.pro
@@ -0,0 +1,11 @@
+CXX_MODULE = qml
+TARGET = labsanimationplugin
+TARGETPATH = Qt/labs/animation
+IMPORT_VERSION = 1.0
+
+SOURCES += \
+ plugin.cpp
+
+QT = qml-private quick-private
+
+load(qml_plugin)
diff --git a/src/imports/labsanimation/plugin.cpp b/src/imports/labsanimation/plugin.cpp
new file mode 100644
index 0000000000..d8c0c071ca
--- /dev/null
+++ b/src/imports/labsanimation/plugin.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqml.h>
+
+#include <private/qquickboundaryrule_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlmodule Qt.labs.animation 1.0
+ \title Qt Quick experimental animation types
+ \ingroup qmlmodules
+ \brief Provides QML experimental types for animation
+ \since 5.14
+
+ This QML module contains experimental QML types related to animation.
+
+ To use the types in this module, import the module with the following line:
+
+ \code
+ import Qt.labs.animation 1.0
+ \endcode
+*/
+
+//![class decl]
+class QtLabsAnimationPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+public:
+ QtLabsAnimationPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { }
+ void registerTypes(const char *uri) override
+ {
+ Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.animation"));
+ qmlRegisterType<QQuickBoundaryRule>(uri, 1, 0, "BoundaryRule");
+ qmlRegisterModule(uri, 1, 0);
+ }
+};
+//![class decl]
+
+QT_END_NAMESPACE
+
+#include "plugin.moc"
diff --git a/src/imports/labsanimation/plugins.qmltypes b/src/imports/labsanimation/plugins.qmltypes
new file mode 100644
index 0000000000..2ecc5e6f5d
--- /dev/null
+++ b/src/imports/labsanimation/plugins.qmltypes
@@ -0,0 +1,36 @@
+import QtQuick.tooling 1.2
+
+// This file describes the plugin-supplied types contained in the library.
+// It is used for QML tooling purposes only.
+//
+// This file was auto-generated by:
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json Qt.labs.animation 1.0'
+
+Module {
+ dependencies: []
+ Component {
+ name: "QQuickBoundaryRule"
+ prototype: "QObject"
+ exports: ["Qt.labs.animation/BoundaryRule 1.0"]
+ exportMetaObjectRevisions: [0]
+ Enum {
+ name: "OvershootFilter"
+ values: {
+ "None": 0,
+ "Peak": 1
+ }
+ }
+ Property { name: "enabled"; type: "bool" }
+ Property { name: "minimum"; type: "double" }
+ Property { name: "minimumOvershoot"; type: "double" }
+ Property { name: "maximum"; type: "double" }
+ Property { name: "maximumOvershoot"; type: "double" }
+ Property { name: "overshootScale"; type: "double" }
+ Property { name: "currentOvershoot"; type: "double"; isReadonly: true }
+ Property { name: "peakOvershoot"; type: "double"; isReadonly: true }
+ Property { name: "overshootFilter"; type: "OvershootFilter" }
+ Property { name: "easing"; type: "QEasingCurve" }
+ Property { name: "returnDuration"; type: "int" }
+ Method { name: "returnToBounds"; type: "bool" }
+ }
+}
diff --git a/src/imports/labsanimation/qmldir b/src/imports/labsanimation/qmldir
new file mode 100644
index 0000000000..b24fc98bfa
--- /dev/null
+++ b/src/imports/labsanimation/qmldir
@@ -0,0 +1,3 @@
+module Qt.labs.animation
+plugin labsanimationplugin
+classname QtLabsAnimationPlugin
diff --git a/src/imports/labsmodels/labsmodels.pro b/src/imports/labsmodels/labsmodels.pro
index 1795ae5e43..5ef2ad76f6 100644
--- a/src/imports/labsmodels/labsmodels.pro
+++ b/src/imports/labsmodels/labsmodels.pro
@@ -6,6 +6,6 @@ IMPORT_VERSION = 1.0
SOURCES += \
plugin.cpp
-QT = qml-private
+QT = qml-private qmlmodels-private
load(qml_plugin)
diff --git a/src/imports/labsmodels/plugins.qmltypes b/src/imports/labsmodels/plugins.qmltypes
index 6272069060..f2a5752422 100644
--- a/src/imports/labsmodels/plugins.qmltypes
+++ b/src/imports/labsmodels/plugins.qmltypes
@@ -9,6 +9,280 @@ import QtQuick.tooling 1.2
Module {
dependencies: []
Component {
+ name: "QAbstractItemModel"
+ prototype: "QObject"
+ Enum {
+ name: "LayoutChangeHint"
+ values: {
+ "NoLayoutChangeHint": 0,
+ "VerticalSortHint": 1,
+ "HorizontalSortHint": 2
+ }
+ }
+ Enum {
+ name: "CheckIndexOption"
+ values: {
+ "NoOption": 0,
+ "IndexIsValid": 1,
+ "DoNotUseParent": 2,
+ "ParentIsInvalid": 4
+ }
+ }
+ Signal {
+ name: "dataChanged"
+ Parameter { name: "topLeft"; type: "QModelIndex" }
+ Parameter { name: "bottomRight"; type: "QModelIndex" }
+ Parameter { name: "roles"; type: "QVector<int>" }
+ }
+ Signal {
+ name: "dataChanged"
+ Parameter { name: "topLeft"; type: "QModelIndex" }
+ Parameter { name: "bottomRight"; type: "QModelIndex" }
+ }
+ Signal {
+ name: "headerDataChanged"
+ Parameter { name: "orientation"; type: "Qt::Orientation" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "layoutChanged"
+ Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
+ Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" }
+ }
+ Signal {
+ name: "layoutChanged"
+ Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
+ }
+ Signal { name: "layoutChanged" }
+ Signal {
+ name: "layoutAboutToBeChanged"
+ Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
+ Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" }
+ }
+ Signal {
+ name: "layoutAboutToBeChanged"
+ Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
+ }
+ Signal { name: "layoutAboutToBeChanged" }
+ Signal {
+ name: "rowsAboutToBeInserted"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "rowsInserted"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "rowsAboutToBeRemoved"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "rowsRemoved"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "columnsAboutToBeInserted"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "columnsInserted"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "columnsAboutToBeRemoved"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal {
+ name: "columnsRemoved"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "first"; type: "int" }
+ Parameter { name: "last"; type: "int" }
+ }
+ Signal { name: "modelAboutToBeReset" }
+ Signal { name: "modelReset" }
+ Signal {
+ name: "rowsAboutToBeMoved"
+ Parameter { name: "sourceParent"; type: "QModelIndex" }
+ Parameter { name: "sourceStart"; type: "int" }
+ Parameter { name: "sourceEnd"; type: "int" }
+ Parameter { name: "destinationParent"; type: "QModelIndex" }
+ Parameter { name: "destinationRow"; type: "int" }
+ }
+ Signal {
+ name: "rowsMoved"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "start"; type: "int" }
+ Parameter { name: "end"; type: "int" }
+ Parameter { name: "destination"; type: "QModelIndex" }
+ Parameter { name: "row"; type: "int" }
+ }
+ Signal {
+ name: "columnsAboutToBeMoved"
+ Parameter { name: "sourceParent"; type: "QModelIndex" }
+ Parameter { name: "sourceStart"; type: "int" }
+ Parameter { name: "sourceEnd"; type: "int" }
+ Parameter { name: "destinationParent"; type: "QModelIndex" }
+ Parameter { name: "destinationColumn"; type: "int" }
+ }
+ Signal {
+ name: "columnsMoved"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ Parameter { name: "start"; type: "int" }
+ Parameter { name: "end"; type: "int" }
+ Parameter { name: "destination"; type: "QModelIndex" }
+ Parameter { name: "column"; type: "int" }
+ }
+ Method { name: "submit"; type: "bool" }
+ Method { name: "revert" }
+ Method {
+ name: "hasIndex"
+ type: "bool"
+ Parameter { name: "row"; type: "int" }
+ Parameter { name: "column"; type: "int" }
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method {
+ name: "hasIndex"
+ type: "bool"
+ Parameter { name: "row"; type: "int" }
+ Parameter { name: "column"; type: "int" }
+ }
+ Method {
+ name: "index"
+ type: "QModelIndex"
+ Parameter { name: "row"; type: "int" }
+ Parameter { name: "column"; type: "int" }
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method {
+ name: "index"
+ type: "QModelIndex"
+ Parameter { name: "row"; type: "int" }
+ Parameter { name: "column"; type: "int" }
+ }
+ Method {
+ name: "parent"
+ type: "QModelIndex"
+ Parameter { name: "child"; type: "QModelIndex" }
+ }
+ Method {
+ name: "sibling"
+ type: "QModelIndex"
+ Parameter { name: "row"; type: "int" }
+ Parameter { name: "column"; type: "int" }
+ Parameter { name: "idx"; type: "QModelIndex" }
+ }
+ Method {
+ name: "rowCount"
+ type: "int"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method { name: "rowCount"; type: "int" }
+ Method {
+ name: "columnCount"
+ type: "int"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method { name: "columnCount"; type: "int" }
+ Method {
+ name: "hasChildren"
+ type: "bool"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method { name: "hasChildren"; type: "bool" }
+ Method {
+ name: "data"
+ type: "QVariant"
+ Parameter { name: "index"; type: "QModelIndex" }
+ Parameter { name: "role"; type: "int" }
+ }
+ Method {
+ name: "data"
+ type: "QVariant"
+ Parameter { name: "index"; type: "QModelIndex" }
+ }
+ Method {
+ name: "setData"
+ type: "bool"
+ Parameter { name: "index"; type: "QModelIndex" }
+ Parameter { name: "value"; type: "QVariant" }
+ Parameter { name: "role"; type: "int" }
+ }
+ Method {
+ name: "setData"
+ type: "bool"
+ Parameter { name: "index"; type: "QModelIndex" }
+ Parameter { name: "value"; type: "QVariant" }
+ }
+ Method {
+ name: "headerData"
+ type: "QVariant"
+ Parameter { name: "section"; type: "int" }
+ Parameter { name: "orientation"; type: "Qt::Orientation" }
+ Parameter { name: "role"; type: "int" }
+ }
+ Method {
+ name: "headerData"
+ type: "QVariant"
+ Parameter { name: "section"; type: "int" }
+ Parameter { name: "orientation"; type: "Qt::Orientation" }
+ }
+ Method {
+ name: "fetchMore"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method {
+ name: "canFetchMore"
+ type: "bool"
+ Parameter { name: "parent"; type: "QModelIndex" }
+ }
+ Method {
+ name: "flags"
+ type: "Qt::ItemFlags"
+ Parameter { name: "index"; type: "QModelIndex" }
+ }
+ Method {
+ name: "match"
+ type: "QModelIndexList"
+ Parameter { name: "start"; type: "QModelIndex" }
+ Parameter { name: "role"; type: "int" }
+ Parameter { name: "value"; type: "QVariant" }
+ Parameter { name: "hits"; type: "int" }
+ Parameter { name: "flags"; type: "Qt::MatchFlags" }
+ }
+ Method {
+ name: "match"
+ type: "QModelIndexList"
+ Parameter { name: "start"; type: "QModelIndex" }
+ Parameter { name: "role"; type: "int" }
+ Parameter { name: "value"; type: "QVariant" }
+ Parameter { name: "hits"; type: "int" }
+ }
+ Method {
+ name: "match"
+ type: "QModelIndexList"
+ Parameter { name: "start"; type: "QModelIndex" }
+ Parameter { name: "role"; type: "int" }
+ Parameter { name: "value"; type: "QVariant" }
+ }
+ }
+ Component { name: "QAbstractTableModel"; prototype: "QAbstractItemModel" }
+ Component {
name: "QQmlAbstractDelegateComponent"
prototype: "QQmlComponent"
exports: ["Qt.labs.qmlmodels/AbstractDelegateComponent 1.0"]
@@ -38,4 +312,103 @@ Module {
Property { name: "role"; type: "string" }
Property { name: "choices"; type: "QQmlDelegateChoice"; isList: true; isReadonly: true }
}
+ Component {
+ name: "QQmlTableModel"
+ defaultProperty: "columns"
+ prototype: "QAbstractTableModel"
+ exports: ["Qt.labs.qmlmodels/TableModel 1.0"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "columnCount"; type: "int"; isReadonly: true }
+ Property { name: "rowCount"; type: "int"; isReadonly: true }
+ Property { name: "rows"; type: "QVariant" }
+ Property { name: "columns"; type: "QQmlTableModelColumn"; isList: true; isReadonly: true }
+ Method {
+ name: "appendRow"
+ Parameter { name: "row"; type: "QVariant" }
+ }
+ Method { name: "clear" }
+ Method {
+ name: "getRow"
+ type: "QVariant"
+ Parameter { name: "rowIndex"; type: "int" }
+ }
+ Method {
+ name: "insertRow"
+ Parameter { name: "rowIndex"; type: "int" }
+ Parameter { name: "row"; type: "QVariant" }
+ }
+ Method {
+ name: "moveRow"
+ Parameter { name: "fromRowIndex"; type: "int" }
+ Parameter { name: "toRowIndex"; type: "int" }
+ Parameter { name: "rows"; type: "int" }
+ }
+ Method {
+ name: "moveRow"
+ Parameter { name: "fromRowIndex"; type: "int" }
+ Parameter { name: "toRowIndex"; type: "int" }
+ }
+ Method {
+ name: "removeRow"
+ Parameter { name: "rowIndex"; type: "int" }
+ Parameter { name: "rows"; type: "int" }
+ }
+ Method {
+ name: "removeRow"
+ Parameter { name: "rowIndex"; type: "int" }
+ }
+ Method {
+ name: "setRow"
+ Parameter { name: "rowIndex"; type: "int" }
+ Parameter { name: "row"; type: "QVariant" }
+ }
+ Method {
+ name: "data"
+ type: "QVariant"
+ Parameter { name: "index"; type: "QModelIndex" }
+ Parameter { name: "role"; type: "string" }
+ }
+ Method {
+ name: "setData"
+ type: "bool"
+ Parameter { name: "index"; type: "QModelIndex" }
+ Parameter { name: "role"; type: "string" }
+ Parameter { name: "value"; type: "QVariant" }
+ }
+ }
+ Component {
+ name: "QQmlTableModelColumn"
+ prototype: "QObject"
+ exports: ["Qt.labs.qmlmodels/TableModelColumn 1.0"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "display"; type: "QJSValue" }
+ Property { name: "setDisplay"; type: "QJSValue" }
+ Property { name: "decoration"; type: "QJSValue" }
+ Property { name: "setDecoration"; type: "QJSValue" }
+ Property { name: "edit"; type: "QJSValue" }
+ Property { name: "setEdit"; type: "QJSValue" }
+ Property { name: "toolTip"; type: "QJSValue" }
+ Property { name: "setToolTip"; type: "QJSValue" }
+ Property { name: "statusTip"; type: "QJSValue" }
+ Property { name: "setStatusTip"; type: "QJSValue" }
+ Property { name: "whatsThis"; type: "QJSValue" }
+ Property { name: "setWhatsThis"; type: "QJSValue" }
+ Property { name: "font"; type: "QJSValue" }
+ Property { name: "setFont"; type: "QJSValue" }
+ Property { name: "textAlignment"; type: "QJSValue" }
+ Property { name: "setTextAlignment"; type: "QJSValue" }
+ Property { name: "background"; type: "QJSValue" }
+ Property { name: "setBackground"; type: "QJSValue" }
+ Property { name: "foreground"; type: "QJSValue" }
+ Property { name: "setForeground"; type: "QJSValue" }
+ Property { name: "checkState"; type: "QJSValue" }
+ Property { name: "setCheckState"; type: "QJSValue" }
+ Property { name: "accessibleText"; type: "QJSValue" }
+ Property { name: "setAccessibleText"; type: "QJSValue" }
+ Property { name: "accessibleDescription"; type: "QJSValue" }
+ Property { name: "setAccessibleDescription"; type: "QJSValue" }
+ Property { name: "sizeHint"; type: "QJSValue" }
+ Property { name: "setSizeHint"; type: "QJSValue" }
+ Signal { name: "indexChanged" }
+ }
}
diff --git a/src/imports/layouts/plugins.qmltypes b/src/imports/layouts/plugins.qmltypes
index 22e8d79ece..6015164511 100644
--- a/src/imports/layouts/plugins.qmltypes
+++ b/src/imports/layouts/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtQuick.Layouts 1.13'
+// 'qmlplugindump -nonrelocatable QtQuick.Layouts 1.14'
Module {
dependencies: ["QtQuick 2.0"]
diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp
index 3c34d8e45a..9004d1ee6f 100644
--- a/src/imports/localstorage/plugin.cpp
+++ b/src/imports/localstorage/plugin.cpp
@@ -41,7 +41,6 @@
#include <QtQml/qqml.h>
#include <private/qqmlengine_p.h>
#include <QDebug>
-#include <private/qv8engine_p.h>
#include <QtSql/qsqldatabase.h>
#include <QtSql/qsqlquery.h>
#include <QtSql/qsqlerror.h>
@@ -86,7 +85,7 @@ QT_BEGIN_NAMESPACE
}
-class QQmlSqlDatabaseData : public QV8Engine::Deletable
+class QQmlSqlDatabaseData : public QV4::ExecutionEngine::Deletable
{
public:
QQmlSqlDatabaseData(QV4::ExecutionEngine *engine);
diff --git a/src/imports/localstorage/plugins.qmltypes b/src/imports/localstorage/plugins.qmltypes
index 3c8c1404f2..6ed334cc9d 100644
--- a/src/imports/localstorage/plugins.qmltypes
+++ b/src/imports/localstorage/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQuick.LocalStorage 2.13'
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQuick.LocalStorage 2.14'
Module {
dependencies: []
diff --git a/src/imports/models/models.pro b/src/imports/models/models.pro
index fc87533cea..fd13b12401 100644
--- a/src/imports/models/models.pro
+++ b/src/imports/models/models.pro
@@ -6,6 +6,6 @@ IMPORT_VERSION = 2.$$QT_MINOR_VERSION
SOURCES += \
plugin.cpp
-QT = qml-private
+QT = qml-private qmlmodels-private
load(qml_plugin)
diff --git a/src/imports/models/plugins.qmltypes b/src/imports/models/plugins.qmltypes
index 6e112c41b6..58122b9b45 100644
--- a/src/imports/models/plugins.qmltypes
+++ b/src/imports/models/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQml.Models 2.13'
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQml.Models 2.14'
Module {
dependencies: []
@@ -441,8 +441,8 @@ Module {
Signal { name: "defaultIncludeChanged" }
Signal {
name: "changed"
- Parameter { name: "removed"; type: "QQmlV4Handle" }
- Parameter { name: "inserted"; type: "QQmlV4Handle" }
+ Parameter { name: "removed"; type: "QJSValue" }
+ Parameter { name: "inserted"; type: "QJSValue" }
}
Method {
name: "insert"
@@ -478,12 +478,64 @@ Module {
}
Method {
name: "get"
- type: "QQmlV4Handle"
+ type: "QJSValue"
Parameter { name: "index"; type: "int" }
}
}
Component { name: "QQmlDelegateModelParts"; prototype: "QObject" }
Component {
+ name: "QQmlInstanceModel"
+ prototype: "QObject"
+ Property { name: "count"; type: "int"; isReadonly: true }
+ Signal {
+ name: "modelUpdated"
+ Parameter { name: "changeSet"; type: "QQmlChangeSet" }
+ Parameter { name: "reset"; type: "bool" }
+ }
+ Signal {
+ name: "createdItem"
+ Parameter { name: "index"; type: "int" }
+ Parameter { name: "object"; type: "QObject"; isPointer: true }
+ }
+ Signal {
+ name: "initItem"
+ Parameter { name: "index"; type: "int" }
+ Parameter { name: "object"; type: "QObject"; isPointer: true }
+ }
+ Signal {
+ name: "destroyingItem"
+ Parameter { name: "object"; type: "QObject"; isPointer: true }
+ }
+ }
+ Component {
+ name: "QQmlInstantiator"
+ defaultProperty: "delegate"
+ prototype: "QObject"
+ exports: ["QtQml.Models/Instantiator 2.14"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "active"; type: "bool" }
+ Property { name: "asynchronous"; type: "bool" }
+ Property { name: "model"; type: "QVariant" }
+ Property { name: "count"; type: "int"; isReadonly: true }
+ Property { name: "delegate"; type: "QQmlComponent"; isPointer: true }
+ Property { name: "object"; type: "QObject"; isReadonly: true; isPointer: true }
+ Signal {
+ name: "objectAdded"
+ Parameter { name: "index"; type: "int" }
+ Parameter { name: "object"; type: "QObject"; isPointer: true }
+ }
+ Signal {
+ name: "objectRemoved"
+ Parameter { name: "index"; type: "int" }
+ Parameter { name: "object"; type: "QObject"; isPointer: true }
+ }
+ Method {
+ name: "objectAt"
+ type: "QObject*"
+ Parameter { name: "index"; type: "int" }
+ }
+ }
+ Component {
name: "QQmlListElement"
prototype: "QObject"
exports: ["QtQml.Models/ListElement 2.1"]
@@ -496,6 +548,7 @@ Module {
exportMetaObjectRevisions: [0]
Property { name: "count"; type: "int"; isReadonly: true }
Property { name: "dynamicRoles"; type: "bool" }
+ Property { name: "agent"; revision: 14; type: "QObject"; isReadonly: true; isPointer: true }
Method { name: "clear" }
Method {
name: "remove"
@@ -511,13 +564,61 @@ Module {
}
Method {
name: "get"
- type: "QQmlV4Handle"
+ type: "QJSValue"
Parameter { name: "index"; type: "int" }
}
Method {
name: "set"
Parameter { name: "index"; type: "int" }
- Parameter { type: "QQmlV4Handle" }
+ Parameter { name: "value"; type: "QJSValue" }
+ }
+ Method {
+ name: "setProperty"
+ Parameter { name: "index"; type: "int" }
+ Parameter { name: "property"; type: "string" }
+ Parameter { name: "value"; type: "QVariant" }
+ }
+ Method {
+ name: "move"
+ Parameter { name: "from"; type: "int" }
+ Parameter { name: "to"; type: "int" }
+ Parameter { name: "count"; type: "int" }
+ }
+ Method { name: "sync" }
+ }
+ Component {
+ name: "QQmlListModelWorkerAgent"
+ prototype: "QObject"
+ Property { name: "count"; type: "int"; isReadonly: true }
+ Property { name: "engine"; type: "QV4::ExecutionEngine"; isPointer: true }
+ Signal {
+ name: "engineChanged"
+ Parameter { name: "engine"; type: "QV4::ExecutionEngine"; isPointer: true }
+ }
+ Method { name: "addref" }
+ Method { name: "release" }
+ Method { name: "clear" }
+ Method {
+ name: "remove"
+ Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true }
+ }
+ Method {
+ name: "append"
+ Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true }
+ }
+ Method {
+ name: "insert"
+ Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true }
+ }
+ Method {
+ name: "get"
+ type: "QJSValue"
+ Parameter { name: "index"; type: "int" }
+ }
+ Method {
+ name: "set"
+ Parameter { name: "index"; type: "int" }
+ Parameter { name: "value"; type: "QJSValue" }
}
Method {
name: "setProperty"
@@ -592,4 +693,18 @@ Module {
prototype: "QObject"
Property { name: "index"; type: "int"; isReadonly: true }
}
+ Component {
+ name: "QQuickPackage"
+ defaultProperty: "data"
+ prototype: "QObject"
+ exports: ["QtQml.Models/Package 2.14"]
+ exportMetaObjectRevisions: [0]
+ attachedType: "QQuickPackageAttached"
+ Property { name: "data"; type: "QObject"; isList: true; isReadonly: true }
+ }
+ Component {
+ name: "QQuickPackageAttached"
+ prototype: "QObject"
+ Property { name: "name"; type: "string" }
+ }
}
diff --git a/src/imports/particles/plugins.qmltypes b/src/imports/particles/plugins.qmltypes
index b6db00e683..0a5e124c3c 100644
--- a/src/imports/particles/plugins.qmltypes
+++ b/src/imports/particles/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtQuick.Particles 2.13'
+// 'qmlplugindump -nonrelocatable QtQuick.Particles 2.14'
Module {
dependencies: ["QtQuick 2.0"]
@@ -165,7 +165,7 @@ Module {
Property { name: "acceleration"; type: "QQuickDirection"; isPointer: true }
Signal {
name: "affectParticles"
- Parameter { name: "particles"; type: "QQmlV4Handle" }
+ Parameter { name: "particles"; type: "QJSValue" }
Parameter { name: "dt"; type: "double" }
}
Signal {
@@ -668,7 +668,7 @@ Module {
Property { name: "velocityFromMovement"; type: "double" }
Signal {
name: "emitParticles"
- Parameter { name: "particles"; type: "QQmlV4Handle" }
+ Parameter { name: "particles"; type: "QJSValue" }
}
Signal {
name: "particlesPerSecondChanged"
@@ -997,6 +997,56 @@ Module {
}
}
Component {
+ name: "QQuickStochasticState"
+ prototype: "QObject"
+ Property { name: "duration"; type: "int" }
+ Property { name: "durationVariation"; type: "int" }
+ Property { name: "randomStart"; type: "bool" }
+ Property { name: "to"; type: "QVariantMap" }
+ Property { name: "name"; type: "string" }
+ Signal {
+ name: "durationChanged"
+ Parameter { name: "arg"; type: "int" }
+ }
+ Signal {
+ name: "nameChanged"
+ Parameter { name: "arg"; type: "string" }
+ }
+ Signal {
+ name: "toChanged"
+ Parameter { name: "arg"; type: "QVariantMap" }
+ }
+ Signal {
+ name: "durationVariationChanged"
+ Parameter { name: "arg"; type: "int" }
+ }
+ Signal { name: "entered" }
+ Signal {
+ name: "randomStartChanged"
+ Parameter { name: "arg"; type: "bool" }
+ }
+ Method {
+ name: "setDuration"
+ Parameter { name: "arg"; type: "int" }
+ }
+ Method {
+ name: "setName"
+ Parameter { name: "arg"; type: "string" }
+ }
+ Method {
+ name: "setTo"
+ Parameter { name: "arg"; type: "QVariantMap" }
+ }
+ Method {
+ name: "setDurationVariation"
+ Parameter { name: "arg"; type: "int" }
+ }
+ Method {
+ name: "setRandomStart"
+ Parameter { name: "arg"; type: "bool" }
+ }
+ }
+ Component {
name: "QQuickTargetDirection"
prototype: "QQuickDirection"
exports: ["QtQuick.Particles/TargetDirection 2.0"]
@@ -1084,8 +1134,8 @@ Module {
Property { name: "emitWidth"; type: "double" }
Signal {
name: "emitFollowParticles"
- Parameter { name: "particles"; type: "QQmlV4Handle" }
- Parameter { name: "followed"; type: "QQmlV4Handle" }
+ Parameter { name: "particles"; type: "QJSValue" }
+ Parameter { name: "followed"; type: "QJSValue" }
}
Signal {
name: "particlesPerParticlePerSecondChanged"
diff --git a/src/imports/qtqml/dependencies.json b/src/imports/qtqml/dependencies.json
new file mode 100644
index 0000000000..0d4f101c7a
--- /dev/null
+++ b/src/imports/qtqml/dependencies.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/src/imports/qtqml/plugin.cpp b/src/imports/qtqml/plugin.cpp
new file mode 100644
index 0000000000..7595d6d65b
--- /dev/null
+++ b/src/imports/qtqml/plugin.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/private/qqmlengine_p.h>
+#include <QtQml/private/qqmlcomponentattached_p.h>
+#include <QtQml/private/qqmlbind_p.h>
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+#include <QtQmlModels/private/qqmlmodelsmodule_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlmodule QtQml 2.\QtMinorVersion
+ \title Qt QML Base Types
+ \ingroup qmlmodules
+ \brief Provides basic QML types
+ \since 5.0
+
+ This QML module contains basic QML types.
+
+ To use the types in this module, import the module with the following line:
+
+ \qml \QtMinorVersion
+ import QtQml 2.\1
+ \endqml
+*/
+
+//![class decl]
+class QtQmlPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+public:
+ QtQmlPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { }
+ void registerTypes(const char *uri) override
+ {
+ Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQml"));
+ QQmlEnginePrivate::defineModule();
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ QQmlModelsModule::registerQmlTypes();
+#endif
+
+ // Auto-increment the import to stay in sync with ALL future QtQml minor versions from 5.11 onward
+ qmlRegisterModule(uri, 2, QT_VERSION_MINOR);
+ }
+};
+//![class decl]
+
+QT_END_NAMESPACE
+
+#include "plugin.moc"
diff --git a/src/imports/qtqml/plugins.qmltypes b/src/imports/qtqml/plugins.qmltypes
index d548a78dd0..7bccfea26a 100644
--- a/src/imports/qtqml/plugins.qmltypes
+++ b/src/imports/qtqml/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable -noforceqtquick QtQml 2.13'
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQml 2.14'
Module {
dependencies: []
@@ -27,19 +27,33 @@ Module {
Component {
name: "QQmlBind"
prototype: "QObject"
- exports: ["QtQml/Binding 2.0", "QtQml/Binding 2.8"]
- exportMetaObjectRevisions: [0, 8]
+ exports: [
+ "QtQml/Binding 2.0",
+ "QtQml/Binding 2.14",
+ "QtQml/Binding 2.8"
+ ]
+ exportMetaObjectRevisions: [0, 14, 8]
+ Enum {
+ name: "RestorationMode"
+ values: {
+ "RestoreNone": 0,
+ "RestoreBinding": 1,
+ "RestoreValue": 2,
+ "RestoreBindingOrValue": 3
+ }
+ }
Property { name: "target"; type: "QObject"; isPointer: true }
Property { name: "property"; type: "string" }
Property { name: "value"; type: "QVariant" }
Property { name: "when"; type: "bool" }
Property { name: "delayed"; revision: 8; type: "bool" }
+ Property { name: "restoreMode"; revision: 14; type: "RestorationMode" }
}
Component {
name: "QQmlComponent"
prototype: "QObject"
- exports: ["QML/Component 1.0", "QtQml/Component 2.0"]
- exportMetaObjectRevisions: [0, 0]
+ exports: ["QtQml/Component 2.0"]
+ exportMetaObjectRevisions: [0]
attachedType: "QQmlComponentAttached"
Enum {
name: "CompilationMode"
@@ -94,11 +108,11 @@ Module {
name: "QQmlConnections"
prototype: "QObject"
exports: ["QtQml/Connections 2.0", "QtQml/Connections 2.3"]
- exportMetaObjectRevisions: [0, 1]
+ exportMetaObjectRevisions: [0, 3]
Property { name: "target"; type: "QObject"; isPointer: true }
- Property { name: "enabled"; revision: 1; type: "bool" }
+ Property { name: "enabled"; revision: 3; type: "bool" }
Property { name: "ignoreUnknownSignals"; type: "bool" }
- Signal { name: "enabledChanged"; revision: 1 }
+ Signal { name: "enabledChanged"; revision: 3 }
}
Component {
name: "QQmlInstanceModel"
@@ -199,7 +213,7 @@ Module {
name: "QQmlLoggingCategory"
prototype: "QObject"
exports: ["QtQml/LoggingCategory 2.12", "QtQml/LoggingCategory 2.8"]
- exportMetaObjectRevisions: [1, 0]
+ exportMetaObjectRevisions: [12, 0]
Enum {
name: "DefaultLogLevel"
values: {
@@ -211,7 +225,7 @@ Module {
}
}
Property { name: "name"; type: "string" }
- Property { name: "defaultLogLevel"; revision: 1; type: "DefaultLogLevel" }
+ Property { name: "defaultLogLevel"; revision: 12; type: "DefaultLogLevel" }
}
Component {
name: "QQmlTimer"
@@ -228,18 +242,4 @@ Module {
Method { name: "stop" }
Method { name: "restart" }
}
- Component {
- name: "QQuickMouseEvent"
- prototype: "QObject"
- Property { name: "x"; type: "double"; isReadonly: true }
- Property { name: "y"; type: "double"; isReadonly: true }
- Property { name: "button"; type: "int"; isReadonly: true }
- Property { name: "buttons"; type: "int"; isReadonly: true }
- Property { name: "modifiers"; type: "int"; isReadonly: true }
- Property { name: "source"; revision: 7; type: "int"; isReadonly: true }
- Property { name: "wasHeld"; type: "bool"; isReadonly: true }
- Property { name: "isClick"; type: "bool"; isReadonly: true }
- Property { name: "accepted"; type: "bool" }
- Property { name: "flags"; revision: 11; type: "int"; isReadonly: true }
- }
}
diff --git a/src/imports/qtqml/qmldir b/src/imports/qtqml/qmldir
index 8175ebb1a1..f6b51c7970 100644
--- a/src/imports/qtqml/qmldir
+++ b/src/imports/qtqml/qmldir
@@ -1,2 +1,4 @@
module QtQml
+plugin qmlplugin
+classname QtQmlPlugin
typeinfo plugins.qmltypes
diff --git a/src/imports/qtqml/qtqml.pro b/src/imports/qtqml/qtqml.pro
index c00172ddc4..7a5169b8fc 100644
--- a/src/imports/qtqml/qtqml.pro
+++ b/src/imports/qtqml/qtqml.pro
@@ -1,12 +1,12 @@
TARGETPATH = QtQml
-AUX_QML_FILES += plugins.qmltypes
+CXX_MODULE = qml
+TARGET = qmlplugin
+IMPORT_VERSION = 2.$$QT_MINOR_VERSION
-load(qml_module)
+SOURCES += \
+ plugin.cpp
-# qmltypes target
-!cross_compile:if(build_pass|!debug_and_release) {
- qtPrepareTool(QMLPLUGINDUMP, qmlplugindump)
+# In Qt6 we won't need qmlmodels-private here
+QT = qml-private qmlmodels-private
- qmltypes.commands = $$QMLPLUGINDUMP -nonrelocatable -noforceqtquick QtQml 2.$$QT_MINOR_VERSION > $$PWD/plugins.qmltypes
- QMAKE_EXTRA_TARGETS += qmltypes
-}
+load(qml_plugin)
diff --git a/src/imports/qtquick2/plugin.cpp b/src/imports/qtquick2/plugin.cpp
index d73a8b3688..efa05c349c 100644
--- a/src/imports/qtquick2/plugin.cpp
+++ b/src/imports/qtquick2/plugin.cpp
@@ -39,6 +39,14 @@
#include <QtQml/qqmlextensionplugin.h>
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+#include <QtQml/private/qqmlengine_p.h>
+#include <QtQmlModels/private/qqmlmodelsmodule_p.h>
+#if QT_CONFIG(qml_worker_script)
+#include <QtQmlWorkerScript/private/qqmlworkerscriptmodule_p.h>
+#endif
+#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+
#include <private/qtquick2_p.h>
QT_BEGIN_NAMESPACE
@@ -55,7 +63,17 @@ public:
Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick"));
Q_UNUSED(uri);
moduleDefined = true;
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ QQmlEnginePrivate::registerQuickTypes();
+ QQmlModelsModule::registerQuickTypes();
+#if QT_CONFIG(qml_worker_script)
+ QQmlWorkerScriptModule::registerQuickTypes();
+#endif
+#endif
QQmlQtQuick2Module::defineModule();
+
+ // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward
+ qmlRegisterModule("QtQuick", 2, QT_VERSION_MINOR);
}
~QtQuick2Plugin() override
diff --git a/src/imports/qtquick2/plugins.qmltypes b/src/imports/qtquick2/plugins.qmltypes
index f006c874da..0ba918e34e 100644
--- a/src/imports/qtquick2/plugins.qmltypes
+++ b/src/imports/qtquick2/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQuick 2.13'
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQuick 2.14'
Module {
dependencies: []
@@ -449,6 +449,22 @@ Module {
}
}
Component {
+ name: "QObject"
+ exports: ["QtQuick/QtObject 2.0"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "objectName"; type: "string" }
+ Signal {
+ name: "objectNameChanged"
+ Parameter { name: "objectName"; type: "string" }
+ }
+ Method { name: "toString" }
+ Method { name: "destroy" }
+ Method {
+ name: "destroy"
+ Parameter { name: "delay"; type: "int" }
+ }
+ }
+ Component {
name: "QPointingDeviceUniqueId"
exports: ["QtQuick/PointingDeviceUniqueId 2.9"]
isCreatable: false
@@ -482,6 +498,92 @@ Module {
}
}
Component {
+ name: "QQmlBind"
+ prototype: "QObject"
+ exports: ["QtQuick/Binding 2.0", "QtQuick/Binding 2.8"]
+ exportMetaObjectRevisions: [0, 8]
+ Enum {
+ name: "RestorationMode"
+ values: {
+ "RestoreNone": 0,
+ "RestoreBinding": 1,
+ "RestoreValue": 2,
+ "RestoreBindingOrValue": 3
+ }
+ }
+ Property { name: "target"; type: "QObject"; isPointer: true }
+ Property { name: "property"; type: "string" }
+ Property { name: "value"; type: "QVariant" }
+ Property { name: "when"; type: "bool" }
+ Property { name: "delayed"; revision: 8; type: "bool" }
+ Property { name: "restoreMode"; revision: 14; type: "RestorationMode" }
+ }
+ Component {
+ name: "QQmlComponent"
+ prototype: "QObject"
+ exports: ["QtQuick/Component 2.0"]
+ exportMetaObjectRevisions: [0]
+ attachedType: "QQmlComponentAttached"
+ Enum {
+ name: "CompilationMode"
+ values: {
+ "PreferSynchronous": 0,
+ "Asynchronous": 1
+ }
+ }
+ Enum {
+ name: "Status"
+ values: {
+ "Null": 0,
+ "Ready": 1,
+ "Loading": 2,
+ "Error": 3
+ }
+ }
+ Property { name: "progress"; type: "double"; isReadonly: true }
+ Property { name: "status"; type: "Status"; isReadonly: true }
+ Property { name: "url"; type: "QUrl"; isReadonly: true }
+ Signal {
+ name: "statusChanged"
+ Parameter { type: "QQmlComponent::Status" }
+ }
+ Signal {
+ name: "progressChanged"
+ Parameter { type: "double" }
+ }
+ Method {
+ name: "loadUrl"
+ Parameter { name: "url"; type: "QUrl" }
+ }
+ Method {
+ name: "loadUrl"
+ Parameter { name: "url"; type: "QUrl" }
+ Parameter { name: "mode"; type: "CompilationMode" }
+ }
+ Method {
+ name: "setData"
+ Parameter { type: "QByteArray" }
+ Parameter { name: "baseUrl"; type: "QUrl" }
+ }
+ Method { name: "errorString"; type: "string" }
+ }
+ Component {
+ name: "QQmlComponentAttached"
+ prototype: "QObject"
+ Signal { name: "completed" }
+ Signal { name: "destruction" }
+ }
+ Component {
+ name: "QQmlConnections"
+ prototype: "QObject"
+ exports: ["QtQuick/Connections 2.0", "QtQuick/Connections 2.7"]
+ exportMetaObjectRevisions: [0, 3]
+ Property { name: "target"; type: "QObject"; isPointer: true }
+ Property { name: "enabled"; revision: 3; type: "bool" }
+ Property { name: "ignoreUnknownSignals"; type: "bool" }
+ Signal { name: "enabledChanged"; revision: 3 }
+ }
+ Component {
name: "QQmlDelegateModel"
defaultProperty: "delegate"
prototype: "QQmlInstanceModel"
@@ -529,8 +631,8 @@ Module {
Signal { name: "defaultIncludeChanged" }
Signal {
name: "changed"
- Parameter { name: "removed"; type: "QQmlV4Handle" }
- Parameter { name: "inserted"; type: "QQmlV4Handle" }
+ Parameter { name: "removed"; type: "QJSValue" }
+ Parameter { name: "inserted"; type: "QJSValue" }
}
Method {
name: "insert"
@@ -566,7 +668,7 @@ Module {
}
Method {
name: "get"
- type: "QQmlV4Handle"
+ type: "QJSValue"
Parameter { name: "index"; type: "int" }
}
}
@@ -634,6 +736,58 @@ Module {
Property { name: "bezierCurve"; type: "QVariantList" }
}
Component {
+ name: "QQmlInstanceModel"
+ prototype: "QObject"
+ Property { name: "count"; type: "int"; isReadonly: true }
+ Signal {
+ name: "modelUpdated"
+ Parameter { name: "changeSet"; type: "QQmlChangeSet" }
+ Parameter { name: "reset"; type: "bool" }
+ }
+ Signal {
+ name: "createdItem"
+ Parameter { name: "index"; type: "int" }
+ Parameter { name: "object"; type: "QObject"; isPointer: true }
+ }
+ Signal {
+ name: "initItem"
+ Parameter { name: "index"; type: "int" }
+ Parameter { name: "object"; type: "QObject"; isPointer: true }
+ }
+ Signal {
+ name: "destroyingItem"
+ Parameter { name: "object"; type: "QObject"; isPointer: true }
+ }
+ }
+ Component {
+ name: "QQmlInstantiator"
+ defaultProperty: "delegate"
+ prototype: "QObject"
+ exports: ["QtQuick/Instantiator 2.1"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "active"; type: "bool" }
+ Property { name: "asynchronous"; type: "bool" }
+ Property { name: "model"; type: "QVariant" }
+ Property { name: "count"; type: "int"; isReadonly: true }
+ Property { name: "delegate"; type: "QQmlComponent"; isPointer: true }
+ Property { name: "object"; type: "QObject"; isReadonly: true; isPointer: true }
+ Signal {
+ name: "objectAdded"
+ Parameter { name: "index"; type: "int" }
+ Parameter { name: "object"; type: "QObject"; isPointer: true }
+ }
+ Signal {
+ name: "objectRemoved"
+ Parameter { name: "index"; type: "int" }
+ Parameter { name: "object"; type: "QObject"; isPointer: true }
+ }
+ Method {
+ name: "objectAt"
+ type: "QObject*"
+ Parameter { name: "index"; type: "int" }
+ }
+ }
+ Component {
name: "QQmlListElement"
prototype: "QObject"
exports: ["QtQuick/ListElement 2.0"]
@@ -646,6 +800,55 @@ Module {
exportMetaObjectRevisions: [0]
Property { name: "count"; type: "int"; isReadonly: true }
Property { name: "dynamicRoles"; type: "bool" }
+ Property { name: "agent"; revision: 14; type: "QObject"; isReadonly: true; isPointer: true }
+ Method { name: "clear" }
+ Method {
+ name: "remove"
+ Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true }
+ }
+ Method {
+ name: "append"
+ Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true }
+ }
+ Method {
+ name: "insert"
+ Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true }
+ }
+ Method {
+ name: "get"
+ type: "QJSValue"
+ Parameter { name: "index"; type: "int" }
+ }
+ Method {
+ name: "set"
+ Parameter { name: "index"; type: "int" }
+ Parameter { name: "value"; type: "QJSValue" }
+ }
+ Method {
+ name: "setProperty"
+ Parameter { name: "index"; type: "int" }
+ Parameter { name: "property"; type: "string" }
+ Parameter { name: "value"; type: "QVariant" }
+ }
+ Method {
+ name: "move"
+ Parameter { name: "from"; type: "int" }
+ Parameter { name: "to"; type: "int" }
+ Parameter { name: "count"; type: "int" }
+ }
+ Method { name: "sync" }
+ }
+ Component {
+ name: "QQmlListModelWorkerAgent"
+ prototype: "QObject"
+ Property { name: "count"; type: "int"; isReadonly: true }
+ Property { name: "engine"; type: "QV4::ExecutionEngine"; isPointer: true }
+ Signal {
+ name: "engineChanged"
+ Parameter { name: "engine"; type: "QV4::ExecutionEngine"; isPointer: true }
+ }
+ Method { name: "addref" }
+ Method { name: "release" }
Method { name: "clear" }
Method {
name: "remove"
@@ -661,13 +864,13 @@ Module {
}
Method {
name: "get"
- type: "QQmlV4Handle"
+ type: "QJSValue"
Parameter { name: "index"; type: "int" }
}
Method {
name: "set"
Parameter { name: "index"; type: "int" }
- Parameter { type: "QQmlV4Handle" }
+ Parameter { name: "value"; type: "QJSValue" }
}
Method {
name: "setProperty"
@@ -684,6 +887,70 @@ Module {
Method { name: "sync" }
}
Component {
+ name: "QQmlLocale"
+ exports: ["QtQuick/Locale 2.0"]
+ isCreatable: false
+ exportMetaObjectRevisions: [0]
+ Enum {
+ name: "MeasurementSystem"
+ values: {
+ "MetricSystem": 0,
+ "ImperialSystem": 1,
+ "ImperialUSSystem": 1,
+ "ImperialUKSystem": 2
+ }
+ }
+ Enum {
+ name: "FormatType"
+ values: {
+ "LongFormat": 0,
+ "ShortFormat": 1,
+ "NarrowFormat": 2
+ }
+ }
+ Enum {
+ name: "CurrencySymbolFormat"
+ values: {
+ "CurrencyIsoCode": 0,
+ "CurrencySymbol": 1,
+ "CurrencyDisplayName": 2
+ }
+ }
+ Enum {
+ name: "DayOfWeek"
+ values: {
+ "Sunday": 0,
+ "Monday": 1,
+ "Tuesday": 2,
+ "Wednesday": 3,
+ "Thursday": 4,
+ "Friday": 5,
+ "Saturday": 6
+ }
+ }
+ }
+ Component {
+ name: "QQmlLoggingCategory"
+ prototype: "QObject"
+ exports: [
+ "QtQuick/LoggingCategory 2.12",
+ "QtQuick/LoggingCategory 2.8"
+ ]
+ exportMetaObjectRevisions: [12, 0]
+ Enum {
+ name: "DefaultLogLevel"
+ values: {
+ "Debug": 0,
+ "Info": 4,
+ "Warning": 1,
+ "Critical": 2,
+ "Fatal": 3
+ }
+ }
+ Property { name: "name"; type: "string" }
+ Property { name: "defaultLogLevel"; revision: 12; type: "DefaultLogLevel" }
+ }
+ Component {
name: "QQmlObjectModel"
defaultProperty: "children"
prototype: "QQmlInstanceModel"
@@ -740,6 +1007,21 @@ Module {
Property { name: "index"; type: "int"; isReadonly: true }
}
Component {
+ name: "QQmlTimer"
+ prototype: "QObject"
+ exports: ["QtQuick/Timer 2.0"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "interval"; type: "int" }
+ Property { name: "running"; type: "bool" }
+ Property { name: "repeat"; type: "bool" }
+ Property { name: "triggeredOnStart"; type: "bool" }
+ Property { name: "parent"; type: "QObject"; isReadonly: true; isPointer: true }
+ Signal { name: "triggered" }
+ Method { name: "start" }
+ Method { name: "stop" }
+ Method { name: "restart" }
+ }
+ Component {
name: "QQuickAbstractAnimation"
prototype: "QObject"
exports: ["QtQuick/Animation 2.0", "QtQuick/Animation 2.12"]
@@ -1300,7 +1582,7 @@ Module {
}
Property { name: "available"; type: "bool"; isReadonly: true }
Property { name: "contextType"; type: "string" }
- Property { name: "context"; type: "QQmlV4Handle"; isReadonly: true }
+ Property { name: "context"; type: "QJSValue"; isReadonly: true }
Property { name: "canvasSize"; type: "QSizeF" }
Property { name: "tileSize"; type: "QSize" }
Property { name: "canvasWindow"; type: "QRectF" }
@@ -1478,11 +1760,22 @@ Module {
Component {
name: "QQuickDragHandler"
prototype: "QQuickMultiPointHandler"
- exports: ["QtQuick/DragHandler 2.12"]
- exportMetaObjectRevisions: [0]
+ exports: ["QtQuick/DragHandler 2.12", "QtQuick/DragHandler 2.14"]
+ exportMetaObjectRevisions: [0, 14]
+ Enum {
+ name: "SnapMode"
+ values: {
+ "NoSnap": 0,
+ "SnapAuto": 1,
+ "SnapIfPressedOutsideTarget": 2,
+ "SnapAlways": 3
+ }
+ }
Property { name: "xAxis"; type: "QQuickDragAxis"; isReadonly: true; isPointer: true }
Property { name: "yAxis"; type: "QQuickDragAxis"; isReadonly: true; isPointer: true }
Property { name: "translation"; type: "QVector2D"; isReadonly: true }
+ Property { name: "snapMode"; revision: 14; type: "SnapMode" }
+ Signal { name: "snapModeChanged"; revision: 14 }
}
Component {
name: "QQuickDropArea"
@@ -1959,7 +2252,13 @@ Module {
"Unknown": 0,
"Software": 1,
"OpenGL": 2,
- "Direct3D12": 3
+ "Direct3D12": 3,
+ "OpenVG": 4,
+ "OpenGLRhi": 5,
+ "Direct3D11Rhi": 6,
+ "VulkanRhi": 7,
+ "MetalRhi": 8,
+ "NullRhi": 9
}
}
Enum {
@@ -1967,7 +2266,8 @@ Module {
values: {
"UnknownShadingLanguage": 0,
"GLSL": 1,
- "HLSL": 2
+ "HLSL": 2,
+ "RhiShader": 3
}
}
Enum {
@@ -2128,10 +2428,11 @@ Module {
prototype: "QQuickImageBase"
exports: [
"QtQuick/Image 2.0",
+ "QtQuick/Image 2.14",
"QtQuick/Image 2.3",
"QtQuick/Image 2.5"
]
- exportMetaObjectRevisions: [0, 1, 2]
+ exportMetaObjectRevisions: [0, 14, 3, 5]
Enum {
name: "HAlignment"
values: {
@@ -2165,8 +2466,8 @@ Module {
Property { name: "paintedHeight"; type: "double"; isReadonly: true }
Property { name: "horizontalAlignment"; type: "HAlignment" }
Property { name: "verticalAlignment"; type: "VAlignment" }
- Property { name: "mipmap"; revision: 1; type: "bool" }
- Property { name: "autoTransform"; revision: 2; type: "bool" }
+ Property { name: "mipmap"; revision: 3; type: "bool" }
+ Property { name: "autoTransform"; revision: 5; type: "bool" }
Signal { name: "paintedGeometryChanged" }
Signal {
name: "horizontalAlignmentChanged"
@@ -2178,15 +2479,18 @@ Module {
}
Signal {
name: "mipmapChanged"
- revision: 1
+ revision: 3
Parameter { type: "bool" }
}
- Signal { name: "autoTransformChanged"; revision: 2 }
+ Signal { name: "autoTransformChanged"; revision: 5 }
}
Component {
name: "QQuickImageBase"
defaultProperty: "data"
prototype: "QQuickImplicitSizeItem"
+ exports: ["QtQuick/ImageBase 2.14"]
+ isCreatable: false
+ exportMetaObjectRevisions: [14]
Enum {
name: "Status"
values: {
@@ -2203,6 +2507,8 @@ Module {
Property { name: "cache"; type: "bool" }
Property { name: "sourceSize"; type: "QSize" }
Property { name: "mirror"; type: "bool" }
+ Property { name: "currentFrame"; revision: 14; type: "int" }
+ Property { name: "frameCount"; revision: 14; type: "int"; isReadonly: true }
Signal {
name: "sourceChanged"
Parameter { type: "QUrl" }
@@ -2215,6 +2521,8 @@ Module {
name: "progressChanged"
Parameter { name: "progress"; type: "double" }
}
+ Signal { name: "currentFrameChanged"; revision: 14 }
+ Signal { name: "frameCountChanged"; revision: 14 }
}
Component {
name: "QQuickImplicitSizeItem"
@@ -2242,7 +2550,7 @@ Module {
"QtQuick/Item 2.4",
"QtQuick/Item 2.7"
]
- exportMetaObjectRevisions: [0, 1, 11, 2, 7]
+ exportMetaObjectRevisions: [0, 1, 11, 4, 7]
Enum {
name: "Flags"
values: {
@@ -2362,14 +2670,14 @@ Module {
Method { name: "update" }
Method {
name: "grabToImage"
- revision: 2
+ revision: 4
type: "bool"
Parameter { name: "callback"; type: "QJSValue" }
Parameter { name: "targetSize"; type: "QSize" }
}
Method {
name: "grabToImage"
- revision: 2
+ revision: 4
type: "bool"
Parameter { name: "callback"; type: "QJSValue" }
}
@@ -2502,7 +2810,7 @@ Module {
"QtQuick/ItemView 2.7"
]
isCreatable: false
- exportMetaObjectRevisions: [1, 13, 2, 7]
+ exportMetaObjectRevisions: [1, 13, 3, 7]
Enum {
name: "LayoutDirection"
values: {
@@ -2546,8 +2854,8 @@ Module {
Property { name: "keyNavigationWraps"; type: "bool" }
Property { name: "keyNavigationEnabled"; revision: 7; type: "bool" }
Property { name: "cacheBuffer"; type: "int" }
- Property { name: "displayMarginBeginning"; revision: 2; type: "int" }
- Property { name: "displayMarginEnd"; revision: 2; type: "int" }
+ Property { name: "displayMarginBeginning"; revision: 3; type: "int" }
+ Property { name: "displayMarginEnd"; revision: 3; type: "int" }
Property { name: "layoutDirection"; type: "Qt::LayoutDirection" }
Property { name: "effectiveLayoutDirection"; type: "Qt::LayoutDirection"; isReadonly: true }
Property { name: "verticalLayoutDirection"; type: "VerticalLayoutDirection" }
@@ -2858,7 +3166,7 @@ Module {
"QtQuick/ListView 2.4",
"QtQuick/ListView 2.7"
]
- exportMetaObjectRevisions: [0, 1, 2, 7]
+ exportMetaObjectRevisions: [0, 1, 4, 7]
attachedType: "QQuickListViewAttached"
Enum {
name: "Orientation"
@@ -2899,10 +3207,10 @@ Module {
Property { name: "section"; type: "QQuickViewSection"; isReadonly: true; isPointer: true }
Property { name: "currentSection"; type: "string"; isReadonly: true }
Property { name: "snapMode"; type: "SnapMode" }
- Property { name: "headerPositioning"; revision: 2; type: "HeaderPositioning" }
- Property { name: "footerPositioning"; revision: 2; type: "FooterPositioning" }
- Signal { name: "headerPositioningChanged"; revision: 2 }
- Signal { name: "footerPositioningChanged"; revision: 2 }
+ Property { name: "headerPositioning"; revision: 4; type: "HeaderPositioning" }
+ Property { name: "footerPositioning"; revision: 4; type: "FooterPositioning" }
+ Signal { name: "headerPositioningChanged"; revision: 4 }
+ Signal { name: "footerPositioningChanged"; revision: 4 }
Method { name: "incrementCurrentIndex" }
Method { name: "decrementCurrentIndex" }
}
@@ -2952,13 +3260,13 @@ Module {
"QtQuick/MouseArea 2.5",
"QtQuick/MouseArea 2.9"
]
- exportMetaObjectRevisions: [0, 1, 2, 9]
+ exportMetaObjectRevisions: [0, 4, 5, 9]
Property { name: "mouseX"; type: "double"; isReadonly: true }
Property { name: "mouseY"; type: "double"; isReadonly: true }
Property { name: "containsMouse"; type: "bool"; isReadonly: true }
Property { name: "pressed"; type: "bool"; isReadonly: true }
Property { name: "enabled"; type: "bool" }
- Property { name: "scrollGestureEnabled"; revision: 2; type: "bool" }
+ Property { name: "scrollGestureEnabled"; revision: 5; type: "bool" }
Property { name: "pressedButtons"; type: "Qt::MouseButtons"; isReadonly: true }
Property { name: "acceptedButtons"; type: "Qt::MouseButtons" }
Property { name: "hoverEnabled"; type: "bool" }
@@ -2966,10 +3274,10 @@ Module {
Property { name: "preventStealing"; type: "bool" }
Property { name: "propagateComposedEvents"; type: "bool" }
Property { name: "cursorShape"; type: "Qt::CursorShape" }
- Property { name: "containsPress"; revision: 1; type: "bool"; isReadonly: true }
+ Property { name: "containsPress"; revision: 4; type: "bool"; isReadonly: true }
Property { name: "pressAndHoldInterval"; revision: 9; type: "int" }
Signal { name: "hoveredChanged" }
- Signal { name: "scrollGestureEnabledChanged"; revision: 2 }
+ Signal { name: "scrollGestureEnabledChanged"; revision: 5 }
Signal {
name: "positionChanged"
Parameter { name: "mouse"; type: "QQuickMouseEvent"; isPointer: true }
@@ -3009,10 +3317,24 @@ Module {
Signal { name: "entered" }
Signal { name: "exited" }
Signal { name: "canceled" }
- Signal { name: "containsPressChanged"; revision: 1 }
+ Signal { name: "containsPressChanged"; revision: 4 }
Signal { name: "pressAndHoldIntervalChanged"; revision: 9 }
}
Component {
+ name: "QQuickMouseEvent"
+ prototype: "QObject"
+ Property { name: "x"; type: "double"; isReadonly: true }
+ Property { name: "y"; type: "double"; isReadonly: true }
+ Property { name: "button"; type: "int"; isReadonly: true }
+ Property { name: "buttons"; type: "int"; isReadonly: true }
+ Property { name: "modifiers"; type: "int"; isReadonly: true }
+ Property { name: "source"; revision: 7; type: "int"; isReadonly: true }
+ Property { name: "wasHeld"; type: "bool"; isReadonly: true }
+ Property { name: "isClick"; type: "bool"; isReadonly: true }
+ Property { name: "accepted"; type: "bool" }
+ Property { name: "flags"; revision: 11; type: "int"; isReadonly: true }
+ }
+ Component {
name: "QQuickMultiPointHandler"
prototype: "QQuickPointerDeviceHandler"
Property { name: "minimumPointCount"; type: "int" }
@@ -3172,13 +3494,21 @@ Module {
name: "QQuickPath"
defaultProperty: "pathElements"
prototype: "QObject"
- exports: ["QtQuick/Path 2.0"]
- exportMetaObjectRevisions: [0]
+ exports: ["QtQuick/Path 2.0", "QtQuick/Path 2.14"]
+ exportMetaObjectRevisions: [0, 14]
Property { name: "pathElements"; type: "QQuickPathElement"; isList: true; isReadonly: true }
Property { name: "startX"; type: "double" }
Property { name: "startY"; type: "double" }
Property { name: "closed"; type: "bool"; isReadonly: true }
+ Property { name: "scale"; revision: 14; type: "QSizeF" }
Signal { name: "changed" }
+ Signal { name: "scaleChanged"; revision: 14 }
+ Method {
+ name: "pointAtPercent"
+ revision: 14
+ type: "QPointF"
+ Parameter { name: "t"; type: "double" }
+ }
}
Component {
name: "QQuickPathAngleArc"
@@ -3250,7 +3580,7 @@ Module {
name: "QQuickPathArc"
prototype: "QQuickCurve"
exports: ["QtQuick/PathArc 2.0", "QtQuick/PathArc 2.9"]
- exportMetaObjectRevisions: [0, 2]
+ exportMetaObjectRevisions: [0, 9]
Enum {
name: "ArcDirection"
values: {
@@ -3262,8 +3592,8 @@ Module {
Property { name: "radiusY"; type: "double" }
Property { name: "useLargeArc"; type: "bool" }
Property { name: "direction"; type: "ArcDirection" }
- Property { name: "xAxisRotation"; revision: 2; type: "double" }
- Signal { name: "xAxisRotationChanged"; revision: 2 }
+ Property { name: "xAxisRotation"; revision: 9; type: "double" }
+ Signal { name: "xAxisRotationChanged"; revision: 9 }
}
Component {
name: "QQuickPathAttribute"
@@ -3322,6 +3652,14 @@ Module {
exportMetaObjectRevisions: [0]
}
Component {
+ name: "QQuickPathMultiline"
+ prototype: "QQuickCurve"
+ exports: ["QtQuick/PathMultiline 2.14"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "start"; type: "QPointF"; isReadonly: true }
+ Property { name: "paths"; type: "QVariant" }
+ }
+ Component {
name: "QQuickPathPercent"
prototype: "QQuickPathElement"
exports: ["QtQuick/PathPercent 2.0"]
@@ -3329,6 +3667,14 @@ Module {
Property { name: "value"; type: "double" }
}
Component {
+ name: "QQuickPathPolyline"
+ prototype: "QQuickCurve"
+ exports: ["QtQuick/PathPolyline 2.14"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "start"; type: "QPointF"; isReadonly: true }
+ Property { name: "path"; type: "QVariant" }
+ }
+ Component {
name: "QQuickPathQuad"
prototype: "QQuickCurve"
exports: ["QtQuick/PathQuad 2.0"]
@@ -3508,7 +3854,7 @@ Module {
defaultProperty: "data"
prototype: "QQuickItem"
exports: ["QtQuick/PinchArea 2.0", "QtQuick/PinchArea 2.5"]
- exportMetaObjectRevisions: [0, 1]
+ exportMetaObjectRevisions: [0, 5]
Property { name: "enabled"; type: "bool" }
Property { name: "pinch"; type: "QQuickPinch"; isReadonly: true; isPointer: true }
Signal {
@@ -3525,7 +3871,7 @@ Module {
}
Signal {
name: "smartZoom"
- revision: 1
+ revision: 5
Parameter { name: "pinch"; type: "QQuickPinchEvent"; isPointer: true }
}
}
@@ -3920,7 +4266,7 @@ Module {
defaultProperty: "data"
prototype: "QQuickItem"
exports: ["QtQuick/ShaderEffect 2.0", "QtQuick/ShaderEffect 2.4"]
- exportMetaObjectRevisions: [0, 1]
+ exportMetaObjectRevisions: [0, 4]
Enum {
name: "CullMode"
values: {
@@ -3944,7 +4290,7 @@ Module {
Property { name: "cullMode"; type: "CullMode" }
Property { name: "log"; type: "string"; isReadonly: true }
Property { name: "status"; type: "Status"; isReadonly: true }
- Property { name: "supportsAtlasTextures"; revision: 1; type: "bool" }
+ Property { name: "supportsAtlasTextures"; revision: 4; type: "bool" }
}
Component {
name: "QQuickShaderEffectMesh"
@@ -3963,7 +4309,7 @@ Module {
"QtQuick/ShaderEffectSource 2.6",
"QtQuick/ShaderEffectSource 2.9"
]
- exportMetaObjectRevisions: [0, 1, 2]
+ exportMetaObjectRevisions: [0, 6, 9]
Enum {
name: "WrapMode"
values: {
@@ -3998,8 +4344,8 @@ Module {
Property { name: "hideSource"; type: "bool" }
Property { name: "mipmap"; type: "bool" }
Property { name: "recursive"; type: "bool" }
- Property { name: "textureMirroring"; revision: 1; type: "TextureMirroring" }
- Property { name: "samples"; revision: 2; type: "int" }
+ Property { name: "textureMirroring"; revision: 6; type: "TextureMirroring" }
+ Property { name: "samples"; revision: 9; type: "int" }
Signal { name: "scheduledUpdateCompleted" }
Method { name: "scheduleUpdate" }
}
@@ -4011,11 +4357,11 @@ Module {
"QtQuick/Shortcut 2.6",
"QtQuick/Shortcut 2.9"
]
- exportMetaObjectRevisions: [0, 1, 9]
+ exportMetaObjectRevisions: [0, 6, 9]
Property { name: "sequence"; type: "QVariant" }
Property { name: "sequences"; revision: 9; type: "QVariantList" }
- Property { name: "nativeText"; revision: 1; type: "string"; isReadonly: true }
- Property { name: "portableText"; revision: 1; type: "string"; isReadonly: true }
+ Property { name: "nativeText"; revision: 6; type: "string"; isReadonly: true }
+ Property { name: "portableText"; revision: 6; type: "string"; isReadonly: true }
Property { name: "enabled"; type: "bool" }
Property { name: "autoRepeat"; type: "bool" }
Property { name: "context"; type: "Qt::ShortcutContext" }
@@ -4229,7 +4575,7 @@ Module {
exports: ["QtQuick/State 2.0"]
exportMetaObjectRevisions: [0]
Property { name: "name"; type: "string" }
- Property { name: "when"; type: "QQmlBinding"; isPointer: true }
+ Property { name: "when"; type: "bool" }
Property { name: "extend"; type: "string" }
Property { name: "changes"; type: "QQuickStateOperation"; isList: true; isReadonly: true }
Signal { name: "completed" }
@@ -4340,8 +4686,8 @@ Module {
name: "QQuickTableView"
defaultProperty: "flickableData"
prototype: "QQuickFlickable"
- exports: ["QtQuick/TableView 2.12"]
- exportMetaObjectRevisions: [0]
+ exports: ["QtQuick/TableView 2.12", "QtQuick/TableView 2.14"]
+ exportMetaObjectRevisions: [0, 14]
attachedType: "QQuickTableViewAttached"
Property { name: "rows"; type: "int"; isReadonly: true }
Property { name: "columns"; type: "int"; isReadonly: true }
@@ -4354,6 +4700,10 @@ Module {
Property { name: "reuseItems"; type: "bool" }
Property { name: "contentWidth"; type: "double" }
Property { name: "contentHeight"; type: "double" }
+ Property { name: "syncView"; revision: 14; type: "QQuickTableView"; isPointer: true }
+ Property { name: "syncDirection"; revision: 14; type: "Qt::Orientations" }
+ Signal { name: "syncViewChanged"; revision: 14 }
+ Signal { name: "syncDirectionChanged"; revision: 14 }
Method { name: "forceLayout" }
}
Component {
@@ -4440,6 +4790,7 @@ Module {
values: {
"PlainText": 0,
"RichText": 1,
+ "MarkdownText": 3,
"AutoText": 2,
"StyledText": 4
}
@@ -4631,7 +4982,8 @@ Module {
values: {
"PlainText": 0,
"RichText": 1,
- "AutoText": 2
+ "AutoText": 2,
+ "MarkdownText": 3
}
}
Enum {
@@ -4892,7 +5244,7 @@ Module {
"QtQuick/TextInput 2.7",
"QtQuick/TextInput 2.9"
]
- exportMetaObjectRevisions: [0, 2, 3, 6, 7, 9]
+ exportMetaObjectRevisions: [0, 2, 4, 6, 7, 9]
Enum {
name: "EchoMode"
values: {
@@ -4976,7 +5328,7 @@ Module {
Property { name: "echoMode"; type: "EchoMode" }
Property { name: "activeFocusOnPress"; type: "bool" }
Property { name: "passwordCharacter"; type: "string" }
- Property { name: "passwordMaskDelay"; revision: 3; type: "int" }
+ Property { name: "passwordMaskDelay"; revision: 4; type: "int" }
Property { name: "displayText"; type: "string"; isReadonly: true }
Property { name: "preeditText"; revision: 7; type: "string"; isReadonly: true }
Property { name: "autoScroll"; type: "bool" }
@@ -5036,7 +5388,7 @@ Module {
}
Signal {
name: "passwordMaskDelayChanged"
- revision: 3
+ revision: 4
Parameter { name: "delay"; type: "int" }
}
Signal { name: "preeditTextChanged"; revision: 7 }
@@ -5093,7 +5445,7 @@ Module {
}
Method {
name: "ensureVisible"
- revision: 3
+ revision: 4
Parameter { name: "position"; type: "int" }
}
Method { name: "clear"; revision: 7 }
@@ -5117,7 +5469,7 @@ Module {
}
Method {
name: "inputMethodQuery"
- revision: 3
+ revision: 4
type: "QVariant"
Parameter { name: "query"; type: "Qt::InputMethodQuery" }
Parameter { name: "argument"; type: "QVariant" }
@@ -5272,6 +5624,24 @@ Module {
Property { name: "accepted"; type: "bool" }
}
Component {
+ name: "QQuickWheelHandler"
+ prototype: "QQuickSinglePointHandler"
+ exports: ["QtQuick/WheelHandler 2.14"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "orientation"; type: "Qt::Orientation" }
+ Property { name: "invertible"; type: "bool" }
+ Property { name: "activeTimeout"; type: "double" }
+ Property { name: "rotation"; type: "double" }
+ Property { name: "rotationScale"; type: "double" }
+ Property { name: "property"; type: "string" }
+ Property { name: "targetScaleMultiplier"; type: "double" }
+ Property { name: "targetTransformAroundCursor"; type: "bool" }
+ Signal {
+ name: "wheel"
+ Parameter { name: "event"; type: "QQuickPointerScrollEvent"; isPointer: true }
+ }
+ }
+ Component {
name: "QQuickWorkerScript"
prototype: "QObject"
exports: ["QtQuick/WorkerScript 2.0"]
@@ -5279,7 +5649,7 @@ Module {
Property { name: "source"; type: "QUrl" }
Signal {
name: "message"
- Parameter { name: "messageObject"; type: "QQmlV4Handle" }
+ Parameter { name: "messageObject"; type: "QJSValue" }
}
Method {
name: "sendMessage"
@@ -5310,8 +5680,31 @@ Module {
}
}
Component {
+ name: "QRegularExpressionValidator"
+ prototype: "QValidator"
+ exports: ["QtQuick/RegularExpressionValidator 2.14"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "regularExpression"; type: "QRegularExpression" }
+ Signal {
+ name: "regularExpressionChanged"
+ Parameter { name: "re"; type: "QRegularExpression" }
+ }
+ Method {
+ name: "setRegularExpression"
+ Parameter { name: "re"; type: "QRegularExpression" }
+ }
+ }
+ Component {
name: "QValidator"
prototype: "QObject"
+ Enum {
+ name: "State"
+ values: {
+ "Invalid": 0,
+ "Intermediate": 1,
+ "Acceptable": 2
+ }
+ }
Signal { name: "changed" }
}
}
diff --git a/src/imports/qtquick2/qtquick2.pro b/src/imports/qtquick2/qtquick2.pro
index 744dce4195..8543049ead 100644
--- a/src/imports/qtquick2/qtquick2.pro
+++ b/src/imports/qtquick2/qtquick2.pro
@@ -6,6 +6,8 @@ IMPORT_VERSION = 2.$$QT_MINOR_VERSION
SOURCES += \
plugin.cpp
-QT += quick-private qml-private
+QT += quick-private qml-private qmlmodels-private
+
+qtConfig(qml-worker-script): QT += qmlworkerscript-private
load(qml_plugin)
diff --git a/src/imports/shapes/plugin.cpp b/src/imports/shapes/plugin.cpp
index e3a9017681..0679a70630 100644
--- a/src/imports/shapes/plugin.cpp
+++ b/src/imports/shapes/plugin.cpp
@@ -69,6 +69,9 @@ public:
// revision in Qt 5.11: added containsMode property
qmlRegisterType<QQuickShape, 11>(uri, 1, 11, "Shape");
+
+ // revision in Qt 5.14: added scale property
+ qmlRegisterType<QQuickShapePath, 14>(uri, 1, 14, "ShapePath"); // QTBUG-61942
}
};
diff --git a/src/imports/shapes/plugins.qmltypes b/src/imports/shapes/plugins.qmltypes
index b78c5a1130..cd779e7149 100644
--- a/src/imports/shapes/plugins.qmltypes
+++ b/src/imports/shapes/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtQuick.Shapes 1.13'
+// 'qmlplugindump -nonrelocatable QtQuick.Shapes 1.14'
Module {
dependencies: ["QtQuick 2.0"]
@@ -89,8 +89,11 @@ Module {
name: "QQuickShapePath"
defaultProperty: "pathElements"
prototype: "QQuickPath"
- exports: ["QtQuick.Shapes/ShapePath 1.0"]
- exportMetaObjectRevisions: [0]
+ exports: [
+ "QtQuick.Shapes/ShapePath 1.0",
+ "QtQuick.Shapes/ShapePath 1.14"
+ ]
+ exportMetaObjectRevisions: [0, 14]
Enum {
name: "FillRule"
values: {
@@ -132,6 +135,7 @@ Module {
Property { name: "dashOffset"; type: "double" }
Property { name: "dashPattern"; type: "QVector<qreal>" }
Property { name: "fillGradient"; type: "QQuickShapeGradient"; isPointer: true }
+ Property { name: "scale"; revision: 14; type: "QSizeF" }
Signal { name: "shapePathChanged" }
}
Component {
diff --git a/src/imports/statemachine/plugins.qmltypes b/src/imports/statemachine/plugins.qmltypes
index f92aeaa080..541b1cc114 100644
--- a/src/imports/statemachine/plugins.qmltypes
+++ b/src/imports/statemachine/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQml.StateMachine 1.13'
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQml.StateMachine 1.14'
Module {
dependencies: []
diff --git a/src/imports/statemachine/signaltransition.cpp b/src/imports/statemachine/signaltransition.cpp
index d4ea25cc4b..468f2020c7 100644
--- a/src/imports/statemachine/signaltransition.cpp
+++ b/src/imports/statemachine/signaltransition.cpp
@@ -47,7 +47,6 @@
#include <QQmlExpression>
#include <private/qv4qobjectwrapper_p.h>
-#include <private/qv8engine_p.h>
#include <private/qjsvalue_p.h>
#include <private/qv4scopedvalue_p.h>
#include <private/qqmlcontext_p.h>
@@ -185,7 +184,7 @@ void SignalTransition::connectTriggered()
m_signalExpression = expression;
}
-void SignalTransitionParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props)
+void SignalTransitionParser::verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props)
{
for (int ii = 0; ii < props.count(); ++ii) {
const QV4::CompiledData::Binding *binding = props.at(ii);
@@ -204,7 +203,9 @@ void SignalTransitionParser::verifyBindings(const QQmlRefPointer<QV4::CompiledDa
}
}
-void SignalTransitionParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
+void SignalTransitionParser::applyBindings(
+ QObject *object, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ const QList<const QV4::CompiledData::Binding *> &bindings)
{
SignalTransition *st = qobject_cast<SignalTransition*>(object);
st->m_compilationUnit = compilationUnit;
diff --git a/src/imports/statemachine/signaltransition.h b/src/imports/statemachine/signaltransition.h
index 90e6f96fbc..f005c5e9b1 100644
--- a/src/imports/statemachine/signaltransition.h
+++ b/src/imports/statemachine/signaltransition.h
@@ -89,7 +89,7 @@ private:
QJSValue m_signal;
QQmlScriptString m_guard;
bool m_complete;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> m_compilationUnit;
QList<const QV4::CompiledData::Binding *> m_bindings;
QQmlBoundSignalExpressionPointer m_signalExpression;
};
@@ -97,8 +97,8 @@ private:
class SignalTransitionParser : public QQmlCustomParser
{
public:
- void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) override;
- void applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
+ void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) override;
+ void applyBindings(QObject *object, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
};
QT_END_NAMESPACE
diff --git a/src/imports/statemachine/statemachine.cpp b/src/imports/statemachine/statemachine.cpp
index ca6c59b6ac..a983644018 100644
--- a/src/imports/statemachine/statemachine.cpp
+++ b/src/imports/statemachine/statemachine.cpp
@@ -51,6 +51,7 @@ StateMachine::StateMachine(QObject *parent)
: QStateMachine(parent), m_completed(false), m_running(false)
{
connect(this, SIGNAL(runningChanged(bool)), SIGNAL(qmlRunningChanged()));
+ connect(this, SIGNAL(childModeChanged()), SLOT(checkChildMode()));
}
bool StateMachine::isRunning() const
@@ -66,6 +67,15 @@ void StateMachine::setRunning(bool running)
m_running = running;
}
+void StateMachine::checkChildMode()
+{
+ if (childMode() != QState::ExclusiveStates) {
+ qmlWarning(this) << "Setting the childMode of a StateMachine to anything else than\n"
+ "QState.ExclusiveStates will result in an invalid state machine,\n"
+ "and can lead to incorrect behavior!";
+ }
+}
+
void StateMachine::componentComplete()
{
if (QStateMachine::initialState() == nullptr && childMode() == QState::ExclusiveStates)
@@ -129,6 +139,9 @@ QQmlListProperty<QObject> StateMachine::children()
erroneous state, the machine will stop executing and an error message will
be printed to the console.
+ \warning Setting the childMode of a StateMachine to anything else than QState::ExclusiveStates
+ will result in an invalid state machine, and can lead to incorrect behavior.
+
\clearfloat
\sa QAbstractState, State, SignalTransition, TimeoutTransition, HistoryState {The Declarative State Machine Framework}
diff --git a/src/imports/statemachine/statemachine.h b/src/imports/statemachine/statemachine.h
index cb0ab43f8b..1fa7a9da43 100644
--- a/src/imports/statemachine/statemachine.h
+++ b/src/imports/statemachine/statemachine.h
@@ -69,6 +69,9 @@ public:
bool isRunning() const;
void setRunning(bool running);
+private Q_SLOTS:
+ void checkChildMode();
+
Q_SIGNALS:
void childrenChanged();
/*!
diff --git a/src/imports/testlib/main.cpp b/src/imports/testlib/main.cpp
index c625c87db7..c52cf417a6 100644
--- a/src/imports/testlib/main.cpp
+++ b/src/imports/testlib/main.cpp
@@ -37,6 +37,7 @@
**
****************************************************************************/
+#include <private/qv4scopedvalue_p.h>
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
#include <QtQml/qjsvalue.h>
@@ -80,7 +81,7 @@ Q_SIGNALS:
public Q_SLOTS:
- QQmlV4Handle typeName(const QVariant& v) const
+ QJSValue typeName(const QVariant& v) const
{
QString name(v.typeName());
if (v.canConvert<QObject*>()) {
@@ -97,27 +98,23 @@ public Q_SLOTS:
QQmlEngine *engine = qmlEngine(this);
QV4::ExecutionEngine *v4 = engine->handle();
- QV4::Scope scope(v4);
- QV4::ScopedValue s(scope, v4->newString(name));
- return QQmlV4Handle(s);
+ return QJSValue(v4, v4->newString(name)->asReturnedValue());
}
bool compare(const QVariant& act, const QVariant& exp) const {
return act == exp;
}
- QQmlV4Handle callerFile(int frameIndex = 0) const
+ QJSValue callerFile(int frameIndex = 0) const
{
QQmlEngine *engine = qmlEngine(this);
QV4::ExecutionEngine *v4 = engine->handle();
QV4::Scope scope(v4);
QVector<QV4::StackFrame> stack = v4->stackTrace(frameIndex + 2);
- if (stack.size() > frameIndex + 1) {
- QV4::ScopedValue s(scope, v4->newString(stack.at(frameIndex + 1).source));
- return QQmlV4Handle(s);
- }
- return QQmlV4Handle();
+ return (stack.size() > frameIndex + 1)
+ ? QJSValue(v4, v4->newString(stack.at(frameIndex + 1).source)->asReturnedValue())
+ : QJSValue();
}
int callerLine(int frameIndex = 0) const
{
@@ -153,7 +150,7 @@ public:
qmlRegisterType<QuickTestEvent>(uri,1,0,"TestEvent");
qmlRegisterType<QuickTestEvent>(uri,1,2,"TestEvent");
qmlRegisterType<QuickTestUtil>(uri,1,0,"TestUtil");
- qmlRegisterType<QQuickTouchEventSequence>();
+ qmlRegisterAnonymousType<QQuickTouchEventSequence>(uri, 1);
// Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward
qmlRegisterModule(uri, 1, QT_VERSION_MINOR);
diff --git a/src/imports/testlib/plugins.qmltypes b/src/imports/testlib/plugins.qmltypes
index 1e081d82ff..6a1371e5f1 100644
--- a/src/imports/testlib/plugins.qmltypes
+++ b/src/imports/testlib/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtTest 1.13'
+// 'qmlplugindump -nonrelocatable QtTest 1.14'
Module {
dependencies: ["QtQuick 2.0", "QtQuick.Window 2.0"]
@@ -343,7 +343,7 @@ Module {
Property { name: "dragThreshold"; type: "int"; isReadonly: true }
Method {
name: "typeName"
- type: "QQmlV4Handle"
+ type: "QJSValue"
Parameter { name: "v"; type: "QVariant" }
}
Method {
@@ -354,10 +354,10 @@ Module {
}
Method {
name: "callerFile"
- type: "QQmlV4Handle"
+ type: "QJSValue"
Parameter { name: "frameIndex"; type: "int" }
}
- Method { name: "callerFile"; type: "QQmlV4Handle" }
+ Method { name: "callerFile"; type: "QJSValue" }
Method {
name: "callerLine"
type: "int"
diff --git a/src/imports/wavefrontmesh/plugins.qmltypes b/src/imports/wavefrontmesh/plugins.qmltypes
index b9dd9e4c46..4e6a1dca73 100644
--- a/src/imports/wavefrontmesh/plugins.qmltypes
+++ b/src/imports/wavefrontmesh/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable Qt.labs.wavefrontmesh 1.13'
+// 'qmlplugindump -nonrelocatable Qt.labs.wavefrontmesh 1.14'
Module {
dependencies: ["QtQuick 2.0"]
diff --git a/src/imports/window/plugins.qmltypes b/src/imports/window/plugins.qmltypes
index b5786ed5a6..693673c4cd 100644
--- a/src/imports/window/plugins.qmltypes
+++ b/src/imports/window/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtQuick.Window 2.13'
+// 'qmlplugindump -nonrelocatable QtQuick.Window 2.14'
Module {
dependencies: ["QtQuick 2.0"]
@@ -26,7 +26,7 @@ Module {
prototype: "QObject"
exports: ["QtQuick.Window/Screen 2.0", "QtQuick.Window/Screen 2.3"]
isCreatable: false
- exportMetaObjectRevisions: [0, 1]
+ exportMetaObjectRevisions: [0, 3]
attachedType: "QQuickScreenAttached"
}
Component {
@@ -48,7 +48,7 @@ Module {
"QtQuick.Window/ScreenInfo 2.3"
]
isCreatable: false
- exportMetaObjectRevisions: [10, 2]
+ exportMetaObjectRevisions: [10, 3]
Property { name: "name"; type: "string"; isReadonly: true }
Property { name: "manufacturer"; revision: 10; type: "string"; isReadonly: true }
Property { name: "model"; revision: 10; type: "string"; isReadonly: true }
@@ -62,14 +62,14 @@ Module {
Property { name: "devicePixelRatio"; type: "double"; isReadonly: true }
Property { name: "primaryOrientation"; type: "Qt::ScreenOrientation"; isReadonly: true }
Property { name: "orientation"; type: "Qt::ScreenOrientation"; isReadonly: true }
- Property { name: "virtualX"; revision: 1; type: "int"; isReadonly: true }
- Property { name: "virtualY"; revision: 1; type: "int"; isReadonly: true }
+ Property { name: "virtualX"; revision: 3; type: "int"; isReadonly: true }
+ Property { name: "virtualY"; revision: 3; type: "int"; isReadonly: true }
Signal { name: "manufacturerChanged"; revision: 10 }
Signal { name: "modelChanged"; revision: 10 }
Signal { name: "serialNumberChanged"; revision: 10 }
Signal { name: "desktopGeometryChanged" }
- Signal { name: "virtualXChanged"; revision: 1 }
- Signal { name: "virtualYChanged"; revision: 1 }
+ Signal { name: "virtualXChanged"; revision: 3 }
+ Signal { name: "virtualYChanged"; revision: 3 }
}
Component {
name: "QQuickWindow"
@@ -100,6 +100,12 @@ Module {
"NativeTextRendering": 1
}
}
+ Enum {
+ name: "NativeObjectType"
+ values: {
+ "NativeObjectTexture": 0
+ }
+ }
Property { name: "data"; type: "QObject"; isList: true; isReadonly: true }
Property { name: "color"; type: "QColor" }
Property { name: "contentItem"; type: "QQuickItem"; isReadonly: true; isPointer: true }
@@ -140,6 +146,8 @@ Module {
Parameter { name: "error"; type: "QQuickWindow::SceneGraphError" }
Parameter { name: "message"; type: "string" }
}
+ Signal { name: "beforeRenderPassRecording"; revision: 14 }
+ Signal { name: "afterRenderPassRecording"; revision: 14 }
Method { name: "update" }
Method { name: "releaseResources" }
}
@@ -164,11 +172,11 @@ Module {
"QtQuick.Window/Window 2.2",
"QtQuick.Window/Window 2.3"
]
- exportMetaObjectRevisions: [0, 13, 1, 2]
+ exportMetaObjectRevisions: [0, 13, 2, 3]
attachedType: "QQuickWindowAttached"
Property { name: "visible"; type: "bool" }
Property { name: "visibility"; type: "Visibility" }
- Property { name: "screen"; revision: 2; type: "QObject"; isPointer: true }
+ Property { name: "screen"; revision: 3; type: "QObject"; isPointer: true }
Signal {
name: "visibleChanged"
Parameter { name: "arg"; type: "bool" }
@@ -177,7 +185,7 @@ Module {
name: "visibilityChanged"
Parameter { name: "visibility"; type: "QWindow::Visibility" }
}
- Signal { name: "screenChanged"; revision: 2 }
+ Signal { name: "screenChanged"; revision: 3 }
}
Component {
name: "QWindow"
diff --git a/src/imports/workerscript/dependencies.json b/src/imports/workerscript/dependencies.json
new file mode 100644
index 0000000000..0d4f101c7a
--- /dev/null
+++ b/src/imports/workerscript/dependencies.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/src/imports/workerscript/plugin.cpp b/src/imports/workerscript/plugin.cpp
new file mode 100644
index 0000000000..5b3bff7934
--- /dev/null
+++ b/src/imports/workerscript/plugin.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtQmlWorkerScript/private/qqmlworkerscriptmodule_p.h>
+#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlmodule QtQml.WorkerScript 2.\QtMinorVersion
+ \title Qt QML WorkerScript QML Types
+ \ingroup qmlmodules
+ \brief Provides QML types for worker scripts
+ \since 5.14
+
+ This QML module contains types for using worker scripts.
+
+ To use the types in this module, import the module with the following line:
+
+ \qml \QtMinorVersion
+ import QtQml.WorkerScript 2.\1
+ \endqml
+*/
+
+class QtQmlWorkerScriptPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+public:
+ QtQmlWorkerScriptPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { }
+ void registerTypes(const char *uri) override
+ {
+ Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQml.WorkerScript"));
+
+ QQmlWorkerScriptModule::defineModule();
+
+ // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward
+ qmlRegisterModule(uri, 2, QT_VERSION_MINOR);
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "plugin.moc"
diff --git a/src/imports/workerscript/plugins.qmltypes b/src/imports/workerscript/plugins.qmltypes
new file mode 100644
index 0000000000..b1d6107022
--- /dev/null
+++ b/src/imports/workerscript/plugins.qmltypes
@@ -0,0 +1,26 @@
+import QtQuick.tooling 1.2
+
+// This file describes the plugin-supplied types contained in the library.
+// It is used for QML tooling purposes only.
+//
+// This file was auto-generated by:
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQml.WorkerScript 2.14'
+
+Module {
+ dependencies: []
+ Component {
+ name: "QQuickWorkerScript"
+ prototype: "QObject"
+ exports: ["QtQml.WorkerScript/WorkerScript 2.0"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "source"; type: "QUrl" }
+ Signal {
+ name: "message"
+ Parameter { name: "messageObject"; type: "QJSValue" }
+ }
+ Method {
+ name: "sendMessage"
+ Parameter { type: "QQmlV4Function"; isPointer: true }
+ }
+ }
+}
diff --git a/src/imports/workerscript/qmldir b/src/imports/workerscript/qmldir
new file mode 100644
index 0000000000..1606400a23
--- /dev/null
+++ b/src/imports/workerscript/qmldir
@@ -0,0 +1,3 @@
+module QtQml.WorkerScript
+plugin workerscriptplugin
+classname QtQmlWorkerScriptPlugin
diff --git a/src/imports/workerscript/workerscript.pro b/src/imports/workerscript/workerscript.pro
new file mode 100644
index 0000000000..d48e285bda
--- /dev/null
+++ b/src/imports/workerscript/workerscript.pro
@@ -0,0 +1,11 @@
+CXX_MODULE = qml
+TARGET = workerscriptplugin
+TARGETPATH = QtQml/WorkerScript.2
+IMPORT_VERSION = 2.$$QT_MINOR_VERSION
+
+SOURCES += \
+ plugin.cpp
+
+QT = qml-private qmlworkerscript-private
+
+load(qml_plugin)
diff --git a/src/particles/particles.qrc b/src/particles/particles.qrc
index ad44cf406e..c7102b9aa5 100644
--- a/src/particles/particles.qrc
+++ b/src/particles/particles.qrc
@@ -16,5 +16,16 @@
<file>shaders/customparticletemplate_core.vert</file>
<file>shaders/imageparticle_core.frag</file>
<file>shaders/imageparticle_core.vert</file>
+
+ <file>shaders_ng/imageparticle_simple.vert.qsb</file>
+ <file>shaders_ng/imageparticle_simple.frag.qsb</file>
+ <file>shaders_ng/imageparticle_tabled.vert.qsb</file>
+ <file>shaders_ng/imageparticle_tabled.frag.qsb</file>
+ <file>shaders_ng/imageparticle_deformed.vert.qsb</file>
+ <file>shaders_ng/imageparticle_deformed.frag.qsb</file>
+ <file>shaders_ng/imageparticle_sprite.vert.qsb</file>
+ <file>shaders_ng/imageparticle_sprite.frag.qsb</file>
+ <file>shaders_ng/imageparticle_colored.vert.qsb</file>
+ <file>shaders_ng/imageparticle_colored.frag.qsb</file>
</qresource>
</RCC>
diff --git a/src/particles/qquickcustomaffector.cpp b/src/particles/qquickcustomaffector.cpp
index ccb00eeba2..5e2133dfaf 100644
--- a/src/particles/qquickcustomaffector.cpp
+++ b/src/particles/qquickcustomaffector.cpp
@@ -38,9 +38,9 @@
****************************************************************************/
#include "qquickcustomaffector_p.h"
-#include <private/qv8engine_p.h>
#include <private/qqmlengine_p.h>
#include <private/qqmlglobal_p.h>
+#include <private/qjsvalue_p.h>
#include <QQmlEngine>
#include <QDebug>
QT_BEGIN_NAMESPACE
@@ -103,7 +103,7 @@ QQuickCustomAffector::QQuickCustomAffector(QQuickItem *parent) :
bool QQuickCustomAffector::isAffectConnected()
{
- IS_SIGNAL_CONNECTED(this, QQuickCustomAffector, affectParticles, (QQmlV4Handle,qreal));
+ IS_SIGNAL_CONNECTED(this, QQuickCustomAffector, affectParticles, (const QJSValue &, qreal));
}
void QQuickCustomAffector::affectSystem(qreal dt)
@@ -156,23 +156,26 @@ void QQuickCustomAffector::affectSystem(qreal dt)
for (int i=0; i<toAffect.size(); i++)
array->put(i, (v = toAffect[i]->v4Value(m_system)));
- if (dt >= simulationCutoff || dt <= simulationDelta) {
+ const auto doAffect = [&](qreal dt) {
affectProperties(toAffect, dt);
- emit affectParticles(QQmlV4Handle(array), dt);
+ QJSValue particles;
+ QJSValuePrivate::setValue(&particles, v4, array);
+ emit affectParticles(particles, dt);
+ };
+
+ if (dt >= simulationCutoff || dt <= simulationDelta) {
+ doAffect(dt);
} else {
int realTime = m_system->timeInt;
m_system->timeInt -= dt * 1000.0;
while (dt > simulationDelta) {
m_system->timeInt += simulationDelta * 1000.0;
dt -= simulationDelta;
- affectProperties(toAffect, simulationDelta);
- emit affectParticles(QQmlV4Handle(array), simulationDelta);
+ doAffect(simulationDelta);
}
m_system->timeInt = realTime;
- if (dt > 0.0) {
- affectProperties(toAffect, dt);
- emit affectParticles(QQmlV4Handle(array), dt);
- }
+ if (dt > 0.0)
+ doAffect(dt);
}
foreach (QQuickParticleData* d, toAffect)
@@ -230,7 +233,7 @@ bool QQuickCustomAffector::affectParticle(QQuickParticleData *d, qreal dt)
return changed;
}
-void QQuickCustomAffector::affectProperties(const QList<QQuickParticleData*> particles, qreal dt)
+void QQuickCustomAffector::affectProperties(const QList<QQuickParticleData*> &particles, qreal dt)
{
foreach (QQuickParticleData* d, particles)
if ( affectParticle(d, dt) )
diff --git a/src/particles/qquickcustomaffector_p.h b/src/particles/qquickcustomaffector_p.h
index c1745798c3..5b1ea55659 100644
--- a/src/particles/qquickcustomaffector_p.h
+++ b/src/particles/qquickcustomaffector_p.h
@@ -108,7 +108,7 @@ public:
Q_SIGNALS:
- void affectParticles(QQmlV4Handle particles, qreal dt);
+ void affectParticles(const QJSValue &particles, qreal dt);
void positionChanged(QQuickDirection * arg);
@@ -156,7 +156,7 @@ protected:
bool affectParticle(QQuickParticleData *d, qreal dt) override;
private:
- void affectProperties(const QList<QQuickParticleData*> particles, qreal dt);
+ void affectProperties(const QList<QQuickParticleData*> &particles, qreal dt);
QQuickDirection * m_position;
QQuickDirection * m_velocity;
QQuickDirection * m_acceleration;
diff --git a/src/particles/qquickcustomparticle.cpp b/src/particles/qquickcustomparticle.cpp
index 85056dffa9..91fd63302a 100644
--- a/src/particles/qquickcustomparticle.cpp
+++ b/src/particles/qquickcustomparticle.cpp
@@ -413,7 +413,7 @@ void QQuickCustomParticle::buildData(QQuickOpenGLShaderEffectNode *rootNode)
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) {
if (m_common.uniformData[shaderType].at(i).name == "qt_Timestamp")
- m_common.uniformData[shaderType][i].value = qVariantFromValue(m_lastTime);
+ m_common.uniformData[shaderType][i].value = QVariant::fromValue(m_lastTime);
}
}
m_common.updateMaterial(rootNode, static_cast<QQuickOpenGLShaderEffectMaterial *>(rootNode->material()),
diff --git a/src/particles/qquickimageparticle.cpp b/src/particles/qquickimageparticle.cpp
index 9f87297b22..4ce8186c7c 100644
--- a/src/particles/qquickimageparticle.cpp
+++ b/src/particles/qquickimageparticle.cpp
@@ -1,6 +1,6 @@
-/****************************************************************************
+/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -41,7 +41,6 @@
#include <private/qsgadaptationlayer_p.h>
#include <private/qquickitem_p.h>
#include <QtQuick/qsgnode.h>
-#include <QtQuick/qsgtexturematerial.h>
#include <QtQuick/qsgtexture.h>
#include <QFile>
#include <QRandomGenerator>
@@ -52,17 +51,19 @@
#include <QOpenGLFunctions>
#include <QSGRendererInterface>
#include <QtQuick/private/qsgshadersourcebuilder_p.h>
-#include <QtQuick/private/qsgtexture_p.h>
+#include <QtQuick/private/qsgplaintexture_p.h>
#include <private/qqmlglobal_p.h>
#include <QtQml/qqmlinfo.h>
#include <cmath>
+#include <QtGui/private/qrhi_p.h>
QT_BEGIN_NAMESPACE
-//TODO: Make it larger on desktop? Requires fixing up shader code with the same define
+// Must match the shader code
#define UNIFORM_ARRAY_SIZE 64
const qreal CONV = 0.017453292519943295;
+
class ImageMaterialData
{
public:
@@ -85,13 +86,10 @@ class ImageMaterialData
QSizeF animSheetSize;
};
-class TabledMaterialData : public ImageMaterialData {};
-class TabledMaterial : public QSGSimpleMaterialShader<TabledMaterialData>
+class TabledMaterialShader : public QSGMaterialShader
{
- QSG_DECLARE_SIMPLE_SHADER(TabledMaterial, TabledMaterialData)
-
public:
- TabledMaterial()
+ TabledMaterialShader()
{
QSGShaderSourceBuilder builder;
const bool isES = QOpenGLContext::currentContext()->isOpenGLES();
@@ -122,36 +120,47 @@ public:
const char *vertexShader() const override { return m_vertex_code.constData(); }
const char *fragmentShader() const override { return m_fragment_code.constData(); }
- QList<QByteArray> attributes() const override {
- return QList<QByteArray>() << "vPosTex" << "vData" << "vVec"
- << "vColor" << "vDeformVec" << "vRotation";
- };
+ char const *const *attributeNames() const override
+ {
+ static const char *const attr[] = { "vPosTex", "vData", "vVec", "vColor", "vDeformVec", "vRotation", nullptr };
+ return attr;
+ }
void initialize() override {
- QSGSimpleMaterialShader<TabledMaterialData>::initialize();
program()->bind();
program()->setUniformValue("_qt_texture", 0);
program()->setUniformValue("colortable", 1);
glFuncs = QOpenGLContext::currentContext()->functions();
+ m_matrix_id = program()->uniformLocation("qt_Matrix");
+ m_opacity_id = program()->uniformLocation("qt_Opacity");
m_timestamp_id = program()->uniformLocation("timestamp");
m_entry_id = program()->uniformLocation("entry");
m_sizetable_id = program()->uniformLocation("sizetable");
m_opacitytable_id = program()->uniformLocation("opacitytable");
}
- void updateState(const TabledMaterialData* d, const TabledMaterialData*) override {
+ void updateState(const RenderState &renderState, QSGMaterial *mat, QSGMaterial *) override {
+ ImageMaterialData *state = static_cast<ImageMaterial *>(mat)->state();
+
+ if (renderState.isMatrixDirty())
+ program()->setUniformValue(m_matrix_id, renderState.combinedMatrix());
+ if (renderState.isOpacityDirty() && m_opacity_id >= 0)
+ program()->setUniformValue(m_opacity_id, renderState.opacity());
+
glFuncs->glActiveTexture(GL_TEXTURE1);
- d->colorTable->bind();
+ state->colorTable->bind();
glFuncs->glActiveTexture(GL_TEXTURE0);
- d->texture->bind();
+ state->texture->bind();
- program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
- program()->setUniformValue(m_entry_id, (float) d->entry);
- program()->setUniformValueArray(m_sizetable_id, (const float*) d->sizeTable, UNIFORM_ARRAY_SIZE, 1);
- program()->setUniformValueArray(m_opacitytable_id, (const float*) d->opacityTable, UNIFORM_ARRAY_SIZE, 1);
+ program()->setUniformValue(m_timestamp_id, (float) state->timestamp);
+ program()->setUniformValue(m_entry_id, (float) state->entry);
+ program()->setUniformValueArray(m_sizetable_id, (const float*) state->sizeTable, UNIFORM_ARRAY_SIZE, 1);
+ program()->setUniformValueArray(m_opacitytable_id, (const float*) state->opacityTable, UNIFORM_ARRAY_SIZE, 1);
}
+ int m_matrix_id;
+ int m_opacity_id;
int m_entry_id;
int m_timestamp_id;
int m_sizetable_id;
@@ -161,13 +170,91 @@ public:
QOpenGLFunctions* glFuncs;
};
-class DeformableMaterialData : public ImageMaterialData {};
-class DeformableMaterial : public QSGSimpleMaterialShader<DeformableMaterialData>
+class TabledMaterialRhiShader : public QSGMaterialRhiShader
+{
+public:
+ TabledMaterialRhiShader()
+ {
+ setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_tabled.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_tabled.frag.qsb"));
+ }
+
+ bool updateUniformData(RenderState &renderState, QSGMaterial *newMaterial, QSGMaterial *) override
+ {
+ QByteArray *buf = renderState.uniformData();
+ Q_ASSERT(buf->size() >= 80 + 2 * (UNIFORM_ARRAY_SIZE * 4 * 4));
+
+ if (renderState.isMatrixDirty()) {
+ const QMatrix4x4 m = renderState.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ }
+
+ if (renderState.isOpacityDirty()) {
+ const float opacity = renderState.opacity();
+ memcpy(buf->data() + 64, &opacity, 4);
+ }
+
+ ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
+
+ float entry = float(state->entry);
+ memcpy(buf->data() + 68, &entry, 4);
+
+ float timestamp = float(state->timestamp);
+ memcpy(buf->data() + 72, &timestamp, 4);
+
+ float *p = reinterpret_cast<float *>(buf->data() + 80);
+ for (int i = 0; i < UNIFORM_ARRAY_SIZE; ++i) {
+ *p = state->sizeTable[i];
+ p += 4;
+ }
+ p = reinterpret_cast<float *>(buf->data() + 80 + (UNIFORM_ARRAY_SIZE * 4 * 4));
+ for (int i = 0; i < UNIFORM_ARRAY_SIZE; ++i) {
+ *p = state->opacityTable[i];
+ p += 4;
+ }
+
+ return true;
+ }
+
+ void updateSampledImage(RenderState &renderState, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *) override
+ {
+ ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
+ if (binding == 2) {
+ state->colorTable->updateRhiTexture(renderState.rhi(), renderState.resourceUpdateBatch());
+ *texture = state->colorTable;
+ } else if (binding == 1) {
+ state->texture->updateRhiTexture(renderState.rhi(), renderState.resourceUpdateBatch());
+ *texture = state->texture;
+ }
+ }
+};
+
+class TabledMaterial : public ImageMaterial
{
- QSG_DECLARE_SIMPLE_SHADER(DeformableMaterial, DeformableMaterialData)
+public:
+ TabledMaterial() { setFlag(SupportsRhiShader, true); }
+ QSGMaterialShader *createShader() const override {
+ if (flags().testFlag(RhiShaderWanted))
+ return new TabledMaterialRhiShader;
+ else
+ return new TabledMaterialShader;
+ }
+ QSGMaterialType *type() const override { return &m_type; }
+
+ ImageMaterialData *state() override { return &m_state; }
+private:
+ static QSGMaterialType m_type;
+ ImageMaterialData m_state;
+};
+
+QSGMaterialType TabledMaterial::m_type;
+
+class DeformableMaterialShader : public QSGMaterialShader
+{
public:
- DeformableMaterial()
+ DeformableMaterialShader()
{
QSGShaderSourceBuilder builder;
const bool isES = QOpenGLContext::currentContext()->isOpenGLES();
@@ -196,27 +283,38 @@ public:
const char *vertexShader() const override { return m_vertex_code.constData(); }
const char *fragmentShader() const override { return m_fragment_code.constData(); }
- QList<QByteArray> attributes() const override {
- return QList<QByteArray>() << "vPosTex" << "vData" << "vVec"
- << "vColor" << "vDeformVec" << "vRotation";
- };
+ char const *const *attributeNames() const override
+ {
+ static const char *const attr[] = { "vPosTex", "vData", "vVec", "vColor", "vDeformVec", "vRotation", nullptr };
+ return attr;
+ }
void initialize() override {
- QSGSimpleMaterialShader<DeformableMaterialData>::initialize();
program()->bind();
program()->setUniformValue("_qt_texture", 0);
glFuncs = QOpenGLContext::currentContext()->functions();
+ m_matrix_id = program()->uniformLocation("qt_Matrix");
+ m_opacity_id = program()->uniformLocation("qt_Opacity");
m_timestamp_id = program()->uniformLocation("timestamp");
m_entry_id = program()->uniformLocation("entry");
}
- void updateState(const DeformableMaterialData* d, const DeformableMaterialData*) override {
- d->texture->bind();
+ void updateState(const RenderState &renderState, QSGMaterial *mat, QSGMaterial *) override {
+ ImageMaterialData *state = static_cast<ImageMaterial *>(mat)->state();
+
+ if (renderState.isMatrixDirty())
+ program()->setUniformValue(m_matrix_id, renderState.combinedMatrix());
+ if (renderState.isOpacityDirty() && m_opacity_id >= 0)
+ program()->setUniformValue(m_opacity_id, renderState.opacity());
+
+ state->texture->bind();
- program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
- program()->setUniformValue(m_entry_id, (float) d->entry);
+ program()->setUniformValue(m_timestamp_id, (float) state->timestamp);
+ program()->setUniformValue(m_entry_id, (float) state->entry);
}
+ int m_matrix_id;
+ int m_opacity_id;
int m_entry_id;
int m_timestamp_id;
QByteArray m_vertex_code;
@@ -224,13 +322,77 @@ public:
QOpenGLFunctions* glFuncs;
};
-class SpriteMaterialData : public ImageMaterialData {};
-class SpriteMaterial : public QSGSimpleMaterialShader<SpriteMaterialData>
+class DeformableMaterialRhiShader : public QSGMaterialRhiShader
{
- QSG_DECLARE_SIMPLE_SHADER(SpriteMaterial, SpriteMaterialData)
+public:
+ DeformableMaterialRhiShader()
+ {
+ setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_deformed.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_deformed.frag.qsb"));
+ }
+ bool updateUniformData(RenderState &renderState, QSGMaterial *newMaterial, QSGMaterial *) override
+ {
+ QByteArray *buf = renderState.uniformData();
+ Q_ASSERT(buf->size() >= 80 + 2 * (UNIFORM_ARRAY_SIZE * 4 * 4));
+
+ if (renderState.isMatrixDirty()) {
+ const QMatrix4x4 m = renderState.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ }
+
+ if (renderState.isOpacityDirty()) {
+ const float opacity = renderState.opacity();
+ memcpy(buf->data() + 64, &opacity, 4);
+ }
+
+ ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
+
+ float entry = float(state->entry);
+ memcpy(buf->data() + 68, &entry, 4);
+
+ float timestamp = float(state->timestamp);
+ memcpy(buf->data() + 72, &timestamp, 4);
+
+ return true;
+ }
+
+ void updateSampledImage(RenderState &renderState, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *) override
+ {
+ ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
+ if (binding == 1) {
+ state->texture->updateRhiTexture(renderState.rhi(), renderState.resourceUpdateBatch());
+ *texture = state->texture;
+ }
+ }
+};
+
+class DeformableMaterial : public ImageMaterial
+{
public:
- SpriteMaterial()
+ DeformableMaterial() { setFlag(SupportsRhiShader, true); }
+ QSGMaterialShader *createShader() const override {
+ if (flags().testFlag(RhiShaderWanted))
+ return new DeformableMaterialRhiShader;
+ else
+ return new DeformableMaterialShader;
+ }
+ QSGMaterialType *type() const override { return &m_type; }
+
+ ImageMaterialData *state() override { return &m_state; }
+
+private:
+ static QSGMaterialType m_type;
+ ImageMaterialData m_state;
+};
+
+QSGMaterialType DeformableMaterial::m_type;
+
+class SpriteMaterialShader : public QSGMaterialShader
+{
+public:
+ SpriteMaterialShader()
{
QSGShaderSourceBuilder builder;
const bool isES = QOpenGLContext::currentContext()->isOpenGLES();
@@ -263,17 +425,20 @@ public:
const char *vertexShader() const override { return m_vertex_code.constData(); }
const char *fragmentShader() const override { return m_fragment_code.constData(); }
- QList<QByteArray> attributes() const override {
- return QList<QByteArray>() << "vPosTex" << "vData" << "vVec"
- << "vColor" << "vDeformVec" << "vRotation" << "vAnimData" << "vAnimPos";
+ char const *const *attributeNames() const override
+ {
+ static const char *const attr[] = { "vPosTex", "vData", "vVec", "vColor", "vDeformVec", "vRotation",
+ "vAnimData", "vAnimPos", nullptr };
+ return attr;
}
void initialize() override {
- QSGSimpleMaterialShader<SpriteMaterialData>::initialize();
program()->bind();
program()->setUniformValue("_qt_texture", 0);
program()->setUniformValue("colortable", 1);
glFuncs = QOpenGLContext::currentContext()->functions();
+ m_matrix_id = program()->uniformLocation("qt_Matrix");
+ m_opacity_id = program()->uniformLocation("qt_Opacity");
//Don't actually expose the animSheetSize in the shader, it's currently only used for CPU calculations.
m_timestamp_id = program()->uniformLocation("timestamp");
m_entry_id = program()->uniformLocation("entry");
@@ -281,20 +446,29 @@ public:
m_opacitytable_id = program()->uniformLocation("opacitytable");
}
- void updateState(const SpriteMaterialData* d, const SpriteMaterialData*) override {
+ void updateState(const RenderState &renderState, QSGMaterial *mat, QSGMaterial *) override {
+ ImageMaterialData *state = static_cast<ImageMaterial *>(mat)->state();
+
+ if (renderState.isMatrixDirty())
+ program()->setUniformValue(m_matrix_id, renderState.combinedMatrix());
+ if (renderState.isOpacityDirty() && m_opacity_id >= 0)
+ program()->setUniformValue(m_opacity_id, renderState.opacity());
+
glFuncs->glActiveTexture(GL_TEXTURE1);
- d->colorTable->bind();
+ state->colorTable->bind();
// make sure we end by setting GL_TEXTURE0 as active texture
glFuncs->glActiveTexture(GL_TEXTURE0);
- d->texture->bind();
+ state->texture->bind();
- program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
- program()->setUniformValue(m_entry_id, (float) d->entry);
- program()->setUniformValueArray(m_sizetable_id, (const float*) d->sizeTable, 64, 1);
- program()->setUniformValueArray(m_opacitytable_id, (const float*) d->opacityTable, UNIFORM_ARRAY_SIZE, 1);
+ program()->setUniformValue(m_timestamp_id, (float) state->timestamp);
+ program()->setUniformValue(m_entry_id, (float) state->entry);
+ program()->setUniformValueArray(m_sizetable_id, (const float*) state->sizeTable, 64, 1);
+ program()->setUniformValueArray(m_opacitytable_id, (const float*) state->opacityTable, UNIFORM_ARRAY_SIZE, 1);
}
+ int m_matrix_id;
+ int m_opacity_id;
int m_timestamp_id;
int m_entry_id;
int m_sizetable_id;
@@ -304,13 +478,91 @@ public:
QOpenGLFunctions* glFuncs;
};
-class ColoredMaterialData : public ImageMaterialData {};
-class ColoredMaterial : public QSGSimpleMaterialShader<ColoredMaterialData>
+class SpriteMaterialRhiShader : public QSGMaterialRhiShader
{
- QSG_DECLARE_SIMPLE_SHADER(ColoredMaterial, ColoredMaterialData)
+public:
+ SpriteMaterialRhiShader()
+ {
+ setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_sprite.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_sprite.frag.qsb"));
+ }
+
+ bool updateUniformData(RenderState &renderState, QSGMaterial *newMaterial, QSGMaterial *) override
+ {
+ QByteArray *buf = renderState.uniformData();
+ Q_ASSERT(buf->size() >= 80 + 2 * (UNIFORM_ARRAY_SIZE * 4 * 4));
+
+ if (renderState.isMatrixDirty()) {
+ const QMatrix4x4 m = renderState.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ }
+
+ if (renderState.isOpacityDirty()) {
+ const float opacity = renderState.opacity();
+ memcpy(buf->data() + 64, &opacity, 4);
+ }
+ ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
+
+ float entry = float(state->entry);
+ memcpy(buf->data() + 68, &entry, 4);
+
+ float timestamp = float(state->timestamp);
+ memcpy(buf->data() + 72, &timestamp, 4);
+
+ float *p = reinterpret_cast<float *>(buf->data() + 80);
+ for (int i = 0; i < UNIFORM_ARRAY_SIZE; ++i) {
+ *p = state->sizeTable[i];
+ p += 4;
+ }
+ p = reinterpret_cast<float *>(buf->data() + 80 + (UNIFORM_ARRAY_SIZE * 4 * 4));
+ for (int i = 0; i < UNIFORM_ARRAY_SIZE; ++i) {
+ *p = state->opacityTable[i];
+ p += 4;
+ }
+
+ return true;
+ }
+
+ void updateSampledImage(RenderState &renderState, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *) override
+ {
+ ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
+ if (binding == 2) {
+ state->colorTable->updateRhiTexture(renderState.rhi(), renderState.resourceUpdateBatch());
+ *texture = state->colorTable;
+ } else if (binding == 1) {
+ state->texture->updateRhiTexture(renderState.rhi(), renderState.resourceUpdateBatch());
+ *texture = state->texture;
+ }
+ }
+};
+
+class SpriteMaterial : public ImageMaterial
+{
public:
- ColoredMaterial()
+ SpriteMaterial() { setFlag(SupportsRhiShader, true); }
+ QSGMaterialShader *createShader() const override {
+ if (flags().testFlag(RhiShaderWanted))
+ return new SpriteMaterialRhiShader;
+ else
+ return new SpriteMaterialShader;
+ }
+ QSGMaterialType *type() const override { return &m_type; }
+
+ ImageMaterialData *state() override { return &m_state; }
+
+private:
+ static QSGMaterialType m_type;
+ ImageMaterialData m_state;
+};
+
+QSGMaterialType SpriteMaterial::m_type;
+
+class ColoredMaterialShader : public QSGMaterialShader
+{
+public:
+ ColoredMaterialShader()
{
QSGShaderSourceBuilder builder;
const bool isES = QOpenGLContext::currentContext()->isOpenGLES();
@@ -337,8 +589,23 @@ public:
const char *vertexShader() const override { return m_vertex_code.constData(); }
const char *fragmentShader() const override { return m_fragment_code.constData(); }
+ char const *const *attributeNames() const override
+ {
+ static const char *const attr[] = { "vPos", "vData", "vVec", "vColor", nullptr };
+ return attr;
+ }
+
+ void initialize() override {
+ program()->bind();
+ program()->setUniformValue("_qt_texture", 0);
+ glFuncs = QOpenGLContext::currentContext()->functions();
+ m_matrix_id = program()->uniformLocation("qt_Matrix");
+ m_opacity_id = program()->uniformLocation("qt_Opacity");
+ m_timestamp_id = program()->uniformLocation("timestamp");
+ m_entry_id = program()->uniformLocation("entry");
+ }
+
void activate() override {
- QSGSimpleMaterialShader<ColoredMaterialData>::activate();
#if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
glEnable(GL_POINT_SPRITE);
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
@@ -346,33 +613,28 @@ public:
}
void deactivate() override {
- QSGSimpleMaterialShader<ColoredMaterialData>::deactivate();
#if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
glDisable(GL_POINT_SPRITE);
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
#endif
}
- QList<QByteArray> attributes() const override {
- return QList<QByteArray>() << "vPos" << "vData" << "vVec" << "vColor";
- }
+ void updateState(const RenderState &renderState, QSGMaterial *mat, QSGMaterial *) override {
+ ImageMaterialData *state = static_cast<ImageMaterial *>(mat)->state();
- void initialize() override {
- QSGSimpleMaterialShader<ColoredMaterialData>::initialize();
- program()->bind();
- program()->setUniformValue("_qt_texture", 0);
- glFuncs = QOpenGLContext::currentContext()->functions();
- m_timestamp_id = program()->uniformLocation("timestamp");
- m_entry_id = program()->uniformLocation("entry");
- }
+ if (renderState.isMatrixDirty())
+ program()->setUniformValue(m_matrix_id, renderState.combinedMatrix());
+ if (renderState.isOpacityDirty() && m_opacity_id >= 0)
+ program()->setUniformValue(m_opacity_id, renderState.opacity());
- void updateState(const ColoredMaterialData* d, const ColoredMaterialData*) override {
- d->texture->bind();
+ state->texture->bind();
- program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
- program()->setUniformValue(m_entry_id, (float) d->entry);
+ program()->setUniformValue(m_timestamp_id, (float) state->timestamp);
+ program()->setUniformValue(m_entry_id, (float) state->entry);
}
+ int m_matrix_id;
+ int m_opacity_id;
int m_timestamp_id;
int m_entry_id;
QByteArray m_vertex_code;
@@ -380,13 +642,77 @@ public:
QOpenGLFunctions* glFuncs;
};
-class SimpleMaterialData : public ImageMaterialData {};
-class SimpleMaterial : public QSGSimpleMaterialShader<SimpleMaterialData>
+class ColoredMaterialRhiShader : public QSGMaterialRhiShader
+{
+public:
+ ColoredMaterialRhiShader()
+ {
+ setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_colored.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_colored.frag.qsb"));
+ }
+
+ bool updateUniformData(RenderState &renderState, QSGMaterial *newMaterial, QSGMaterial *) override
+ {
+ QByteArray *buf = renderState.uniformData();
+ Q_ASSERT(buf->size() >= 80 + 2 * (UNIFORM_ARRAY_SIZE * 4 * 4));
+
+ if (renderState.isMatrixDirty()) {
+ const QMatrix4x4 m = renderState.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ }
+
+ if (renderState.isOpacityDirty()) {
+ const float opacity = renderState.opacity();
+ memcpy(buf->data() + 64, &opacity, 4);
+ }
+
+ ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
+
+ float entry = float(state->entry);
+ memcpy(buf->data() + 68, &entry, 4);
+
+ float timestamp = float(state->timestamp);
+ memcpy(buf->data() + 72, &timestamp, 4);
+
+ return true;
+ }
+
+ void updateSampledImage(RenderState &renderState, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *) override
+ {
+ ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
+ if (binding == 1) {
+ state->texture->updateRhiTexture(renderState.rhi(), renderState.resourceUpdateBatch());
+ *texture = state->texture;
+ }
+ }
+};
+
+class ColoredMaterial : public ImageMaterial
{
- QSG_DECLARE_SIMPLE_SHADER(SimpleMaterial, SimpleMaterialData)
+public:
+ ColoredMaterial() { setFlag(SupportsRhiShader, true); }
+ QSGMaterialShader *createShader() const override {
+ if (flags().testFlag(RhiShaderWanted))
+ return new ColoredMaterialRhiShader;
+ else
+ return new ColoredMaterialShader;
+ }
+ QSGMaterialType *type() const override { return &m_type; }
+ ImageMaterialData *state() override { return &m_state; }
+
+private:
+ static QSGMaterialType m_type;
+ ImageMaterialData m_state;
+};
+
+QSGMaterialType ColoredMaterial::m_type;
+
+class SimpleMaterialShader : public QSGMaterialShader
+{
public:
- SimpleMaterial()
+ SimpleMaterialShader()
{
QSGShaderSourceBuilder builder;
const bool isES = QOpenGLContext::currentContext()->isOpenGLES();
@@ -411,8 +737,23 @@ public:
const char *vertexShader() const override { return m_vertex_code.constData(); }
const char *fragmentShader() const override { return m_fragment_code.constData(); }
+ char const *const *attributeNames() const override
+ {
+ static const char *const attr[] = { "vPos", "vData", "vVec", nullptr };
+ return attr;
+ }
+
+ void initialize() override {
+ program()->bind();
+ program()->setUniformValue("_qt_texture", 0);
+ glFuncs = QOpenGLContext::currentContext()->functions();
+ m_matrix_id = program()->uniformLocation("qt_Matrix");
+ m_opacity_id = program()->uniformLocation("qt_Opacity");
+ m_timestamp_id = program()->uniformLocation("timestamp");
+ m_entry_id = program()->uniformLocation("entry");
+ }
+
void activate() override {
- QSGSimpleMaterialShader<SimpleMaterialData>::activate();
#if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
glEnable(GL_POINT_SPRITE);
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
@@ -420,33 +761,28 @@ public:
}
void deactivate() override {
- QSGSimpleMaterialShader<SimpleMaterialData>::deactivate();
#if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
glDisable(GL_POINT_SPRITE);
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
#endif
}
- QList<QByteArray> attributes() const override {
- return QList<QByteArray>() << "vPos" << "vData" << "vVec";
- }
+ void updateState(const RenderState &renderState, QSGMaterial *mat, QSGMaterial *) override {
+ ImageMaterialData *state = static_cast<ImageMaterial *>(mat)->state();
- void initialize() override {
- QSGSimpleMaterialShader<SimpleMaterialData>::initialize();
- program()->bind();
- program()->setUniformValue("_qt_texture", 0);
- glFuncs = QOpenGLContext::currentContext()->functions();
- m_timestamp_id = program()->uniformLocation("timestamp");
- m_entry_id = program()->uniformLocation("entry");
- }
+ if (renderState.isMatrixDirty())
+ program()->setUniformValue(m_matrix_id, renderState.combinedMatrix());
+ if (renderState.isOpacityDirty() && m_opacity_id >= 0)
+ program()->setUniformValue(m_opacity_id, renderState.opacity());
- void updateState(const SimpleMaterialData* d, const SimpleMaterialData*) override {
- d->texture->bind();
+ state->texture->bind();
- program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
- program()->setUniformValue(m_entry_id, (float) d->entry);
+ program()->setUniformValue(m_timestamp_id, (float) state->timestamp);
+ program()->setUniformValue(m_entry_id, (float) state->entry);
}
+ int m_matrix_id;
+ int m_opacity_id;
int m_timestamp_id;
int m_entry_id;
QByteArray m_vertex_code;
@@ -454,6 +790,73 @@ public:
QOpenGLFunctions* glFuncs;
};
+class SimpleMaterialRhiShader : public QSGMaterialRhiShader
+{
+public:
+ SimpleMaterialRhiShader()
+ {
+ setShaderFileName(VertexStage, QStringLiteral(":/particles/shaders_ng/imageparticle_simple.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/particles/shaders_ng/imageparticle_simple.frag.qsb"));
+ }
+
+ bool updateUniformData(RenderState &renderState, QSGMaterial *newMaterial, QSGMaterial *) override
+ {
+ QByteArray *buf = renderState.uniformData();
+ Q_ASSERT(buf->size() >= 80 + 2 * (UNIFORM_ARRAY_SIZE * 4 * 4));
+
+ if (renderState.isMatrixDirty()) {
+ const QMatrix4x4 m = renderState.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ }
+
+ if (renderState.isOpacityDirty()) {
+ const float opacity = renderState.opacity();
+ memcpy(buf->data() + 64, &opacity, 4);
+ }
+
+ ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
+
+ float entry = float(state->entry);
+ memcpy(buf->data() + 68, &entry, 4);
+
+ float timestamp = float(state->timestamp);
+ memcpy(buf->data() + 72, &timestamp, 4);
+
+ return true;
+ }
+
+ void updateSampledImage(RenderState &renderState, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *) override
+ {
+ ImageMaterialData *state = static_cast<ImageMaterial *>(newMaterial)->state();
+ if (binding == 1) {
+ state->texture->updateRhiTexture(renderState.rhi(), renderState.resourceUpdateBatch());
+ *texture = state->texture;
+ }
+ }
+};
+
+class SimpleMaterial : public ImageMaterial
+{
+public:
+ SimpleMaterial() { setFlag(SupportsRhiShader, true); }
+ QSGMaterialShader *createShader() const override {
+ if (flags().testFlag(RhiShaderWanted))
+ return new SimpleMaterialRhiShader;
+ else
+ return new SimpleMaterialShader;
+ }
+ QSGMaterialType *type() const override { return &m_type; }
+
+ ImageMaterialData *state() override { return &m_state; }
+
+private:
+ static QSGMaterialType m_type;
+ ImageMaterialData m_state;
+};
+
+QSGMaterialType SimpleMaterial::m_type;
+
void fillUniformArrayFromImage(float* array, const QImage& img, int size)
{
if (img.isNull()){
@@ -727,6 +1130,8 @@ QQuickImageParticle::QQuickImageParticle(QQuickItem* parent)
, m_debugMode(false)
, m_entryEffect(Fade)
, m_startedImageLoading(0)
+ , m_rhi(nullptr)
+ , m_apiChecked(false)
{
setFlag(ItemHasContents);
}
@@ -1003,7 +1408,7 @@ void QQuickImageParticle::setEntryEffect(EntryEffect arg)
if (m_entryEffect != arg) {
m_entryEffect = arg;
if (m_material)
- getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
+ getState(m_material)->entry = (qreal) m_entryEffect;
emit entryEffectChanged(arg);
}
}
@@ -1227,7 +1632,7 @@ void QQuickImageParticle::buildParticleNodes(QSGNode** passThrough)
void QQuickImageParticle::finishBuildParticleNodes(QSGNode** node)
{
- if (!QOpenGLContext::currentContext())
+ if (!m_rhi && !QOpenGLContext::currentContext())
return;
if (m_count * 4 > 0xffff) {
@@ -1274,28 +1679,38 @@ void QQuickImageParticle::finishBuildParticleNodes(QSGNode** node)
}
}
}
+
+ if (!m_rhi) { // the RHI may be backed by GL but these checks should be obsolete in any case
#ifdef Q_OS_WIN
- if (perfLevel < Deformable) //QTBUG-24540 , point sprite 'extension' isn't working on windows.
- perfLevel = Deformable;
+ if (perfLevel < Deformable) //QTBUG-24540 , point sprite 'extension' isn't working on windows.
+ perfLevel = Deformable;
#endif
#ifdef Q_OS_MAC
- // OS X 10.8.3 introduced a bug in the AMD drivers, for at least the 2011 macbook pros,
- // causing point sprites who read gl_PointCoord in the frag shader to come out as
- // green-red blobs.
- const GLubyte *glVendor = QOpenGLContext::currentContext()->functions()->glGetString(GL_VENDOR);
- if (perfLevel < Deformable && glVendor && strstr((char *) glVendor, "ATI")) {
- perfLevel = Deformable;
- }
+ // macOS 10.8.3 introduced a bug in the AMD drivers, for at least the 2011 macbook pros,
+ // causing point sprites who read gl_PointCoord in the frag shader to come out as
+ // green-red blobs.
+ const GLubyte *glVendor = QOpenGLContext::currentContext()->functions()->glGetString(GL_VENDOR);
+ if (perfLevel < Deformable && glVendor && strstr((char *) glVendor, "ATI")) {
+ perfLevel = Deformable;
+ }
#endif
#ifdef Q_OS_LINUX
- // Nouveau drivers can potentially freeze a machine entirely when taking the point-sprite path.
- const GLubyte *glVendor = QOpenGLContext::currentContext()->functions()->glGetString(GL_VENDOR);
- if (perfLevel < Deformable && glVendor && strstr((const char *) glVendor, "nouveau"))
- perfLevel = Deformable;
+ // Nouveau drivers can potentially freeze a machine entirely when taking the point-sprite path.
+ const GLubyte *glVendor = QOpenGLContext::currentContext()->functions()->glGetString(GL_VENDOR);
+ if (perfLevel < Deformable && glVendor && strstr((const char *) glVendor, "nouveau"))
+ perfLevel = Deformable;
#endif
+ } else {
+ // Points with a size other than 1 are an optional feature with QRhi
+ // because some of the underlying APIs have no support for this.
+ // Therefore, avoid the point sprite path with APIs like Direct3D.
+ if (perfLevel < Deformable && !m_rhi->isFeatureSupported(QRhi::VertexShaderPointSize))
+ perfLevel = Deformable;
+ }
+
if (perfLevel >= Colored && !m_color.isValid())
m_color = QColor(Qt::white);//Hidden default, but different from unset
@@ -1311,6 +1726,7 @@ void QQuickImageParticle::finishBuildParticleNodes(QSGNode** node)
bool imageLoaded = false;
switch (perfLevel) {//Fallthrough intended
case Sprites:
+ {
if (!m_spriteEngine) {
qWarning() << "ImageParticle: No sprite engine...";
//Sprite performance mode with static image is supported, but not advised
@@ -1321,16 +1737,19 @@ void QQuickImageParticle::finishBuildParticleNodes(QSGNode** node)
return;
imageLoaded = true;
}
- m_material = SpriteMaterial::createMaterial();
+ m_material = new SpriteMaterial;
+ ImageMaterialData *state = getState(m_material);
if (imageLoaded)
- getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(image);
- getState<ImageMaterialData>(m_material)->animSheetSize = QSizeF(image.size() / image.devicePixelRatioF());
+ state->texture = QSGPlainTexture::fromImage(image);
+ state->animSheetSize = QSizeF(image.size() / image.devicePixelRatioF());
if (m_spriteEngine)
m_spriteEngine->setCount(m_count);
+ }
Q_FALLTHROUGH();
case Tabled:
+ {
if (!m_material)
- m_material = TabledMaterial::createMaterial();
+ m_material = new TabledMaterial;
if (m_colorTable) {
if (m_colorTable->pix.isReady())
@@ -1357,21 +1776,29 @@ void QQuickImageParticle::finishBuildParticleNodes(QSGNode** node)
colortable = QImage(1,1,QImage::Format_ARGB32_Premultiplied);
colortable.fill(Qt::white);
}
- getState<ImageMaterialData>(m_material)->colorTable = QSGPlainTexture::fromImage(colortable);
- fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->sizeTable, sizetable, UNIFORM_ARRAY_SIZE);
- fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->opacityTable, opacitytable, UNIFORM_ARRAY_SIZE);
+ ImageMaterialData *state = getState(m_material);
+ state->colorTable = QSGPlainTexture::fromImage(colortable);
+ fillUniformArrayFromImage(state->sizeTable, sizetable, UNIFORM_ARRAY_SIZE);
+ fillUniformArrayFromImage(state->opacityTable, opacitytable, UNIFORM_ARRAY_SIZE);
+ }
Q_FALLTHROUGH();
case Deformable:
+ {
if (!m_material)
- m_material = DeformableMaterial::createMaterial();
+ m_material = new DeformableMaterial;
+ }
Q_FALLTHROUGH();
case Colored:
+ {
if (!m_material)
- m_material = ColoredMaterial::createMaterial();
+ m_material = new ColoredMaterial;
+ }
Q_FALLTHROUGH();
default://Also Simple
+ {
if (!m_material)
- m_material = SimpleMaterial::createMaterial();
+ m_material = new SimpleMaterial;
+ ImageMaterialData *state = getState(m_material);
if (!imageLoaded) {
if (!m_image || !m_image->pix.isReady()) {
if (m_image)
@@ -1379,14 +1806,15 @@ void QQuickImageParticle::finishBuildParticleNodes(QSGNode** node)
delete m_material;
return;
}
- //getState<ImageMaterialData>(m_material)->texture //TODO: Shouldn't this be better? But not crash?
+ //state->texture //TODO: Shouldn't this be better? But not crash?
// = QQuickItemPrivate::get(this)->sceneGraphContext()->textureForFactory(m_imagePix.textureFactory());
- getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(m_image->pix.image());
+ state->texture = QSGPlainTexture::fromImage(m_image->pix.image());
}
- getState<ImageMaterialData>(m_material)->texture->setFiltering(QSGTexture::Linear);
- getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
+ state->texture->setFiltering(QSGTexture::Linear);
+ state->entry = (qreal) m_entryEffect;
m_material->setFlag(QSGMaterial::Blending | QSGMaterial::RequiresFullMatrix);
}
+ }
m_nodes.clear();
for (auto groupId : groupIds()) {
@@ -1419,14 +1847,23 @@ void QQuickImageParticle::finishBuildParticleNodes(QSGNode** node)
node->setFlag(QSGNode::OwnsGeometry);
node->setGeometry(g);
if (perfLevel <= Colored){
- g->setDrawingMode(GL_POINTS);
- if (m_debugMode){
- GLfloat pointSizeRange[2];
- QOpenGLContext::currentContext()->functions()->glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
- qDebug() << "Using point sprites, GL_ALIASED_POINT_SIZE_RANGE " <<pointSizeRange[0] << ":" << pointSizeRange[1];
+ g->setDrawingMode(QSGGeometry::DrawPoints);
+ if (m_debugMode) {
+ if (m_rhi) {
+ qDebug("Using point sprites");
+ } else {
+#if QT_CONFIG(opengl)
+ GLfloat pointSizeRange[2];
+ QOpenGLContext::currentContext()->functions()->glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
+ qDebug() << "Using point sprites, GL_ALIASED_POINT_SIZE_RANGE " <<pointSizeRange[0] << ":" << pointSizeRange[1];
+#else
+ qDebug("Using point sprites");
+#endif
+ }
}
- }else
- g->setDrawingMode(GL_TRIANGLES);
+ } else {
+ g->setDrawingMode(QSGGeometry::DrawTriangles);
+ }
for (int p=0; p < count; ++p)
commit(groupId, p);//commit sets geometry for the node, has its own perfLevel switch
@@ -1467,16 +1904,34 @@ void QQuickImageParticle::finishBuildParticleNodes(QSGNode** node)
update();
}
-static inline bool isOpenGL(QSGRenderContext *rc)
-{
- QSGRendererInterface *rif = rc->sceneGraphContext()->rendererInterface(rc);
- return !rif || rif->graphicsApi() == QSGRendererInterface::OpenGL;
-}
-
QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
{
- if (!node && !isOpenGL(QQuickItemPrivate::get(this)->sceneGraphRenderContext()))
- return nullptr;
+ if (!m_apiChecked || m_windowChanged) {
+ m_apiChecked = true;
+ m_windowChanged = false;
+
+ QSGRenderContext *rc = QQuickItemPrivate::get(this)->sceneGraphRenderContext();
+ QSGRendererInterface *rif = rc->sceneGraphContext()->rendererInterface(rc);
+ if (!rif)
+ return nullptr;
+
+ QSGRendererInterface::GraphicsApi api = rif->graphicsApi();
+ const bool isDirectOpenGL = api == QSGRendererInterface::OpenGL;
+ const bool isRhi = QSGRendererInterface::isApiRhiBased(api);
+
+ if (!node && !isDirectOpenGL && !isRhi)
+ return nullptr;
+
+ if (isRhi)
+ m_rhi = static_cast<QRhi *>(rif->getResource(m_window, QSGRendererInterface::RhiResource));
+ else
+ m_rhi = nullptr;
+
+ if (isRhi && !m_rhi) {
+ qWarning("Failed to query QRhi, particles disabled");
+ return nullptr;
+ }
+ }
if (m_pleaseReset){
// Cannot just destroy the node and then return null (in case image
@@ -1555,7 +2010,7 @@ void QQuickImageParticle::prepareNextFrame(QSGNode **node)
case Colored:
case Simple:
default: //Also Simple
- getState<ImageMaterialData>(m_material)->timestamp = time;
+ getState(m_material)->timestamp = time;
break;
}
foreach (QSGGeometryNode* node, m_nodes)
@@ -1564,6 +2019,7 @@ void QQuickImageParticle::prepareNextFrame(QSGNode **node)
void QQuickImageParticle::spritesUpdate(qreal time)
{
+ ImageMaterialData *state = getState(m_material);
// Sprite progression handled CPU side, so as to have per-frame control.
for (auto groupId : groupIds()) {
for (QQuickParticleData* mainDatum : qAsConst(m_system->groupData[groupId]->data)) {
@@ -1601,7 +2057,7 @@ void QQuickImageParticle::spritesUpdate(qreal time)
}
if (m_spriteEngine->sprite(spriteIdx)->reverse())//### Store this in datum too?
frameAt = (datum->frameCount - 1) - frameAt;
- QSizeF sheetSize = getState<ImageMaterialData>(m_material)->animSheetSize;
+ QSizeF sheetSize = state->animSheetSize;
qreal y = datum->animY / sheetSize.height();
qreal w = datum->animWidth / sheetSize.width();
qreal h = datum->animHeight / sheetSize.height();
@@ -1700,6 +2156,7 @@ void QQuickImageParticle::initialize(int gIdx, int pIdx)
writeTo->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
}
} else {
+ ImageMaterialData *state = getState(m_material);
QQuickParticleData* writeTo = getShadowDatum(datum);
writeTo->animT = datum->t;
writeTo->frameCount = 1;
@@ -1708,8 +2165,8 @@ void QQuickImageParticle::initialize(int gIdx, int pIdx)
writeTo->animIdx = 0;
writeTo->animT = 0;
writeTo->animX = writeTo->animY = 0;
- writeTo->animWidth = getState<ImageMaterialData>(m_material)->animSheetSize.width();
- writeTo->animHeight = getState<ImageMaterialData>(m_material)->animSheetSize.height();
+ writeTo->animWidth = state->animSheetSize.width();
+ writeTo->animHeight = state->animSheetSize.height();
}
Q_FALLTHROUGH();
case Tabled:
diff --git a/src/particles/qquickimageparticle_p.h b/src/particles/qquickimageparticle_p.h
index e9ac56d64e..45eb73b86c 100644
--- a/src/particles/qquickimageparticle_p.h
+++ b/src/particles/qquickimageparticle_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef ULTRAPARTICLE_H
-#define ULTRAPARTICLE_H
+#ifndef QQUICKIMAGEPARTICLE_P_H
+#define QQUICKIMAGEPARTICLE_P_H
//
// W A R N I N G
@@ -50,21 +50,25 @@
//
// We mean it.
//
+
#include "qquickparticlepainter_p.h"
#include "qquickdirection_p.h"
#include <private/qquickpixmapcache_p.h>
#include <QQmlListProperty>
-#include <QtQuick/qsgsimplematerial.h>
#include <QtGui/qcolor.h>
+#include <QtQuick/qsgmaterial.h>
QT_BEGIN_NAMESPACE
class ImageMaterialData;
class QSGGeometryNode;
+class QSGMaterial;
class QQuickSprite;
class QQuickStochasticEngine;
+class QRhi;
+
struct SimpleVertex {
float x;
float y;
@@ -153,6 +157,12 @@ struct Vertices {
Vertex v4;
};
+class ImageMaterial : public QSGMaterial
+{
+public:
+ virtual ImageMaterialData *state() = 0;
+};
+
class QQuickImageParticle : public QQuickParticlePainter
{
Q_OBJECT
@@ -440,14 +450,17 @@ private:
}
}
- template<class MaterialData>
- static MaterialData* getState(QSGMaterial* m) {
- return static_cast<QSGSimpleMaterial<MaterialData> *>(m)->state();
+ ImageMaterialData *getState(QSGMaterial *m) {
+ return static_cast<ImageMaterial *>(m)->state();
}
+
EntryEffect m_entryEffect;
Status m_status;
int m_startedImageLoading;
+ QRhi *m_rhi;
+ bool m_apiChecked;
};
QT_END_NAMESPACE
-#endif // ULTRAPARTICLE_H
+
+#endif // QQUICKIMAGEPARTICLE_P_H
diff --git a/src/particles/qquickitemparticle.cpp b/src/particles/qquickitemparticle.cpp
index a171fc3288..fc28864746 100644
--- a/src/particles/qquickitemparticle.cpp
+++ b/src/particles/qquickitemparticle.cpp
@@ -238,13 +238,13 @@ void QQuickItemParticle::reset()
// delete all managed items which had their logical particles cleared
// but leave it alone if the logical particle is maintained
- QSet<QQuickItem*> lost = QSet<QQuickItem*>::fromList(m_managed);
+ QSet<QQuickItem*> lost = QSet<QQuickItem*>(m_managed.cbegin(), m_managed.cend());
for (auto groupId : groupIds()) {
for (QQuickParticleData* d : qAsConst(m_system->groupData[groupId]->data)) {
lost.remove(d->delegate);
}
}
- m_deletables.append(lost.toList());
+ m_deletables.append(lost.values());
//TODO: This doesn't yet handle calling detach on taken particles in the system reset case
processDeletables();
}
diff --git a/src/particles/qquickmaskextruder.cpp b/src/particles/qquickmaskextruder.cpp
index 4c5d9e9d88..2ce3650743 100644
--- a/src/particles/qquickmaskextruder.cpp
+++ b/src/particles/qquickmaskextruder.cpp
@@ -68,14 +68,14 @@ QQuickMaskExtruder::QQuickMaskExtruder(QObject *parent) :
{
}
-void QQuickMaskExtruder::setSource(QUrl arg)
+void QQuickMaskExtruder::setSource(const QUrl &arg)
{
if (m_source != arg) {
m_source = arg;
m_lastHeight = -1;//Trigger reset
m_lastWidth = -1;
- emit sourceChanged(arg);
+ emit sourceChanged(m_source);
startMaskLoading();
}
}
diff --git a/src/particles/qquickmaskextruder_p.h b/src/particles/qquickmaskextruder_p.h
index 0fc0331db8..bbb61b0d28 100644
--- a/src/particles/qquickmaskextruder_p.h
+++ b/src/particles/qquickmaskextruder_p.h
@@ -73,10 +73,10 @@ public:
Q_SIGNALS:
- void sourceChanged(QUrl arg);
+ void sourceChanged(const QUrl &arg);
public Q_SLOTS:
- void setSource(QUrl arg);
+ void setSource(const QUrl &arg);
private Q_SLOTS:
void startMaskLoading();
diff --git a/src/particles/qquickparticleemitter.cpp b/src/particles/qquickparticleemitter.cpp
index f154446484..c0d9fa941d 100644
--- a/src/particles/qquickparticleemitter.cpp
+++ b/src/particles/qquickparticleemitter.cpp
@@ -40,6 +40,7 @@
#include "qquickparticleemitter_p.h"
#include <private/qqmlengine_p.h>
#include <private/qqmlglobal_p.h>
+#include <private/qjsvalue_p.h>
#include <QRandomGenerator>
QT_BEGIN_NAMESPACE
@@ -258,7 +259,7 @@ QQuickParticleEmitter::~QQuickParticleEmitter()
bool QQuickParticleEmitter::isEmitConnected()
{
- IS_SIGNAL_CONNECTED(this, QQuickParticleEmitter, emitParticles, (QQmlV4Handle));
+ IS_SIGNAL_CONNECTED(this, QQuickParticleEmitter, emitParticles, (const QJSValue &));
}
void QQuickParticleEmitter::reclaculateGroupId() const
@@ -497,7 +498,9 @@ void QQuickParticleEmitter::emitWindow(int timeStamp)
for (int i=0; i<toEmit.size(); i++)
array->put(i, (v = toEmit[i]->v4Value(m_system)));
- emitParticles(QQmlV4Handle(array));//A chance for arbitrary JS changes
+ QJSValue particles;
+ QJSValuePrivate::setValue(&particles, v4, array);
+ emit emitParticles(particles);//A chance for arbitrary JS changes
}
m_last_emission = pt;
diff --git a/src/particles/qquickparticleemitter_p.h b/src/particles/qquickparticleemitter_p.h
index 4f7e12da44..64b9bcef32 100644
--- a/src/particles/qquickparticleemitter_p.h
+++ b/src/particles/qquickparticleemitter_p.h
@@ -135,7 +135,7 @@ public:
void setVelocityFromMovement(qreal s);
void componentComplete() override;
Q_SIGNALS:
- void emitParticles(QQmlV4Handle particles);
+ void emitParticles(const QJSValue &particles);
void particlesPerSecondChanged(qreal);
void particleDurationChanged(int);
void enabledChanged(bool);
diff --git a/src/particles/qquickparticlepainter.cpp b/src/particles/qquickparticlepainter.cpp
index e762b3ae1d..78e11fafcf 100644
--- a/src/particles/qquickparticlepainter.cpp
+++ b/src/particles/qquickparticlepainter.cpp
@@ -70,6 +70,7 @@ QQuickParticlePainter::QQuickParticlePainter(QQuickItem *parent)
, m_count(0)
, m_pleaseReset(true)
, m_window(nullptr)
+ , m_windowChanged(false)
, m_groupIdsNeedRecalculation(false)
{
}
@@ -80,6 +81,7 @@ void QQuickParticlePainter::itemChange(ItemChange change, const ItemChangeData &
if (m_window)
disconnect(m_window, SIGNAL(sceneGraphInvalidated()), this, SLOT(sceneGraphInvalidated()));
m_window = data.window;
+ m_windowChanged = true;
if (m_window)
connect(m_window, SIGNAL(sceneGraphInvalidated()), this, SLOT(sceneGraphInvalidated()), Qt::DirectConnection);
}
diff --git a/src/particles/qquickparticlepainter_p.h b/src/particles/qquickparticlepainter_p.h
index ac14a18103..16fc6b6f45 100644
--- a/src/particles/qquickparticlepainter_p.h
+++ b/src/particles/qquickparticlepainter_p.h
@@ -141,6 +141,7 @@ protected:
QPointF m_systemOffset;
QQuickWindow *m_window;
+ bool m_windowChanged;
private: // methods
void recalculateGroupIds() const;
diff --git a/src/particles/qquickparticlesystem.cpp b/src/particles/qquickparticlesystem.cpp
index 1499df0360..14ffc67324 100644
--- a/src/particles/qquickparticlesystem.cpp
+++ b/src/particles/qquickparticlesystem.cpp
@@ -523,7 +523,7 @@ void QQuickParticleData::clone(const QQuickParticleData& other)
animationOwner = other.animationOwner;
}
-QQmlV4Handle QQuickParticleData::v4Value(QQuickParticleSystem* particleSystem)
+QV4::ReturnedValue QQuickParticleData::v4Value(QQuickParticleSystem* particleSystem)
{
if (!v8Datum)
v8Datum = new QQuickV4ParticleData(qmlEngine(particleSystem)->handle(), this, particleSystem);
diff --git a/src/particles/qquickparticlesystem_p.h b/src/particles/qquickparticlesystem_p.h
index 73351fb99a..1e34086b0c 100644
--- a/src/particles/qquickparticlesystem_p.h
+++ b/src/particles/qquickparticlesystem_p.h
@@ -59,8 +59,9 @@
#include <private/qquicksprite_p.h>
#include <QAbstractAnimation>
#include <QtQml/qqml.h>
-#include <private/qv8engine_p.h> //For QQmlV4Handle
#include <private/qv4util_p.h>
+#include <private/qv4global_p.h>
+#include <private/qv4staticvalue_p.h>
#include "qtquickparticlesglobal_p.h"
QT_BEGIN_NAMESPACE
@@ -333,7 +334,7 @@ public:
float curSize(QQuickParticleSystem *particleSystem) const;
void clone(const QQuickParticleData& other);//Not =, leaves meta-data like index
- QQmlV4Handle v4Value(QQuickParticleSystem *particleSystem);
+ QV4::ReturnedValue v4Value(QQuickParticleSystem *particleSystem);
void extendLife(float time, QQuickParticleSystem *particleSystem);
static inline Q_DECL_CONSTEXPR float EPSILON() Q_DECL_NOTHROW { return 0.001f; }
diff --git a/src/particles/qquicktrailemitter.cpp b/src/particles/qquicktrailemitter.cpp
index ca3ebbd4ec..102dc7bd2e 100644
--- a/src/particles/qquicktrailemitter.cpp
+++ b/src/particles/qquicktrailemitter.cpp
@@ -40,6 +40,7 @@
#include "qquicktrailemitter_p.h"
#include <private/qqmlengine_p.h>
#include <private/qqmlglobal_p.h>
+#include <private/qjsvalue_p.h>
#include <QRandomGenerator>
#include <cmath>
QT_BEGIN_NAMESPACE
@@ -127,7 +128,8 @@ QQuickTrailEmitter::QQuickTrailEmitter(QQuickItem *parent) :
bool QQuickTrailEmitter::isEmitFollowConnected()
{
- IS_SIGNAL_CONNECTED(this, QQuickTrailEmitter, emitFollowParticles, (QQmlV4Handle,QQmlV4Handle));
+ IS_SIGNAL_CONNECTED(this, QQuickTrailEmitter, emitFollowParticles,
+ (const QJSValue &, const QJSValue &));
}
void QQuickTrailEmitter::recalcParticlesPerSecond(){
@@ -275,10 +277,12 @@ void QQuickTrailEmitter::emitWindow(int timeStamp)
for (int i=0; i<toEmit.size(); i++)
array->put(i, (v = toEmit[i]->v4Value(m_system)));
+ QJSValue particles;
+ QJSValuePrivate::setValue(&particles, v4, array);
if (isEmitFollowConnected())
- emitFollowParticles(QQmlV4Handle(array), d->v4Value(m_system));//A chance for many arbitrary JS changes
+ emit emitFollowParticles(particles, QJSValue(v4, d->v4Value(m_system)));//A chance for many arbitrary JS changes
else if (isEmitConnected())
- emitParticles(QQmlV4Handle(array));//A chance for arbitrary JS changes
+ emit emitParticles(particles);//A chance for arbitrary JS changes
}
m_lastEmission[d->index] = pt;
}
diff --git a/src/particles/qquicktrailemitter_p.h b/src/particles/qquicktrailemitter_p.h
index 99464436ba..22b96afd25 100644
--- a/src/particles/qquicktrailemitter_p.h
+++ b/src/particles/qquicktrailemitter_p.h
@@ -100,7 +100,7 @@ public:
}
Q_SIGNALS:
- void emitFollowParticles(QQmlV4Handle particles, QQmlV4Handle followed);
+ void emitFollowParticles(const QJSValue &particles, const QJSValue &followed);
void particlesPerParticlePerSecondChanged(int arg);
diff --git a/src/particles/qquickv4particledata.cpp b/src/particles/qquickv4particledata.cpp
index 42b30f0472..459797f977 100644
--- a/src/particles/qquickv4particledata.cpp
+++ b/src/particles/qquickv4particledata.cpp
@@ -290,7 +290,7 @@ struct QV4ParticleData : public QV4::Object
DEFINE_OBJECT_VTABLE(QV4ParticleData);
-class QV4ParticleDataDeletable : public QV8Engine::Deletable
+class QV4ParticleDataDeletable : public QV4::ExecutionEngine::Deletable
{
public:
QV4ParticleDataDeletable(QV4::ExecutionEngine *engine);
@@ -528,9 +528,9 @@ QQuickV4ParticleData::~QQuickV4ParticleData()
{
}
-QQmlV4Handle QQuickV4ParticleData::v4Value() const
+QV4::ReturnedValue QQuickV4ParticleData::v4Value() const
{
- return QQmlV4Handle(m_v4Value.value());
+ return m_v4Value.value();
}
QT_END_NAMESPACE
diff --git a/src/particles/qquickv4particledata_p.h b/src/particles/qquickv4particledata_p.h
index 3d682ab297..e41700f7c2 100644
--- a/src/particles/qquickv4particledata_p.h
+++ b/src/particles/qquickv4particledata_p.h
@@ -51,7 +51,6 @@
// We mean it.
//
-#include <private/qv8engine_p.h>
#include <private/qv4persistent_p.h>
#include <private/qv4value_p.h>
@@ -63,7 +62,7 @@ class QQuickV4ParticleData {
public:
QQuickV4ParticleData(QV4::ExecutionEngine*, QQuickParticleData*, QQuickParticleSystem *system);
~QQuickV4ParticleData();
- QQmlV4Handle v4Value() const;
+ QV4::ReturnedValue v4Value() const;
private:
QV4::PersistentValue m_v4Value;
};
diff --git a/src/particles/shaders_ng/compile.bat b/src/particles/shaders_ng/compile.bat
new file mode 100755
index 0000000000..2376d5bf6d
--- /dev/null
+++ b/src/particles/shaders_ng/compile.bat
@@ -0,0 +1,53 @@
+:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::
+:: Copyright (C) 2019 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$
+::
+:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o imageparticle_simple.vert.qsb imageparticle.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o imageparticle_simple.frag.qsb imageparticle.frag
+
+qsb -DTABLE -DDEFORM -DCOLOR -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o imageparticle_tabled.vert.qsb imageparticle.vert
+qsb -DTABLE -DDEFORM -DCOLOR --glsl "150,120,100 es" --hlsl 50 --msl 12 -o imageparticle_tabled.frag.qsb imageparticle.frag
+
+qsb -DDEFORM -DCOLOR -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o imageparticle_deformed.vert.qsb imageparticle.vert
+qsb -DDEFORM -DCOLOR --glsl "150,120,100 es" --hlsl 50 --msl 12 -o imageparticle_deformed.frag.qsb imageparticle.frag
+
+qsb -DSPRITE -DTABLE -DDEFORM -DCOLOR -b --zorder-loc 8 --glsl "150,120,100 es" --hlsl 50 --msl 12 -o imageparticle_sprite.vert.qsb imageparticle.vert
+qsb -DSPRITE -DTABLE -DDEFORM -DCOLOR --glsl "150,120,100 es" --hlsl 50 --msl 12 -o imageparticle_sprite.frag.qsb imageparticle.frag
+
+qsb -DCOLOR -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o imageparticle_colored.vert.qsb imageparticle.vert
+qsb -DCOLOR --glsl "150,120,100 es" --hlsl 50 --msl 12 -o imageparticle_colored.frag.qsb imageparticle.frag
diff --git a/src/particles/shaders_ng/imageparticle.frag b/src/particles/shaders_ng/imageparticle.frag
new file mode 100644
index 0000000000..cefb7d2d75
--- /dev/null
+++ b/src/particles/shaders_ng/imageparticle.frag
@@ -0,0 +1,55 @@
+#version 440
+
+#if defined(TABLE)
+layout(location = 0) in vec2 tt;
+#endif
+
+#if defined(SPRITE)
+layout(location = 1) in vec4 fTexS;
+#elif defined(DEFORM)
+layout(location = 1) in vec2 fTex;
+#endif
+
+#if defined(COLOR)
+layout(location = 2) in vec4 fColor;
+#else
+layout(location = 2) in float fFade;
+#endif
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ float opacity;
+ float entry;
+ float timestamp;
+ float sizetable[64];
+ float opacitytable[64];
+} ubuf;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+#if defined(TABLE) || defined(SPRITE)
+layout(binding = 2) uniform sampler2D colortable;
+#endif
+
+void main()
+{
+#if defined(SPRITE)
+ fragColor = mix(texture(_qt_texture, fTexS.xy), texture(_qt_texture, fTexS.zw), tt.y)
+ * fColor
+ * texture(colortable, tt)
+ * ubuf.opacity;
+#elif defined(TABLE)
+ fragColor = texture(_qt_texture, fTex)
+ * fColor
+ * texture(colortable, tt)
+ * ubuf.opacity;
+#elif defined(DEFORM)
+ fragColor = texture(_qt_texture, fTex) * fColor * ubuf.opacity;
+#elif defined(COLOR)
+ fragColor = texture(_qt_texture, gl_PointCoord) * fColor * ubuf.opacity;
+#else
+ fragColor = texture(_qt_texture, gl_PointCoord) * fFade * ubuf.opacity;
+#endif
+}
diff --git a/src/particles/shaders_ng/imageparticle.vert b/src/particles/shaders_ng/imageparticle.vert
new file mode 100644
index 0000000000..870139452b
--- /dev/null
+++ b/src/particles/shaders_ng/imageparticle.vert
@@ -0,0 +1,145 @@
+#version 440
+
+layout(location = 1) in vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize
+layout(location = 2) in vec4 vVec; // x,y = constant velocity, z,w = acceleration
+
+#if defined(DEFORM)
+layout(location = 0) in vec4 vPosTex;
+#else
+layout(location = 0) in vec2 vPos;
+#endif
+
+#if defined(COLOR)
+layout(location = 3) in vec4 vColor;
+#endif
+
+#if defined(DEFORM)
+layout(location = 4) in vec4 vDeformVec; // x,y x unit vector; z,w = y unit vector
+layout(location = 5) in vec3 vRotation; // x = radians of rotation, y = rotation velocity, z = bool autoRotate
+#endif
+
+#if defined(SPRITE)
+layout(location = 6) in vec3 vAnimData; // w,h(premultiplied of anim), interpolation progress
+layout(location = 7) in vec4 vAnimPos; // x,y, x,y (two frames for interpolation)
+#endif
+
+#if defined(TABLE)
+layout(location = 0) out vec2 tt; //y is progress if Sprite mode
+#endif
+
+#if defined(SPRITE)
+layout(location = 1) out vec4 fTexS;
+#elif defined(DEFORM)
+layout(location = 1) out vec2 fTex;
+#endif
+
+#if defined(COLOR)
+layout(location = 2) out vec4 fColor;
+#else
+layout(location = 2) out float fFade;
+#endif
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ float opacity;
+ float entry;
+ float timestamp;
+ float sizetable[64];
+ float opacitytable[64];
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; float gl_PointSize; };
+
+void main()
+{
+ float t = (ubuf.timestamp - vData.x) / vData.y;
+ if (t < 0. || t > 1.) {
+#if defined(DEFORM)
+ gl_Position = ubuf.matrix * vec4(vPosTex.x, vPosTex.y, 0., 1.);
+#else
+ gl_PointSize = 0.;
+#endif
+ } else {
+#if defined(SPRITE)
+ tt.y = vAnimData.z;
+
+ // Calculate frame location in texture
+ fTexS.xy = vAnimPos.xy + vPosTex.zw * vAnimData.xy;
+
+ // Next frame is also passed, for interpolation
+ fTexS.zw = vAnimPos.zw + vPosTex.zw * vAnimData.xy;
+
+#elif defined(DEFORM)
+ fTex = vPosTex.zw;
+#endif
+ float currentSize = mix(vData.z, vData.w, t * t);
+ float fade = 1.;
+ float fadeIn = min(t * 10., 1.);
+ float fadeOut = 1. - clamp((t - 0.75) * 4.,0., 1.);
+
+#if defined(TABLE)
+ currentSize = currentSize * ubuf.sizetable[int(floor(t*64.))];
+ fade = fade * ubuf.opacitytable[int(floor(t*64.))];
+#endif
+
+ if (ubuf.entry == 1.)
+ fade = fade * fadeIn * fadeOut;
+ else if (ubuf.entry == 2.)
+ currentSize = currentSize * fadeIn * fadeOut;
+
+ if (currentSize <= 0.) {
+#if defined(DEFORM)
+ gl_Position = ubuf.matrix * vec4(vPosTex.x, vPosTex.y, 0., 1.);
+#else
+ gl_PointSize = 0.;
+#endif
+ } else {
+ if (currentSize < 3.) // Sizes too small look jittery as they move
+ currentSize = 3.;
+
+ vec2 pos;
+#if defined(DEFORM)
+ float rotation = vRotation.x + vRotation.y * t * vData.y;
+ if (vRotation.z == 1.0) {
+ vec2 curVel = vVec.zw * t * vData.y + vVec.xy;
+ if (length(curVel) > 0.)
+ rotation += atan(curVel.y, curVel.x);
+ }
+ vec2 trigCalcs = vec2(cos(rotation), sin(rotation));
+ vec4 deform = vDeformVec * currentSize * (vPosTex.zzww - 0.5);
+ vec4 rotatedDeform = deform.xxzz * trigCalcs.xyxy;
+ rotatedDeform = rotatedDeform + (deform.yyww * trigCalcs.yxyx * vec4(-1.,1.,-1.,1.));
+ /* The readable version:
+ vec2 xDeform = vDeformVec.xy * currentSize * (vTex.x-0.5);
+ vec2 yDeform = vDeformVec.zw * currentSize * (vTex.y-0.5);
+ vec2 xRotatedDeform;
+ xRotatedDeform.x = trigCalcs.x*xDeform.x - trigCalcs.y*xDeform.y;
+ xRotatedDeform.y = trigCalcs.y*xDeform.x + trigCalcs.x*xDeform.y;
+ vec2 yRotatedDeform;
+ yRotatedDeform.x = trigCalcs.x*yDeform.x - trigCalcs.y*yDeform.y;
+ yRotatedDeform.y = trigCalcs.y*yDeform.x + trigCalcs.x*yDeform.y;
+ */
+ pos = vPosTex.xy
+ + rotatedDeform.xy
+ + rotatedDeform.zw
+ + vVec.xy * t * vData.y // apply velocity
+ + 0.5 * vVec.zw * pow(t * vData.y, 2.); // apply acceleration
+#else
+ pos = vPos
+ + vVec.xy * t * vData.y // apply velocity vector..
+ + 0.5 * vVec.zw * pow(t * vData.y, 2.);
+ gl_PointSize = currentSize;
+#endif
+ gl_Position = ubuf.matrix * vec4(pos.x, pos.y, 0, 1);
+
+#if defined(COLOR)
+ fColor = vColor * fade;
+#else
+ fFade = fade;
+#endif
+#if defined(TABLE)
+ tt.x = t;
+#endif
+ }
+ }
+}
diff --git a/src/particles/shaders_ng/imageparticle_colored.frag.qsb b/src/particles/shaders_ng/imageparticle_colored.frag.qsb
new file mode 100644
index 0000000000..7ae640d224
--- /dev/null
+++ b/src/particles/shaders_ng/imageparticle_colored.frag.qsb
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_colored.vert.qsb b/src/particles/shaders_ng/imageparticle_colored.vert.qsb
new file mode 100644
index 0000000000..0e2938b72c
--- /dev/null
+++ b/src/particles/shaders_ng/imageparticle_colored.vert.qsb
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_deformed.frag.qsb b/src/particles/shaders_ng/imageparticle_deformed.frag.qsb
new file mode 100644
index 0000000000..fa9d9d35bb
--- /dev/null
+++ b/src/particles/shaders_ng/imageparticle_deformed.frag.qsb
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_deformed.vert.qsb b/src/particles/shaders_ng/imageparticle_deformed.vert.qsb
new file mode 100644
index 0000000000..65092d5b26
--- /dev/null
+++ b/src/particles/shaders_ng/imageparticle_deformed.vert.qsb
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_simple.frag.qsb b/src/particles/shaders_ng/imageparticle_simple.frag.qsb
new file mode 100644
index 0000000000..a5874cba24
--- /dev/null
+++ b/src/particles/shaders_ng/imageparticle_simple.frag.qsb
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_simple.vert.qsb b/src/particles/shaders_ng/imageparticle_simple.vert.qsb
new file mode 100644
index 0000000000..da815d7e19
--- /dev/null
+++ b/src/particles/shaders_ng/imageparticle_simple.vert.qsb
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_sprite.frag.qsb b/src/particles/shaders_ng/imageparticle_sprite.frag.qsb
new file mode 100644
index 0000000000..778550344b
--- /dev/null
+++ b/src/particles/shaders_ng/imageparticle_sprite.frag.qsb
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_sprite.vert.qsb b/src/particles/shaders_ng/imageparticle_sprite.vert.qsb
new file mode 100644
index 0000000000..45b21ace8a
--- /dev/null
+++ b/src/particles/shaders_ng/imageparticle_sprite.vert.qsb
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_tabled.frag.qsb b/src/particles/shaders_ng/imageparticle_tabled.frag.qsb
new file mode 100644
index 0000000000..c5dcc2c68f
--- /dev/null
+++ b/src/particles/shaders_ng/imageparticle_tabled.frag.qsb
Binary files differ
diff --git a/src/particles/shaders_ng/imageparticle_tabled.vert.qsb b/src/particles/shaders_ng/imageparticle_tabled.vert.qsb
new file mode 100644
index 0000000000..ea42607570
--- /dev/null
+++ b/src/particles/shaders_ng/imageparticle_tabled.vert.qsb
Binary files differ
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp
index 4c104f01de..029004ffd1 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp
@@ -191,22 +191,18 @@ QQmlEngineDebugServiceImpl::propertyData(QObject *obj, int propIdx)
if (binding)
rv.binding = binding->expression();
- if (QQmlValueTypeFactory::isValueType(prop.userType())) {
- rv.type = QQmlObjectProperty::Basic;
- } else if (QQmlMetaType::isQObject(prop.userType())) {
+ rv.value = valueContents(prop.read(obj));
+
+ if (QQmlMetaType::isQObject(prop.userType())) {
rv.type = QQmlObjectProperty::Object;
} else if (QQmlMetaType::isList(prop.userType())) {
rv.type = QQmlObjectProperty::List;
} else if (prop.userType() == QMetaType::QVariant) {
rv.type = QQmlObjectProperty::Variant;
+ } else if (rv.value.isValid()) {
+ rv.type = QQmlObjectProperty::Basic;
}
- QVariant value;
- if (rv.type != QQmlObjectProperty::Unknown && prop.userType() != 0) {
- value = prop.read(obj);
- }
- rv.value = valueContents(value);
-
return rv;
}
@@ -233,11 +229,9 @@ QVariant QQmlEngineDebugServiceImpl::valueContents(QVariant value) const
if (value.type() == QVariant::Map) {
QVariantMap contents;
- QMapIterator<QString, QVariant> i(value.toMap());
- while (i.hasNext()) {
- i.next();
+ const auto map = value.toMap();
+ for (auto i = map.cbegin(), end = map.cend(); i != end; ++i)
contents.insert(i.key(), valueContents(i.value()));
- }
return contents;
}
@@ -271,10 +265,10 @@ QVariant QQmlEngineDebugServiceImpl::valueContents(QVariant value) const
return s;
}
}
-
- if (isSaveable(value))
- return value;
}
+
+ if (isSaveable(value))
+ return value;
}
if (QQmlMetaType::isQObject(userType)) {
@@ -496,7 +490,7 @@ void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message)
QQmlDebugPacket ds(message);
QByteArray type;
- int queryId;
+ qint32 queryId;
ds >> type >> queryId;
QQmlDebugPacket rs;
@@ -509,13 +503,13 @@ void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message)
QJSEngine *engine = m_engines.at(ii);
QString engineName = engine->objectName();
- int engineId = QQmlDebugService::idForObject(engine);
+ qint32 engineId = QQmlDebugService::idForObject(engine);
rs << engineName << engineId;
}
} else if (type == "LIST_OBJECTS") {
- int engineId = -1;
+ qint32 engineId = -1;
ds >> engineId;
QQmlEngine *engine =
@@ -538,7 +532,7 @@ void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message)
}
} else if (type == "FETCH_OBJECT") {
- int objectId;
+ qint32 objectId;
bool recurse;
bool dumpProperties = true;
@@ -556,8 +550,8 @@ void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message)
} else if (type == "FETCH_OBJECTS_FOR_LOCATION") {
QString file;
- int lineNumber;
- int columnNumber;
+ qint32 lineNumber;
+ qint32 columnNumber;
bool recurse;
bool dumpProperties = true;
@@ -575,7 +569,7 @@ void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message)
}
} else if (type == "WATCH_OBJECT") {
- int objectId;
+ qint32 objectId;
ds >> objectId;
bool ok = m_watch->addWatch(queryId, objectId);
@@ -583,7 +577,7 @@ void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message)
rs << QByteArray("WATCH_OBJECT_R") << queryId << ok;
} else if (type == "WATCH_PROPERTY") {
- int objectId;
+ qint32 objectId;
QByteArray property;
ds >> objectId >> property;
@@ -592,7 +586,7 @@ void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message)
rs << QByteArray("WATCH_PROPERTY_R") << queryId << ok;
} else if (type == "WATCH_EXPR_OBJECT") {
- int debugId;
+ qint32 debugId;
QString expr;
ds >> debugId >> expr;
@@ -606,11 +600,11 @@ void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message)
rs << QByteArray("NO_WATCH_R") << queryId << ok;
} else if (type == "EVAL_EXPRESSION") {
- int objectId;
+ qint32 objectId;
QString expr;
ds >> objectId >> expr;
- int engineId = -1;
+ qint32 engineId = -1;
if (!ds.atEnd())
ds >> engineId;
@@ -638,12 +632,12 @@ void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message)
rs << QByteArray("EVAL_EXPRESSION_R") << queryId << result;
} else if (type == "SET_BINDING") {
- int objectId;
+ qint32 objectId;
QString propertyName;
QVariant expr;
bool isLiteralValue;
QString filename;
- int line;
+ qint32 line;
ds >> objectId >> propertyName >> expr >> isLiteralValue >>
filename >> line;
bool ok = setBinding(objectId, propertyName, expr, isLiteralValue,
@@ -652,7 +646,7 @@ void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message)
rs << QByteArray("SET_BINDING_R") << queryId << ok;
} else if (type == "RESET_BINDING") {
- int objectId;
+ qint32 objectId;
QString propertyName;
ds >> objectId >> propertyName;
bool ok = resetBinding(objectId, propertyName);
@@ -660,7 +654,7 @@ void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message)
rs << QByteArray("RESET_BINDING_R") << queryId << ok;
} else if (type == "SET_METHOD_BODY") {
- int objectId;
+ qint32 objectId;
QString methodName;
QString methodBody;
ds >> objectId >> methodName >> methodBody;
@@ -823,7 +817,8 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth
return true;
}
-void QQmlEngineDebugServiceImpl::propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value)
+void QQmlEngineDebugServiceImpl::propertyChanged(
+ qint32 id, qint32 objectId, const QMetaProperty &property, const QVariant &value)
{
QQmlDebugPacket rs;
rs << QByteArray("UPDATE_WATCH") << id << objectId << QByteArray(property.name()) << valueContents(value);
@@ -854,14 +849,14 @@ void QQmlEngineDebugServiceImpl::objectCreated(QJSEngine *engine, QObject *objec
if (!m_engines.contains(engine))
return;
- int engineId = QQmlDebugService::idForObject(engine);
- int objectId = QQmlDebugService::idForObject(object);
- int parentId = QQmlDebugService::idForObject(object->parent());
+ qint32 engineId = QQmlDebugService::idForObject(engine);
+ qint32 objectId = QQmlDebugService::idForObject(object);
+ qint32 parentId = QQmlDebugService::idForObject(object->parent());
QQmlDebugPacket rs;
//unique queryId -1
- rs << QByteArray("OBJECT_CREATED") << -1 << engineId << objectId << parentId;
+ rs << QByteArray("OBJECT_CREATED") << qint32(-1) << engineId << objectId << parentId;
emit messageToClient(name(), rs.data());
}
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h
index c0c24058eb..741768cd00 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h
+++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h
@@ -111,7 +111,8 @@ private:
friend class QQmlDebuggerServiceFactory;
void processMessage(const QByteArray &msg);
- void propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value);
+ void propertyChanged(qint32 id, qint32 objectId, const QMetaProperty &property,
+ const QVariant &value);
void prepareDeferredObjects(QObject *);
void buildObjectList(QDataStream &, QQmlContext *,
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp
index 86571e6cbe..8caa5ac23e 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp
@@ -109,12 +109,7 @@ QQmlWatchProxy::QQmlWatchProxy(int id,
void QQmlWatchProxy::notifyValueChanged()
{
- QVariant v;
- if (m_expr)
- v = m_expr->evaluate();
- else if (QQmlValueTypeFactory::isValueType(m_property.userType()))
- v = m_property.read(m_object);
-
+ const QVariant v = m_expr ? m_expr->evaluate() : m_property.read(m_object);
emit m_watch->propertyChanged(m_id, m_debugId, m_property, v);
}
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
index 8c92b4b170..506ecb64bb 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
@@ -125,7 +125,7 @@ const QV4::Object *collectProperty(const QV4::ScopedValue &value, QV4::Execution
QJsonObject &dict)
{
QV4::Scope scope(engine);
- QV4::ScopedValue typeString(scope, QV4::Runtime::method_typeofValue(engine, value));
+ QV4::ScopedValue typeString(scope, QV4::Runtime::TypeofValue::call(engine, value));
dict.insert(QStringLiteral("type"), typeString->toQStringNoThrow());
const QLatin1String valueKey("value");
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp
index b424ef9f6c..61fea96e2f 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp
@@ -264,7 +264,7 @@ GatherSourcesJob::GatherSourcesJob(QV4::ExecutionEngine *engine)
void GatherSourcesJob::run()
{
- for (QV4::CompiledData::CompilationUnit *unit : engine->compilationUnits) {
+ for (QV4::ExecutableCompilationUnit *unit : engine->compilationUnits) {
QString fileName = unit->fileName();
if (!fileName.isEmpty())
sources.append(fileName);
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
index 5866163ca6..07db5234bf 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
@@ -44,7 +44,6 @@
#include <private/qv4engine_p.h>
#include <private/qv4function_p.h>
#include <private/qqmldebugconnector_p.h>
-#include <private/qv8engine_p.h>
#include <private/qversionedpacket_p.h>
#include <QtCore/QJsonArray>
diff --git a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp
index bac4e01df1..012730902b 100644
--- a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp
+++ b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp
@@ -48,6 +48,7 @@
#include <private/qversionedpacket_p.h>
#include <QtGui/qwindow.h>
+#include <QtCore/qregularexpression.h>
//INSPECTOR SERVICE PROTOCOL
// <HEADER><COMMAND><DATA>
@@ -273,8 +274,10 @@ QString GlobalInspector::titleForItem(QQuickItem *item) const
QString className = QLatin1String(item->metaObject()->className());
QString objectStringId = idStringForObject(item);
- className.remove(QRegExp(QLatin1String("_QMLTYPE_\\d+")));
- className.remove(QRegExp(QLatin1String("_QML_\\d+")));
+#if QT_CONFIG(regularexpression)
+ className.remove(QRegularExpression(QLatin1String("_QMLTYPE_\\d+")));
+ className.remove(QRegularExpression(QLatin1String("_QML_\\d+")));
+#endif
if (className.startsWith(QLatin1String("QQuick")))
className = className.mid(6);
diff --git a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
index a5c2f40420..dceaab9f6d 100644
--- a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
@@ -41,7 +41,6 @@
#include <private/qqmldebugconnector_p.h>
#include <private/qv4debugging_p.h>
-#include <private/qv8engine_p.h>
#include <private/qv4engine_p.h>
#include <private/qv4debugging_p.h>
#include <private/qv4script_p.h>
@@ -378,7 +377,7 @@ void Collector::collect(QJsonArray *out, const QString &parentIName, const QStri
dict.insert(QStringLiteral("iname"), iname);
dict.insert(QStringLiteral("name"), nonEmptyName);
- QV4::ScopedValue typeString(scope, QV4::Runtime::method_typeofValue(m_engine, value));
+ QV4::ScopedValue typeString(scope, QV4::Runtime::TypeofValue::call(m_engine, value));
dict.insert(QStringLiteral("type"), typeString->toQStringNoThrow());
switch (value.type()) {
diff --git a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.h b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.h
index 4b4661be2f..86f2e31d60 100644
--- a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.h
+++ b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.h
@@ -42,7 +42,6 @@
#include <private/qqmldebugconnector_p.h>
#include <private/qv4debugging_p.h>
-#include <private/qv8engine_p.h>
#include <private/qv4engine_p.h>
#include <private/qv4debugging_p.h>
#include <private/qv4script_p.h>
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp
index 9927089e5e..bb43f75c63 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp
@@ -40,6 +40,8 @@
#include "qqmlpreviewfileloader.h"
#include "qqmlpreviewservice.h"
+#include <QtQml/qqmlfile.h>
+
#include <QtCore/qlibraryinfo.h>
#include <QtCore/qstandardpaths.h>
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp
index 5d2684b510..8bb3b95e48 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp
@@ -93,7 +93,9 @@ QQmlPreviewHandler::QQmlPreviewHandler(QObject *parent) : QObject(parent)
QQmlPreviewHandler::~QQmlPreviewHandler()
{
+#if QT_CONFIG(translation)
removeTranslators();
+#endif
clear();
}
@@ -223,6 +225,7 @@ void QQmlPreviewHandler::doZoom()
m_lastPosition.initLastSavedWindowPosition(m_currentWindow);
}
+#if QT_CONFIG(translation)
void QQmlPreviewHandler::removeTranslators()
{
if (!m_qtTranslator.isNull()) {
@@ -255,6 +258,7 @@ void QQmlPreviewHandler::language(const QUrl &context, const QLocale &locale)
for (QQmlEngine *engine : qAsConst(m_engines))
engine->retranslate();
}
+#endif
void QQmlPreviewHandler::clear()
{
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h
index 47491b9d8f..f7f343a4ee 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h
@@ -81,7 +81,9 @@ public:
void loadUrl(const QUrl &url);
void rerun();
void zoom(qreal newFactor);
+#if QT_CONFIG(translation)
void language(const QUrl &context, const QLocale &locale);
+#endif
void clear();
@@ -115,7 +117,9 @@ private:
void frameSwapped();
void fpsTimerHit();
+#if QT_CONFIG(translation)
void removeTranslators();
+#endif
QScopedPointer<QQuickItem> m_dummyItem;
QList<QQmlEngine *> m_engines;
@@ -145,8 +149,10 @@ private:
FrameTime m_rendering;
FrameTime m_synchronizing;
+#if QT_CONFIG(translation)
QScopedPointer<QTranslator> m_qtTranslator;
QScopedPointer<QTranslator> m_qmlTranslator;
+#endif
};
QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp
index 2e2224df47..2e6aaa5858 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp
@@ -64,7 +64,9 @@ QQmlPreviewServiceImpl::QQmlPreviewServiceImpl(QObject *parent) :
connect(this, &QQmlPreviewServiceImpl::load, &m_handler, &QQmlPreviewHandler::loadUrl);
connect(this, &QQmlPreviewServiceImpl::rerun, &m_handler, &QQmlPreviewHandler::rerun);
connect(this, &QQmlPreviewServiceImpl::zoom, &m_handler, &QQmlPreviewHandler::zoom);
+#if QT_CONFIG(translation)
connect(this, &QQmlPreviewServiceImpl::language, &m_handler, &QQmlPreviewHandler::language);
+#endif
connect(&m_handler, &QQmlPreviewHandler::error, this, &QQmlPreviewServiceImpl::forwardError,
Qt::DirectConnection);
connect(&m_handler, &QQmlPreviewHandler::fps, this, &QQmlPreviewServiceImpl::forwardFps,
@@ -135,6 +137,7 @@ void QQmlPreviewServiceImpl::messageReceived(const QByteArray &data)
emit zoom(static_cast<qreal>(factor));
break;
}
+#if QT_CONFIG(translation)
case Language: {
QUrl context;
QString locale;
@@ -143,6 +146,7 @@ void QQmlPreviewServiceImpl::messageReceived(const QByteArray &data)
locale.isEmpty() ? QLocale() : QLocale(locale));
break;
}
+#endif
default:
forwardError(QString::fromLatin1("Invalid command: %1").arg(command));
break;
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h
index 7bdc87ec59..de50e6fc61 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h
@@ -99,7 +99,9 @@ signals:
void rerun();
void clearCache();
void zoom(qreal factor);
+#if QT_CONFIG(translation)
void language(const QUrl &context, const QLocale &locale);
+#endif
private:
QScopedPointer<QQmlPreviewFileEngineHandler> m_fileEngine;
diff --git a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp
index 8293e88038..cc663cd6b3 100644
--- a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp
+++ b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp
@@ -355,14 +355,12 @@ void QQmlDebugServerImpl::parseArguments()
if (argsNext == argsItEnd)
break;
if (ok) {
- const QString nextArgument = argsNext->toString();
-
- // Don't use QStringLiteral here. QRegExp has a global cache and will save an implicitly
- // shared copy of the passed string. That copy isn't properly detached when the library
- // is unloaded if the original string lives in the library's .rodata
- if (nextArgument.contains(QRegExp(QLatin1String("^\\s*\\d+\\s*$")))) {
- portTo = nextArgument.toInt(&ok);
+ portTo = argsNext->toString().toInt(&ok);
+ if (ok) {
++argsIt;
+ } else {
+ portTo = portFrom;
+ ok = true;
}
}
} else if (strArgument.startsWith(QLatin1String("host:"))) {
@@ -508,7 +506,7 @@ void QQmlDebugServerImpl::receiveMessage()
in >> m_clientPlugins;
for (DebugServiceConstIt iter = m_plugins.constBegin(), cend = m_plugins.constEnd(); iter != cend; ++iter) {
- const QString pluginName = iter.key();
+ const QString &pluginName = iter.key();
QQmlDebugService::State newState = QQmlDebugService::Unavailable;
if (m_clientPlugins.contains(pluginName))
newState = QQmlDebugService::Enabled;
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp
index 9b88af995d..f9bd04aa54 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp
@@ -61,8 +61,9 @@ QSGInternalRectangleNode *QSGD3D12Context::createInternalRectangleNode()
return new QSGD3D12InternalRectangleNode;
}
-QSGInternalImageNode *QSGD3D12Context::createInternalImageNode()
+QSGInternalImageNode *QSGD3D12Context::createInternalImageNode(QSGRenderContext *renderContext)
{
+ Q_UNUSED(renderContext);
return new QSGD3D12InternalImageNode;
}
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h
index 70cc606b52..382183fef6 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h
@@ -62,7 +62,7 @@ public:
QSGRenderContext *createRenderContext() override;
QSGInternalRectangleNode *createInternalRectangleNode() override;
- QSGInternalImageNode *createInternalImageNode() override;
+ QSGInternalImageNode *createInternalImageNode(QSGRenderContext *renderContext) override;
QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override;
QSGGlyphNode *createGlyphNode(QSGRenderContext *renderContext, bool preferNativeGlyphNode) override;
QSGLayer *createLayer(QSGRenderContext *renderContext) override;
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12layer.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12layer.cpp
index faa6f7566a..b9d3a180cf 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12layer.cpp
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12layer.cpp
@@ -53,7 +53,8 @@ QT_BEGIN_NAMESPACE
DECLARE_DEBUG_VAR(render)
QSGD3D12Layer::QSGD3D12Layer(QSGD3D12RenderContext *rc)
- : m_rc(rc)
+ : QSGLayer(*(new QSGD3D12LayerPrivate)),
+ m_rc(rc)
{
if (Q_UNLIKELY(debug_render()))
qDebug("new layer %p", this);
@@ -74,6 +75,12 @@ int QSGD3D12Layer::textureId() const
return m_rt; // not a texture id per se but will do
}
+int QSGD3D12LayerPrivate::comparisonKey() const
+{
+ Q_Q(const QSGD3D12Layer);
+ return q->m_rt;
+}
+
QSize QSGD3D12Layer::textureSize() const
{
return m_size;
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12layer_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12layer_p.h
index f828843227..42a56877cf 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12layer_p.h
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12layer_p.h
@@ -52,13 +52,16 @@
//
#include <private/qsgadaptationlayer_p.h>
+#include <private/qsgtexture_p.h>
QT_BEGIN_NAMESPACE
class QSGD3D12RenderContext;
+class QSGD3D12LayerPrivate;
class QSGD3D12Layer : public QSGLayer
{
+ Q_DECLARE_PRIVATE(QSGD3D12Layer)
Q_OBJECT
public:
@@ -114,6 +117,13 @@ private:
bool m_updateContentPending = false;
};
+class QSGD3D12LayerPrivate : public QSGTexturePrivate
+{
+ Q_DECLARE_PUBLIC(QSGD3D12Layer)
+public:
+ int comparisonKey() const override;
+};
+
QT_END_NAMESPACE
#endif // QSGD3D12LAYER_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp
index 4ee4656e63..48693207c6 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp
@@ -64,7 +64,7 @@ bool QSGD3D12RenderContext::isValid() const
return m_engine != nullptr;
}
-void QSGD3D12RenderContext::initialize(void *)
+void QSGD3D12RenderContext::initialize(const InitParams *)
{
if (m_initialized)
return;
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h
index 35aca100f4..c555c0808e 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h
@@ -63,7 +63,7 @@ class QSGD3D12RenderContext : public QSGRenderContext, public QSGRendererInterfa
public:
QSGD3D12RenderContext(QSGContext *ctx);
bool isValid() const override;
- void initialize(void *context) override;
+ void initialize(const InitParams *params) override;
void invalidate() override;
void renderNextFrame(QSGRenderer *renderer, uint fbo) override;
QSGTexture *createTexture(const QImage &image, uint flags) const override;
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp
index 0d501f48c0..4a6894e69e 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp
@@ -148,7 +148,7 @@ void QSGD3D12RenderLoop::windowDestroyed(QQuickWindow *window)
delete rc;
delete engine;
- delete wd->animationController;
+ wd->animationController.reset();
}
void QSGD3D12RenderLoop::exposeWindow(QQuickWindow *window)
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp
index b4fb721a8b..1f574a9802 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp
@@ -78,32 +78,6 @@ void QSGD3D12ShaderLinker::reset(const QByteArray &vertBlob, const QByteArray &f
textureNameMap.clear();
}
-void QSGD3D12ShaderLinker::feedVertexInput(const QSGShaderEffectNode::ShaderData &shader)
-{
- bool foundPos = false, foundTexCoord = false;
-
- for (const auto &ip : qAsConst(shader.shaderInfo.inputParameters)) {
- if (ip.semanticName == QByteArrayLiteral("POSITION"))
- foundPos = true;
- else if (ip.semanticName == QByteArrayLiteral("TEXCOORD"))
- foundTexCoord = true;
- }
-
- if (!foundPos) {
- qWarning("ShaderEffect: No POSITION input found.");
- error = true;
- }
- if (!foundTexCoord) {
- qWarning("ShaderEffect: No TEXCOORD input found.");
- error = true;
- }
-
- // Nothing else to do here, the QSGGeometry::AttributeSet decides anyway
- // and that is already generated by QQuickShaderEffectMesh via
- // QSGGeometry::defaultAttributes_TexturedPoint2D() and has the semantics
- // so it will just work.
-}
-
void QSGD3D12ShaderLinker::feedConstants(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices)
{
Q_ASSERT(shader.shaderInfo.variables.count() == shader.varData.count());
@@ -634,19 +608,12 @@ void QSGD3D12ShaderEffectNode::syncMaterial(SyncData *syncData)
m_material.linker.reset(vertBlob, fragBlob);
if (m_material.hasCustomVertexShader) {
- m_material.linker.feedVertexInput(*syncData->vertex.shader);
m_material.linker.feedConstants(*syncData->vertex.shader);
} else {
QSGShaderEffectNode::ShaderData defaultSD;
defaultSD.shaderInfo.blob = vertBlob;
defaultSD.shaderInfo.type = QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex;
- QSGGuiThreadShaderEffectManager::ShaderInfo::InputParameter ip;
- ip.semanticName = QByteArrayLiteral("POSITION");
- defaultSD.shaderInfo.inputParameters.append(ip);
- ip.semanticName = QByteArrayLiteral("TEXCOORD");
- defaultSD.shaderInfo.inputParameters.append(ip);
-
// { float4x4 qt_Matrix; float qt_Opacity; } where only the matrix is used
QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
v.name = QByteArrayLiteral("qt_Matrix");
@@ -656,10 +623,7 @@ void QSGD3D12ShaderEffectNode::syncMaterial(SyncData *syncData)
QSGShaderEffectNode::VariableData vd;
vd.specialType = QSGShaderEffectNode::VariableData::Matrix;
defaultSD.varData.append(vd);
-
defaultSD.shaderInfo.constantDataSize = (16 + 1) * sizeof(float);
-
- m_material.linker.feedVertexInput(defaultSD);
m_material.linker.feedConstants(defaultSD);
}
@@ -943,20 +907,6 @@ bool QSGD3D12GuiThreadShaderEffectManager::reflect(ShaderInfo *result)
qDebug("Shader reflection size %d type %d v%u.%u input elems %d cbuffers %d boundres %d",
result->blob.size(), result->type, major, minor, ieCount, cbufferCount, boundResCount);
- for (int i = 0; i < ieCount; ++i) {
- D3D12_SIGNATURE_PARAMETER_DESC desc;
- if (FAILED(reflector->GetInputParameterDesc(i, &desc))) {
- qWarning("D3D reflection: Failed to query input parameter %d", i);
- return false;
- }
- if (desc.SystemValueType != D3D_NAME_UNDEFINED)
- continue;
- ShaderInfo::InputParameter param;
- param.semanticName = QByteArray(desc.SemanticName);
- param.semanticIndex = desc.SemanticIndex;
- result->inputParameters.append(param);
- }
-
for (int i = 0; i < boundResCount; ++i) {
D3D12_SHADER_INPUT_BIND_DESC desc;
if (FAILED(reflector->GetResourceBindingDesc(i, &desc))) {
@@ -1036,10 +986,8 @@ bool QSGD3D12GuiThreadShaderEffectManager::reflect(ShaderInfo *result)
}
}
- if (Q_UNLIKELY(debug_shader())) {
- qDebug() << "Input:" << result->inputParameters;
+ if (Q_UNLIKELY(debug_shader()))
qDebug() << "Variables:" << result->variables << "cbuffer size" << result->constantDataSize;
- }
return true;
}
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h
index ee17e59130..dec85fd782 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h
@@ -67,7 +67,6 @@ class QSGD3D12ShaderLinker
public:
void reset(const QByteArray &vertBlob, const QByteArray &fragBlob);
- void feedVertexInput(const QSGShaderEffectNode::ShaderData &shader);
void feedConstants(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices = nullptr);
void feedSamplers(const QSGShaderEffectNode::ShaderData &shader);
void feedTextures(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices = nullptr);
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp
index a5f3eb7a31..b49b851c23 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp
@@ -69,6 +69,12 @@ void QSGD3D12Texture::create(const QImage &image, uint flags)
m_createPending = true;
}
+QSGD3D12Texture::QSGD3D12Texture(QSGD3D12Engine *engine)
+ : QSGTexture(*(new QSGD3D12TexturePrivate)),
+ m_engine(engine)
+{
+}
+
QSGD3D12Texture::~QSGD3D12Texture()
{
if (m_id)
@@ -80,6 +86,12 @@ int QSGD3D12Texture::textureId() const
return m_id;
}
+int QSGD3D12TexturePrivate::comparisonKey() const
+{
+ Q_Q(const QSGD3D12Texture);
+ return q->m_id;
+}
+
QSize QSGD3D12Texture::textureSize() const
{
return m_image.size();
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12texture_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12texture_p.h
index 3d0e226ddb..f6a5257773 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12texture_p.h
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12texture_p.h
@@ -51,17 +51,19 @@
// We mean it.
//
-#include <qsgtexture.h>
+#include <private/qsgtexture_p.h>
#include <basetsd.h>
QT_BEGIN_NAMESPACE
class QSGD3D12Engine;
+class QSGD3D12TexturePrivate;
class QSGD3D12Texture : public QSGTexture
{
+ Q_DECLARE_PRIVATE(QSGD3D12Texture)
public:
- QSGD3D12Texture(QSGD3D12Engine *engine) : m_engine(engine) { }
+ QSGD3D12Texture(QSGD3D12Engine *engine);
~QSGD3D12Texture();
void create(const QImage &image, uint flags);
@@ -82,6 +84,13 @@ protected:
bool m_alphaWanted = false;
};
+class QSGD3D12TexturePrivate : public QSGTexturePrivate
+{
+ Q_DECLARE_PUBLIC(QSGD3D12Texture)
+public:
+ int comparisonKey() const override;
+};
+
QT_END_NAMESPACE
#endif
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp
index 120a84566f..4302a9119b 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp
@@ -121,10 +121,6 @@ const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 1);
// Passed from the RL to RT when GUI has been locked, waiting for sync.
const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2);
-// Passed by the RT to itself to trigger another render pass. This is typically
-// a result of QQuickWindow::update().
-const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 3);
-
// Passed by the RL to the RT to maybe release resource if no windows are
// rendering.
const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4);
@@ -377,7 +373,7 @@ bool QSGD3D12RenderThread::event(QEvent *e)
QCoreApplication::processEvents();
QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
if (wme->destroying)
- delete wd->animationController;
+ wd->animationController.reset();
}
if (wme->destroying)
active = false;
@@ -436,14 +432,6 @@ bool QSGD3D12RenderThread::event(QEvent *e)
return true;
}
- case WM_RequestRepaint:
- if (Q_UNLIKELY(debug_loop()))
- qDebug("RT - WM_RequestPaint");
- // When GUI posts this event, it is followed by a polishAndSync, so we
- // must not exit the event loop yet.
- pendingUpdate |= RepaintRequest;
- break;
-
default:
break;
}
@@ -1018,7 +1006,7 @@ void QSGD3D12ThreadedRenderLoop::handleExposure(QQuickWindow *window)
if (Q_UNLIKELY(debug_loop()))
qDebug("starting render thread");
// Push a few things to the render thread.
- QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController;
+ QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController.data();
if (controller->thread() != w->thread)
controller->moveToThread(w->thread);
if (w->thread->thread() == QThread::currentThread()) {
diff --git a/src/plugins/scenegraph/openvg/qsgopenvgcontext.cpp b/src/plugins/scenegraph/openvg/qsgopenvgcontext.cpp
index e4acda1ffd..a5231e15d1 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvgcontext.cpp
+++ b/src/plugins/scenegraph/openvg/qsgopenvgcontext.cpp
@@ -68,10 +68,14 @@ QSGOpenVGRenderContext::QSGOpenVGRenderContext(QSGContext *context)
}
-void QSGOpenVGRenderContext::initialize(void *context)
+void QSGOpenVGRenderContext::initialize(const QSGRenderContext::InitParams *params)
{
- m_vgContext = static_cast<QOpenVGContext*>(context);
- QSGRenderContext::initialize(context);
+ const InitParams *vgparams = static_cast<const InitParams *>(params);
+ if (vgparams->sType != INIT_PARAMS_MAGIC)
+ qFatal("Invalid OpenVG render context parameters");
+
+ m_vgContext = vgparams->context;
+ QSGRenderContext::initialize(params);
emit initialized();
}
@@ -162,7 +166,7 @@ QSGInternalRectangleNode *QSGOpenVGContext::createInternalRectangleNode()
return new QSGOpenVGInternalRectangleNode();
}
-QSGInternalImageNode *QSGOpenVGContext::createInternalImageNode()
+QSGInternalImageNode *QSGOpenVGContext::createInternalImageNode(QSGRenderContext *)
{
return new QSGOpenVGInternalImageNode();
}
diff --git a/src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h b/src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h
index 31a1e8643f..15d0b3f344 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h
+++ b/src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h
@@ -57,7 +57,13 @@ class QSGOpenVGRenderContext : public QSGRenderContext, public QSGRendererInterf
public:
QSGOpenVGRenderContext(QSGContext *context);
- void initialize(void *context) override;
+ static const int INIT_PARAMS_MAGIC = 0x51E;
+ struct InitParams : public QSGRenderContext::InitParams {
+ int sType = INIT_PARAMS_MAGIC;
+ QOpenVGContext *context = nullptr;
+ };
+
+ void initialize(const QSGRenderContext::InitParams *params) override;
void invalidate() override;
void renderNextFrame(QSGRenderer *renderer, uint fboId) override;
QSGTexture *createTexture(const QImage &image, uint flags) const override;
@@ -94,7 +100,7 @@ public:
QSGLayer *createLayer(QSGRenderContext *renderContext) override;
QSurfaceFormat defaultSurfaceFormat() const override;
QSGInternalRectangleNode *createInternalRectangleNode() override;
- QSGInternalImageNode *createInternalImageNode() override;
+ QSGInternalImageNode *createInternalImageNode(QSGRenderContext *renderContext) override;
#if QT_CONFIG(quick_sprite)
QSGSpriteNode *createSpriteNode() override;
#endif
diff --git a/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.h b/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.h
index 107ec0c892..40d67761bf 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.h
+++ b/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.h
@@ -42,7 +42,6 @@
#include <QtGui/QGlyphRun>
#include <QtCore/QSet>
-#include <QtCore/QLinkedList>
#include <VG/openvg.h>
QT_BEGIN_NAMESPACE
diff --git a/src/plugins/scenegraph/openvg/qsgopenvglayer.cpp b/src/plugins/scenegraph/openvg/qsgopenvglayer.cpp
index 047539d431..b03168b334 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvglayer.cpp
+++ b/src/plugins/scenegraph/openvg/qsgopenvglayer.cpp
@@ -44,7 +44,8 @@
QT_BEGIN_NAMESPACE
QSGOpenVGLayer::QSGOpenVGLayer(QSGRenderContext *renderContext)
- : m_item(nullptr)
+ : QSGLayer(*(new QSGOpenVGLayerPrivate))
+ , m_item(nullptr)
, m_renderer(nullptr)
, m_device_pixel_ratio(1)
, m_mirrorHorizontal(false)
@@ -312,4 +313,9 @@ void QSGOpenVGLayer::grab()
markDirtyTexture(); // Continuously update if 'live' and 'recursive'.
}
+int QSGOpenVGLayerPrivate::comparisonKey() const
+{
+ return 0;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/openvg/qsgopenvglayer.h b/src/plugins/scenegraph/openvg/qsgopenvglayer.h
index 8deedc3347..f2763463cd 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvglayer.h
+++ b/src/plugins/scenegraph/openvg/qsgopenvglayer.h
@@ -42,6 +42,7 @@
#include <private/qsgadaptationlayer_p.h>
#include <private/qsgcontext_p.h>
+#include <private/qsgtexture_p.h>
#include "qopenvgcontext_p.h"
#include "qopenvgoffscreensurface.h"
@@ -50,9 +51,11 @@ QT_BEGIN_NAMESPACE
class QSGOpenVGRenderer;
class QSGOpenVGRenderContext;
+class QSGOpenVGLayerPrivate;
class QSGOpenVGLayer : public QSGLayer
{
+ Q_DECLARE_PRIVATE(QSGOpenVGLayer)
public:
QSGOpenVGLayer(QSGRenderContext *renderContext);
~QSGOpenVGLayer();
@@ -109,6 +112,13 @@ private:
QOpenVGOffscreenSurface *m_secondaryOffscreenSurface;
};
+class QSGOpenVGLayerPrivate : public QSGTexturePrivate
+{
+ Q_DECLARE_PUBLIC(QSGOpenVGLayer)
+public:
+ int comparisonKey() const override;
+};
+
QT_END_NAMESPACE
#endif // QSGOPENVGLAYER_H
diff --git a/src/plugins/scenegraph/openvg/qsgopenvgpainternode.cpp b/src/plugins/scenegraph/openvg/qsgopenvgpainternode.cpp
index fb68ebf2bc..74f30f8189 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvgpainternode.cpp
+++ b/src/plugins/scenegraph/openvg/qsgopenvgpainternode.cpp
@@ -39,6 +39,7 @@
#include "qsgopenvgpainternode.h"
#include "qsgopenvgtexture.h"
+#include <private/qsgcontext_p.h>
#include <qmath.h>
#include <QtGui/QPainter>
diff --git a/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp b/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp
index 994ac251e3..85651ece9d 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp
+++ b/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp
@@ -96,7 +96,7 @@ void QSGOpenVGRenderLoop::windowDestroyed(QQuickWindow *window)
vg->doneCurrent();
}
- delete d->animationController;
+ d->animationController.reset();
}
void QSGOpenVGRenderLoop::exposureChanged(QQuickWindow *window)
@@ -176,7 +176,9 @@ void QSGOpenVGRenderLoop::renderWindow(QQuickWindow *window)
if (vg == nullptr) {
vg = new QOpenVGContext(window);
vg->makeCurrent();
- cd->context->initialize(vg);
+ QSGOpenVGRenderContext::InitParams params;
+ params.context = vg;
+ cd->context->initialize(&params);
} else {
vg->makeCurrent();
}
diff --git a/src/qml/common/common.pri b/src/qml/common/common.pri
new file mode 100644
index 0000000000..bcc3ea0fa0
--- /dev/null
+++ b/src/qml/common/common.pri
@@ -0,0 +1,35 @@
+!build_pass {
+ # Create a header containing a hash that describes this library. For a
+ # released version of Qt, we'll use the .tag file that is updated by git
+ # archive with the commit hash. For unreleased versions, we'll ask git
+ # describe. Note that it won't update unless qmake is run again, even if
+ # the commit change also changed something in this library.
+ tagFile = $$PWD/../../.tag
+ tag =
+ exists($$tagFile) {
+ tag = $$cat($$tagFile, singleline)
+ QMAKE_INTERNAL_INCLUDED_FILES += $$tagFile
+ }
+ !equals(tag, "$${LITERAL_DOLLAR}Format:%H$${LITERAL_DOLLAR}") {
+ QML_COMPILE_HASH = $$tag
+ } else:exists($$PWD/../../.git) {
+ commit = $$system(git rev-parse HEAD)
+ QML_COMPILE_HASH = $$commit
+ }
+ compile_hash_contents = \
+ "// Generated file, DO NOT EDIT" \
+ "$${LITERAL_HASH}define QML_COMPILE_HASH \"$$QML_COMPILE_HASH\"" \
+ "$${LITERAL_HASH}define QML_COMPILE_HASH_LENGTH $$str_size($$QML_COMPILE_HASH)"
+ write_file("$$OUT_PWD/qml_compile_hash_p.h", compile_hash_contents)|error()
+}
+
+HEADERS += \
+ $$PWD/qqmlapiversion_p.h \
+ $$PWD/qqmljsdiagnosticmessage_p.h \
+ $$PWD/qqmljsfixedpoolarray_p.h \
+ $$PWD/qqmljsmemorypool_p.h \
+ $$PWD/qv4alloca_p.h \
+ $$PWD/qv4calldata_p.h \
+ $$PWD/qv4compileddata_p.h \
+ $$PWD/qv4staticvalue_p.h \
+ $$PWD/qv4stringtoarrayindex_p.h
diff --git a/src/qml/common/qqmlapiversion_p.h b/src/qml/common/qqmlapiversion_p.h
new file mode 100644
index 0000000000..eca05558d8
--- /dev/null
+++ b/src/qml/common/qqmlapiversion_p.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLAPIVERSION_P_H
+#define QQMLAPIVERSION_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.
+//
+
+#define Q_QML_PRIVATE_API_VERSION 5
+
+#endif // QQMLAPIVERSION_P_H
diff --git a/src/qml/common/qqmljsdiagnosticmessage_p.h b/src/qml/common/qqmljsdiagnosticmessage_p.h
new file mode 100644
index 0000000000..763332ba76
--- /dev/null
+++ b/src/qml/common/qqmljsdiagnosticmessage_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLJSDIAGNOSTICMESSAGE_P_H
+#define QQMLJSDIAGNOSTICMESSAGE_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/qlogging.h>
+#include <QtCore/qstring.h>
+
+// Include the API version here, to avoid complications when querying it for the
+// QQmlSourceLocation -> line/column change.
+#include <private/qqmlapiversion_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+struct DiagnosticMessage
+{
+ QString message;
+ QtMsgType type = QtCriticalMsg;
+ quint32 line = 0;
+ quint32 column = 0;
+
+ bool isError() const
+ {
+ return type == QtCriticalMsg;
+ }
+
+ bool isWarning() const
+ {
+ return type == QtWarningMsg;
+ }
+
+ bool isValid() const
+ {
+ return !message.isEmpty();
+ }
+};
+} // namespace QQmlJS
+
+Q_DECLARE_TYPEINFO(QQmlJS::DiagnosticMessage, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif // QQMLJSDIAGNOSTICMESSAGE_P_H
diff --git a/src/qml/common/qqmljsfixedpoolarray_p.h b/src/qml/common/qqmljsfixedpoolarray_p.h
new file mode 100644
index 0000000000..b65b994d6c
--- /dev/null
+++ b/src/qml/common/qqmljsfixedpoolarray_p.h
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLJSFIXEDPOOLARRAY_P_H
+#define QQMLJSFIXEDPOOLARRAY_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/qglobal.h>
+#include <private/qqmljsmemorypool_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+
+template <typename T>
+class FixedPoolArray
+{
+ T *data;
+ int count = 0;
+
+public:
+ FixedPoolArray()
+ : data(nullptr)
+ {}
+
+ FixedPoolArray(MemoryPool *pool, int size)
+ { allocate(pool, size); }
+
+ void allocate(MemoryPool *pool, int size)
+ {
+ count = size;
+ data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T)));
+ }
+
+ void allocate(MemoryPool *pool, const QVector<T> &vector)
+ {
+ count = vector.count();
+ data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T)));
+
+ if (QTypeInfo<T>::isComplex) {
+ for (int i = 0; i < count; ++i)
+ new (data + i) T(vector.at(i));
+ } else {
+ memcpy(data, static_cast<const void*>(vector.constData()), count * sizeof(T));
+ }
+ }
+
+ template <typename Container>
+ void allocate(MemoryPool *pool, const Container &container)
+ {
+ count = container.count();
+ data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T)));
+ typename Container::ConstIterator it = container.constBegin();
+ for (int i = 0; i < count; ++i)
+ new (data + i) T(*it++);
+ }
+
+ int size() const
+ { return count; }
+
+ const T &at(int index) const {
+ Q_ASSERT(index >= 0 && index < count);
+ return data[index];
+ }
+
+ T &at(int index) {
+ Q_ASSERT(index >= 0 && index < count);
+ return data[index];
+ }
+
+ T &operator[](int index) {
+ return at(index);
+ }
+
+
+ int indexOf(const T &value) const {
+ for (int i = 0; i < count; ++i)
+ if (data[i] == value)
+ return i;
+ return -1;
+ }
+
+ const T *begin() const { return data; }
+ const T *end() const { return data + count; }
+
+ T *begin() { return data; }
+ T *end() { return data + count; }
+};
+
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QQMLJSFIXEDPOOLARRAY_P_H
diff --git a/src/qml/parser/qqmljsmemorypool_p.h b/src/qml/common/qqmljsmemorypool_p.h
index e7b1f46414..0cf7ea84e6 100644
--- a/src/qml/parser/qqmljsmemorypool_p.h
+++ b/src/qml/common/qqmljsmemorypool_p.h
@@ -51,8 +51,6 @@
// We mean it.
//
-#include "qqmljsglobal_p.h"
-
#include <QtCore/qglobal.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qdebug.h>
@@ -65,7 +63,7 @@ namespace QQmlJS {
class Managed;
-class QML_PARSER_EXPORT MemoryPool : public QSharedData
+class MemoryPool : public QSharedData
{
MemoryPool(const MemoryPool &other);
void operator =(const MemoryPool &other);
@@ -162,7 +160,7 @@ private:
};
};
-class QML_PARSER_EXPORT Managed
+class Managed
{
Q_DISABLE_COPY(Managed)
public:
@@ -174,81 +172,6 @@ public:
void operator delete(void *, MemoryPool *) {}
};
-template <typename T>
-class FixedPoolArray
-{
- T *data;
- int count = 0;
-
-public:
- FixedPoolArray()
- : data(nullptr)
- {}
-
- FixedPoolArray(MemoryPool *pool, int size)
- { allocate(pool, size); }
-
- void allocate(MemoryPool *pool, int size)
- {
- count = size;
- data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T)));
- }
-
- void allocate(MemoryPool *pool, const QVector<T> &vector)
- {
- count = vector.count();
- data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T)));
-
- if (QTypeInfo<T>::isComplex) {
- for (int i = 0; i < count; ++i)
- new (data + i) T(vector.at(i));
- } else {
- memcpy(data, static_cast<const void*>(vector.constData()), count * sizeof(T));
- }
- }
-
- template <typename Container>
- void allocate(MemoryPool *pool, const Container &container)
- {
- count = container.count();
- data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T)));
- typename Container::ConstIterator it = container.constBegin();
- for (int i = 0; i < count; ++i)
- new (data + i) T(*it++);
- }
-
- int size() const
- { return count; }
-
- const T &at(int index) const {
- Q_ASSERT(index >= 0 && index < count);
- return data[index];
- }
-
- T &at(int index) {
- Q_ASSERT(index >= 0 && index < count);
- return data[index];
- }
-
- T &operator[](int index) {
- return at(index);
- }
-
-
- int indexOf(const T &value) const {
- for (int i = 0; i < count; ++i)
- if (data[i] == value)
- return i;
- return -1;
- }
-
- const T *begin() const { return data; }
- const T *end() const { return data + count; }
-
- T *begin() { return data; }
- T *end() { return data + count; }
-};
-
} // namespace QQmlJS
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4alloca_p.h b/src/qml/common/qv4alloca_p.h
index 65c3e4d65a..65c3e4d65a 100644
--- a/src/qml/jsruntime/qv4alloca_p.h
+++ b/src/qml/common/qv4alloca_p.h
diff --git a/src/qml/jit/qv4jithelpers_p.h b/src/qml/common/qv4calldata_p.h
index d9abfc071e..5a5280cb86 100644
--- a/src/qml/jit/qv4jithelpers_p.h
+++ b/src/qml/common/qv4calldata_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef TEMPLATE_H
-#define TEMPLATE_H
+#ifndef QV4CALLDATA_P_H
+#define QV4CALLDATA_P_H
//
// W A R N I N G
@@ -51,42 +51,73 @@
// We mean it.
//
-#include <private/qv4global_p.h>
-
-//QT_REQUIRE_CONFIG(qml_jit);
+#include <private/qv4staticvalue_p.h>
QT_BEGIN_NAMESPACE
namespace QV4 {
-#ifdef V4_ENABLE_JIT
+struct CallData
+{
+ enum Offsets {
+ Function = 0,
+ Context = 1,
+ Accumulator = 2,
+ This = 3,
+ NewTarget = 4,
+ Argc = 5,
+
+ LastOffset = Argc,
+ OffsetCount = LastOffset + 1
+ };
+
+ StaticValue function;
+ StaticValue context;
+ StaticValue accumulator;
+ StaticValue thisObject;
+ StaticValue newTarget;
+ StaticValue _argc;
+
+ int argc() const {
+ Q_ASSERT(_argc.isInteger());
+ return _argc.int_32();
+ }
+
+ void setArgc(int argc) {
+ Q_ASSERT(argc >= 0);
+ _argc.setInt_32(argc);
+ }
+
+ inline ReturnedValue argument(int i) const {
+ return i < argc() ? args[i].asReturnedValue()
+ : StaticValue::undefinedValue().asReturnedValue();
+ }
+
+ StaticValue args[1];
-namespace JIT {
-namespace Helpers {
+ static Q_DECL_CONSTEXPR int HeaderSize()
+ {
+ return offsetof(CallData, args) / sizeof(QV4::StaticValue);
+ }
-void convertThisToObject(ExecutionEngine *engine, Value *t);
-ReturnedValue loadGlobalLookup(Function *f, ExecutionEngine *engine, int index);
-ReturnedValue loadQmlContextPropertyLookup(Function *f, ExecutionEngine *engine, int index);
-ReturnedValue toObject(ExecutionEngine *engine, const Value &obj);
-ReturnedValue exp(const Value &base, const Value &exp);
-ReturnedValue getLookup(Function *f, ExecutionEngine *engine, const Value &base, int index);
-void setLookupStrict(Function *f, int index, Value &base, const Value &value);
-void setLookupSloppy(Function *f, int index, Value &base, const Value &value);
-void pushBlockContext(Value *stack, int index);
-void cloneBlockContext(Value *contextSlot);
-void pushScriptContext(Value *stack, ExecutionEngine *engine, int index);
-void popScriptContext(Value *stack, ExecutionEngine *engine);
-ReturnedValue deleteProperty(QV4::Function *function, const QV4::Value &base, const QV4::Value &index);
-ReturnedValue deleteName(Function *function, int name);
-void throwOnNullOrUndefined(ExecutionEngine *engine, const Value &v);
+ template<typename Value>
+ Value *argValues();
-} // Helpers namespace
-} // JIT namespace
+ template<typename Value>
+ const Value *argValues() const;
+};
-#endif // V4_ENABLE_JIT
+Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value);
+Q_STATIC_ASSERT(offsetof(CallData, function ) == CallData::Function * sizeof(StaticValue));
+Q_STATIC_ASSERT(offsetof(CallData, context ) == CallData::Context * sizeof(StaticValue));
+Q_STATIC_ASSERT(offsetof(CallData, accumulator) == CallData::Accumulator * sizeof(StaticValue));
+Q_STATIC_ASSERT(offsetof(CallData, thisObject ) == CallData::This * sizeof(StaticValue));
+Q_STATIC_ASSERT(offsetof(CallData, newTarget ) == CallData::NewTarget * sizeof(StaticValue));
+Q_STATIC_ASSERT(offsetof(CallData, _argc ) == CallData::Argc * sizeof(StaticValue));
+Q_STATIC_ASSERT(offsetof(CallData, args ) == 6 * sizeof(StaticValue));
-} // QV4 namespace
+} // namespace QV4
QT_END_NAMESPACE
-#endif // TEMPLATE_H
+#endif // QV4CALLDATA_P_H
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h
index 7b26939da0..c3ddce5884 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/common/qv4compileddata_p.h
@@ -50,36 +50,35 @@
// We mean it.
//
+#include <functional>
+
#include <QtCore/qstring.h>
-#include <QVector>
-#include <QStringList>
-#include <QHash>
-#include <QUrl>
-
-#include <private/qv4value_p.h>
-#include <private/qv4executableallocator_p.h>
-#include <private/qqmlrefcount_p.h>
-#include <private/qqmlnullablevalue_p.h>
-#include <private/qv4identifier_p.h>
-#include <private/qflagpointer_p.h>
-#include <private/qendian_p.h>
-#include <private/qqmljsastfwd_p.h>
-#ifndef V4_BOOTSTRAP
-#include <private/qqmltypenamecache_p.h>
-#include <private/qqmlpropertycache_p.h>
-#include "private/qintrusivelist_p.h"
+#include <QtCore/qscopeguard.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qhash.h>
+
+#if QT_CONFIG(temporaryfile)
+#include <QtCore/qsavefile.h>
#endif
+#include <private/qendian_p.h>
+#include <private/qv4staticvalue_p.h>
+#include <functional>
+
QT_BEGIN_NAMESPACE
// Bump this whenever the compiler data structures change in an incompatible way.
-#define QV4_DATA_STRUCTURE_VERSION 0x21
+//
+// IMPORTANT:
+//
+// Also change the comment behind the number to describe the latest change. This has the added
+// benefit that if another patch changes the version too, it will result in a merge conflict, and
+// not get removed silently.
+#define QV4_DATA_STRUCTURE_VERSION 0x24 // Collect function parameter types
class QIODevice;
-class QQmlPropertyCache;
-class QQmlPropertyData;
class QQmlTypeNameCache;
-class QQmlScriptData;
class QQmlType;
class QQmlEngine;
@@ -88,14 +87,14 @@ struct Document;
}
namespace QV4 {
-
namespace Heap {
struct Module;
+struct String;
+struct InternalClass;
};
struct Function;
class EvalISelFactory;
-class CompilationUnitMapper;
namespace CompiledData {
@@ -128,8 +127,6 @@ struct Location
Location() : _dummy(0) { }
- Location &operator=(const QQmlJS::AST::SourceLocation &astLocation);
-
inline bool operator<(const Location &other) const {
return line < other.line ||
(line == other.line && column < other.column);
@@ -259,6 +256,29 @@ struct Block
};
static_assert(sizeof(Block) == 12, "Block structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+enum class BuiltinType : unsigned int {
+ Var = 0, Variant, Int, Bool, Real, String, Url, Color,
+ Font, Time, Date, DateTime, Rect, Point, Size,
+ Vector2D, Vector3D, Vector4D, Matrix4x4, Quaternion, InvalidBuiltin
+};
+
+struct ParameterType
+{
+ union {
+ quint32 _dummy;
+ quint32_le_bitfield<0, 1> indexIsBuiltinType;
+ quint32_le_bitfield<1, 31> typeNameIndexOrBuiltinType;
+ };
+};
+static_assert(sizeof(ParameterType) == 4, "ParameterType structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+struct Parameter
+{
+ quint32_le nameIndex;
+ ParameterType type;
+};
+static_assert(sizeof(Parameter) == 8, "Parameter structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
// Function is aligned on an 8-byte boundary to make sure there are no bus errors or penalties
// for unaligned access. The ordering of the fields is also from largest to smallest.
struct Function
@@ -277,6 +297,7 @@ struct Function
quint16_le length;
quint16_le nFormals;
quint32_le formalsOffset; // Can't turn this into a calculated offset because of the mutation in CompilationUnit::createUnitData.
+ ParameterType returnType;
quint32_le localsOffset;
quint16_le nLocals;
quint16_le nLineNumbers;
@@ -291,10 +312,6 @@ struct Function
quint32_le nLabelInfos;
size_t labelInfosOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); }
- typedef quint16_le TraceInfoCount;
- TraceInfoCount nTraceInfos;
- static Q_DECL_CONSTEXPR TraceInfoCount NoTracing() { return TraceInfoCount::max(); }
-
// Keep all unaligned data at the end
quint8 flags;
quint8 padding1;
@@ -302,13 +319,13 @@ struct Function
// quint32 formalsIndex[nFormals]
// quint32 localsIndex[nLocals]
- const quint32_le *formalsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + formalsOffset); }
+ const Parameter *formalsTable() const { return reinterpret_cast<const Parameter *>(reinterpret_cast<const char *>(this) + formalsOffset); }
const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); }
const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast<const CodeOffsetToLine *>(reinterpret_cast<const char *>(this) + lineNumberOffset()); }
// --- QQmlPropertyCacheCreator interface
- const quint32_le *formalsBegin() const { return formalsTable(); }
- const quint32_le *formalsEnd() const { return formalsTable() + nFormals; }
+ const Parameter *formalsBegin() const { return formalsTable(); }
+ const Parameter *formalsEnd() const { return formalsTable() + nFormals; }
// ---
const quint32_le *labelInfoTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + labelInfosOffset()); }
@@ -316,7 +333,7 @@ struct Function
const char *code() const { return reinterpret_cast<const char *>(this) + codeOffset; }
static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int labelInfoSize, int codeSize) {
- int trailingData = (nFormals + nLocals + nInnerfunctions + labelInfoSize)*sizeof (quint32)
+ int trailingData = nFormals * sizeof(Parameter) + (nLocals + nInnerfunctions + labelInfoSize)*sizeof (quint32)
+ nLines*sizeof(CodeOffsetToLine);
size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize);
Q_ASSERT(size < INT_MAX);
@@ -327,7 +344,7 @@ struct Function
return (a + 7) & ~size_t(7);
}
};
-static_assert(sizeof(Function) == 52, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+static_assert(sizeof(Function) == 56, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Method {
enum Type {
@@ -415,7 +432,7 @@ static_assert(sizeof(ImportEntry) == 16, "ImportEntry structure needs to have th
// Qml data structures
-struct Q_QML_EXPORT TranslationData
+struct TranslationData
{
quint32_le stringIndex;
quint32_le commentIndex;
@@ -424,7 +441,7 @@ struct Q_QML_EXPORT TranslationData
};
static_assert(sizeof(TranslationData) == 16, "TranslationData structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
-struct Q_QML_PRIVATE_EXPORT Binding
+struct Binding
{
quint32_le propertyNameIndex;
@@ -521,22 +538,52 @@ struct Q_QML_PRIVATE_EXPORT Binding
bool isFunctionExpression() const { return (flags & IsFunctionExpression); }
- static QString escapedString(const QString &string);
+ //reverse of Lexer::singleEscape()
+ static QString escapedString(const QString &string)
+ {
+ QString tmp = QLatin1String("\"");
+ for (int i = 0; i < string.length(); ++i) {
+ const QChar &c = string.at(i);
+ switch (c.unicode()) {
+ case 0x08:
+ tmp += QLatin1String("\\b");
+ break;
+ case 0x09:
+ tmp += QLatin1String("\\t");
+ break;
+ case 0x0A:
+ tmp += QLatin1String("\\n");
+ break;
+ case 0x0B:
+ tmp += QLatin1String("\\v");
+ break;
+ case 0x0C:
+ tmp += QLatin1String("\\f");
+ break;
+ case 0x0D:
+ tmp += QLatin1String("\\r");
+ break;
+ case 0x22:
+ tmp += QLatin1String("\\\"");
+ break;
+ case 0x27:
+ tmp += QLatin1String("\\\'");
+ break;
+ case 0x5C:
+ tmp += QLatin1String("\\\\");
+ break;
+ default:
+ tmp += c;
+ break;
+ }
+ }
+ tmp += QLatin1Char('\"');
+ return tmp;
+ }
bool isTranslationBinding() const { return type == Type_Translation || type == Type_TranslationById; }
bool evaluatesToString() const { return type == Type_String || isTranslationBinding(); }
-#ifndef V4_BOOTSTRAP
- QString valueAsString(const CompilationUnit *unit) const;
- QString valueAsScriptString(const CompilationUnit *unit) const;
-#endif
- double valueAsNumber(const Value *constantTable) const
- {
- if (type != Type_Number)
- return 0.0;
- return constantTable[value.constantValueIndex].doubleValue();
- }
-
bool valueAsBoolean() const
{
if (type == Type_Boolean)
@@ -580,15 +627,6 @@ struct Enum
};
static_assert(sizeof(Enum) == 12, "Enum structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
-struct Parameter
-{
- quint32_le nameIndex;
- quint32_le type;
- quint32_le customTypeNameIndex;
- Location location;
-};
-static_assert(sizeof(Parameter) == 16, "Parameter structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
-
struct Signal
{
quint32_le nameIndex;
@@ -616,24 +654,33 @@ static_assert(sizeof(Signal) == 12, "Signal structure needs to have the expected
struct Property
{
- enum Type : unsigned int { Var = 0, Variant, Int, Bool, Real, String, Url, Color,
- Font, Time, Date, DateTime, Rect, Point, Size,
- Vector2D, Vector3D, Vector4D, Matrix4x4, Quaternion,
- Custom, CustomList };
-
- enum Flags : unsigned int {
- IsReadOnly = 0x1
- };
-
quint32_le nameIndex;
union {
- quint32_le_bitfield<0, 31> type;
- quint32_le_bitfield<31, 1> flags; // readonly
+ quint32_le_bitfield<0, 29> builtinTypeOrTypeNameIndex;
+ quint32_le_bitfield<29, 1> isBuiltinType;
+ quint32_le_bitfield<30, 1> isList;
+ quint32_le_bitfield<31, 1> isReadOnly;
};
- quint32_le customTypeNameIndex; // If type >= Custom
+
Location location;
+
+ void setBuiltinType(BuiltinType t)
+ {
+ builtinTypeOrTypeNameIndex = static_cast<quint32>(t);
+ isBuiltinType = true;
+ }
+ BuiltinType builtinType() const {
+ if (isBuiltinType)
+ return static_cast<BuiltinType>(quint32(builtinTypeOrTypeNameIndex));
+ return BuiltinType::InvalidBuiltin;
+ }
+ void setCustomType(int nameIndex)
+ {
+ builtinTypeOrTypeNameIndex = nameIndex;
+ isBuiltinType = false;
+ }
};
-static_assert(sizeof(Property) == 16, "Property structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+static_assert(sizeof(Property) == 12, "Property structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Alias {
enum Flags : unsigned int {
@@ -830,7 +877,6 @@ static_assert(sizeof(QmlUnit) == 16, "QmlUnit structure needs to have the expect
enum { QmlCompileHashSpace = 48 };
static const char magic_str[] = "qv4cdata";
-extern const char qml_compile_hash[QmlCompileHashSpace + 1];
struct Unit
{
@@ -845,8 +891,6 @@ struct Unit
char libraryVersionHash[QmlCompileHashSpace];
char md5Checksum[16]; // checksum of all bytes following this field.
- void generateChecksum();
-
char dependencyMD5Checksum[16];
enum : unsigned int {
@@ -894,8 +938,6 @@ struct Unit
quint32_le offsetToQmlUnit;
- bool verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const;
-
/* QML specific fields */
const QmlUnit *qmlUnit() const {
@@ -1029,8 +1071,8 @@ struct TypeReferenceMap : QHash<int, TypeReference>
auto prop = obj->propertiesBegin();
auto propEnd = obj->propertiesEnd();
for ( ; prop != propEnd; ++prop) {
- if (prop->type >= QV4::CompiledData::Property::Custom) {
- TypeReference &r = this->add(prop->customTypeNameIndex, prop->location);
+ if (!prop->isBuiltinType) {
+ TypeReference &r = this->add(prop->builtinTypeOrTypeNameIndex, prop->location);
r.errorWhenNotFound = true;
}
}
@@ -1051,142 +1093,144 @@ struct TypeReferenceMap : QHash<int, TypeReference>
}
};
-#ifndef V4_BOOTSTRAP
-struct ResolvedTypeReference;
-// map from name index
-// While this could be a hash, a map is chosen here to provide a stable
-// order, which is used to calculating a check-sum on dependent meta-objects.
-struct ResolvedTypeReferenceMap: public QMap<int, ResolvedTypeReference*>
+using DependentTypesHasher = std::function<QByteArray()>;
+
+// This is how this hooks into the existing structures:
+
+struct CompilationUnitBase
{
- bool addToHash(QCryptographicHash *hash, QQmlEngine *engine) const;
-};
+ Q_DISABLE_COPY(CompilationUnitBase)
-using DependentTypesHasher = std::function<bool(QCryptographicHash *)>;
-#else
-struct DependentTypesHasher {};
-#endif
+ CompilationUnitBase() = default;
+ ~CompilationUnitBase() = default;
-// index is per-object binding index
-typedef QVector<QQmlPropertyData*> BindingPropertyData;
+ CompilationUnitBase(CompilationUnitBase &&other) noexcept { *this = std::move(other); }
-// This is how this hooks into the existing structures:
+ CompilationUnitBase &operator=(CompilationUnitBase &&other) noexcept
+ {
+ if (this != &other) {
+ runtimeStrings = other.runtimeStrings;
+ other.runtimeStrings = nullptr;
+ constants = other.constants;
+ other.constants = nullptr;
+ runtimeRegularExpressions = other.runtimeRegularExpressions;
+ other.runtimeRegularExpressions = nullptr;
+ runtimeClasses = other.runtimeClasses;
+ other.runtimeClasses = nullptr;
+ imports = other.imports;
+ other.imports = nullptr;
+ }
+ return *this;
+ }
-struct Q_QML_PRIVATE_EXPORT CompilationUnitBase
-{
// pointers either to data->constants() or little-endian memory copy.
- QV4::Heap::String **runtimeStrings = nullptr; // Array
- const Value* constants = nullptr;
- QV4::Value *runtimeRegularExpressions = nullptr;
- QV4::Heap::InternalClass **runtimeClasses = nullptr;
- const Value** imports = nullptr;
+ Heap::String **runtimeStrings = nullptr; // Array
+ const StaticValue* constants = nullptr;
+ QV4::StaticValue *runtimeRegularExpressions = nullptr;
+ Heap::InternalClass **runtimeClasses = nullptr;
+ const StaticValue** imports = nullptr;
};
Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value);
Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeStrings) == 0);
Q_STATIC_ASSERT(offsetof(CompilationUnitBase, constants) == sizeof(QV4::Heap::String **));
-Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeRegularExpressions) == offsetof(CompilationUnitBase, constants) + sizeof(const Value *));
-Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeClasses) == offsetof(CompilationUnitBase, runtimeRegularExpressions) + sizeof(const Value *));
-Q_STATIC_ASSERT(offsetof(CompilationUnitBase, imports) == offsetof(CompilationUnitBase, runtimeClasses) + sizeof(const Value *));
+Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeRegularExpressions) == offsetof(CompilationUnitBase, constants) + sizeof(const StaticValue *));
+Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeClasses) == offsetof(CompilationUnitBase, runtimeRegularExpressions) + sizeof(const StaticValue *));
+Q_STATIC_ASSERT(offsetof(CompilationUnitBase, imports) == offsetof(CompilationUnitBase, runtimeClasses) + sizeof(const StaticValue *));
-struct Q_QML_PRIVATE_EXPORT CompilationUnit final : public CompilationUnitBase
+struct CompilationUnit : public CompilationUnitBase
{
+ Q_DISABLE_COPY(CompilationUnit)
+
const Unit *data = nullptr;
const QmlUnit *qmlData = nullptr;
+ QStringList dynamicStrings;
public:
- CompilationUnit(const Unit *unitData = nullptr, const QString &fileName = QString(), const QString &finalUrlString = QString());
- ~CompilationUnit();
+ using CompiledObject = CompiledData::Object;
- void addref()
+ CompilationUnit(const Unit *unitData = nullptr, const QString &fileName = QString(),
+ const QString &finalUrlString = QString())
{
- Q_ASSERT(refCount.load() > 0);
- refCount.ref();
+ setUnitData(unitData, nullptr, fileName, finalUrlString);
}
- void release()
- {
- Q_ASSERT(refCount.load() > 0);
- if (!refCount.deref())
- destroy();
- }
- int count() const
+ ~CompilationUnit()
{
- return refCount.load();
- }
-
- const Unit *unitData() const { return data; }
- void setUnitData(const Unit *unitData, const QmlUnit *qmlUnit = nullptr,
- const QString &fileName = QString(), const QString &finalUrlString = QString());
+ if (data) {
+ if (data->qmlUnit() != qmlData)
+ free(const_cast<QmlUnit *>(qmlData));
+ qmlData = nullptr;
-#ifndef V4_BOOTSTRAP
- QIntrusiveListNode nextCompilationUnit;
- ExecutionEngine *engine = nullptr;
- QQmlEnginePrivate *qmlEngine = nullptr; // only used in QML environment for composite types, not in plain QJSEngine case.
+ if (!(data->flags & QV4::CompiledData::Unit::StaticData))
+ free(const_cast<Unit *>(data));
+ }
+ data = nullptr;
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ delete [] constants;
+ constants = nullptr;
+#endif
- // url() and fileName() shall be used to load the actual QML/JS code or to show errors or
- // warnings about that code. They include any potential URL interceptions and thus represent the
- // "physical" location of the code.
- //
- // finalUrl() and finalUrlString() shall be used to resolve further URLs referred to in the code
- // They are _not_ intercepted and thus represent the "logical" name for the code.
+ delete [] imports;
+ imports = nullptr;
+ }
- QString fileName() const { return m_fileName; }
- QString finalUrlString() const { return m_finalUrlString; }
- QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; }
- QUrl finalUrl() const
+ CompilationUnit(CompilationUnit &&other) noexcept
{
- if (m_finalUrl.isNull)
- m_finalUrl = QUrl(finalUrlString());
- return m_finalUrl;
+ *this = std::move(other);
}
- QV4::Lookup *runtimeLookups = nullptr;
- QVector<QV4::Function *> runtimeFunctions;
- QVector<QV4::Heap::InternalClass *> runtimeBlocks;
- mutable QVector<QV4::Heap::Object *> templateObjects;
- mutable QQmlNullableValue<QUrl> m_url;
- mutable QQmlNullableValue<QUrl> m_finalUrl;
-
- // QML specific fields
- QQmlPropertyCacheVector propertyCaches;
- QQmlRefPointer<QQmlPropertyCache> rootPropertyCache() const { return propertyCaches.at(/*root object*/0); }
-
- QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
-
- // index is object index. This allows fast access to the
- // property data when initializing bindings, avoiding expensive
- // lookups by string (property name).
- QVector<BindingPropertyData> bindingPropertyDataPerObject;
-
- // mapping from component object index (CompiledData::Unit object index that points to component) to identifier hash of named objects
- // this is initialized on-demand by QQmlContextData
- QHash<int, IdentifierHash> namedObjectsPerComponentCache;
- inline IdentifierHash namedObjectsPerComponent(int componentObjectIndex);
-
- void finalizeCompositeType(QQmlEnginePrivate *qmlEngine);
-
- int totalBindingsCount = 0; // Number of bindings used in this type
- int totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses
- int totalObjectCount = 0; // Number of objects explicitly instantiated
-
- QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts;
- ResolvedTypeReferenceMap resolvedTypes;
- ResolvedTypeReference *resolvedType(int id) const { return resolvedTypes.value(id); }
+ CompilationUnit &operator=(CompilationUnit &&other) noexcept
+ {
+ if (this != &other) {
+ data = other.data;
+ other.data = nullptr;
+ qmlData = other.qmlData;
+ other.qmlData = nullptr;
+ dynamicStrings = std::move(other.dynamicStrings);
+ other.dynamicStrings.clear();
+ m_fileName = std::move(other.m_fileName);
+ other.m_fileName.clear();
+ m_finalUrlString = std::move(other.m_finalUrlString);
+ other.m_finalUrlString.clear();
+ m_module = other.m_module;
+ other.m_module = nullptr;
+ CompilationUnitBase::operator=(std::move(other));
+ }
+ return *this;
+ }
- bool verifyChecksum(const DependentTypesHasher &dependencyHasher) const;
+ const Unit *unitData() const { return data; }
- int metaTypeId = -1;
- int listMetaTypeId = -1;
- bool isRegisteredWithEngine = false;
+ void setUnitData(const Unit *unitData, const QmlUnit *qmlUnit = nullptr,
+ const QString &fileName = QString(), const QString &finalUrlString = QString())
+ {
+ data = unitData;
+ qmlData = nullptr;
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ delete [] constants;
+#endif
+ constants = nullptr;
+ m_fileName.clear();
+ m_finalUrlString.clear();
+ if (!data)
+ return;
+
+ qmlData = qmlUnit ? qmlUnit : data->qmlUnit();
+
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ StaticValue *bigEndianConstants = new StaticValue[data->constantTableSize];
+ const quint64_le *littleEndianConstants = data->constants();
+ for (uint i = 0; i < data->constantTableSize; ++i)
+ bigEndianConstants[i] = StaticValue::fromReturnedValue(littleEndianConstants[i]);
+ constants = bigEndianConstants;
+#else
+ constants = reinterpret_cast<const StaticValue*>(data->constants());
+#endif
- QScopedPointer<CompilationUnitMapper> backingFile;
- QStringList dynamicStrings;
+ m_fileName = !fileName.isEmpty() ? fileName : stringAt(data->sourceFileIndex);
+ m_finalUrlString = !finalUrlString.isEmpty() ? finalUrlString : stringAt(data->finalUrlIndex);
+ }
- // --- interface for QQmlPropertyCacheCreator
- typedef Object CompiledObject;
- int objectCount() const { return qmlData->nObjects; }
- const Object *objectAt(int index) const { return qmlData->objectAt(index); }
- int importCount() const { return qmlData->nImports; }
- const Import *importAt(int index) const { return qmlData->importAt(index); }
QString stringAt(int index) const
{
if (uint(index) >= data->stringTableSize)
@@ -1194,117 +1238,84 @@ public:
return data->stringAtInternal(index);
}
- Heap::Object *templateObjectAt(int index) const;
-
- struct FunctionIterator
- {
- FunctionIterator(const Unit *unit, const Object *object, int index) : unit(unit), object(object), index(index) {}
- const Unit *unit;
- const Object *object;
- int index;
-
- const Function *operator->() const { return unit->functionAt(object->functionOffsetTable()[index]); }
- void operator++() { ++index; }
- bool operator==(const FunctionIterator &rhs) const { return index == rhs.index; }
- bool operator!=(const FunctionIterator &rhs) const { return index != rhs.index; }
- };
- FunctionIterator objectFunctionsBegin(const Object *object) const { return FunctionIterator(data, object, 0); }
- FunctionIterator objectFunctionsEnd(const Object *object) const { return FunctionIterator(data, object, object->nFunctions); }
- // ---
-
- bool isESModule() const { return data->flags & Unit::IsESModule; }
- bool isSharedLibrary() const { return data->flags & Unit::IsSharedLibrary; }
- QStringList moduleRequests() const;
- Heap::Module *instantiate(ExecutionEngine *engine);
- const Value *resolveExport(QV4::String *exportName);
- QStringList exportedNames() const;
- void evaluate();
- void evaluateModuleRequests();
-
- QV4::Function *linkToEngine(QV4::ExecutionEngine *engine);
- void unlink();
-
- void markObjects(MarkStack *markStack);
-
- bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString);
-
- static QString localCacheFilePath(const QUrl &url);
-
-protected:
- quint32 totalStringCount() const
- { return data->stringTableSize; }
+ QString fileName() const { return m_fileName; }
+ QString finalUrlString() const { return m_finalUrlString; }
-#else // V4_BOOTSTRAP
- QString stringAt(int index) const { return data->stringAtInternal(index); }
-#endif // V4_BOOTSTRAP
+ Heap::Module *module() const { return m_module; }
+ void setModule(Heap::Module *module) { m_module = module; }
private:
- void destroy();
-
- struct ResolveSetEntry
- {
- ResolveSetEntry() {}
- ResolveSetEntry(CompilationUnit *module, QV4::String *exportName)
- : module(module), exportName(exportName) {}
- CompilationUnit *module = nullptr;
- QV4::String *exportName = nullptr;
- };
-
- const Value *resolveExportRecursively(QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet);
- const ExportEntry *lookupNameInExportTable(const ExportEntry *firstExportEntry, int tableSize, QV4::String *name) const;
- void getExportedNamesRecursively(QStringList *names, QVector<const CompilationUnit *> *exportNameSet, bool includeDefaultExport = true) const;
-
QString m_fileName; // initialized from data->sourceFileIndex
QString m_finalUrlString; // initialized from data->finalUrlIndex
- QAtomicInt refCount = 1;
-
- Q_NEVER_INLINE IdentifierHash createNamedObjectsPerComponent(int componentObjectIndex);
-
Heap::Module *m_module = nullptr;
+};
+class SaveableUnitPointer
+{
+ Q_DISABLE_COPY_MOVE(SaveableUnitPointer)
public:
-#if defined(V4_BOOTSTRAP)
- bool saveToDisk(const QString &outputFileName, QString *errorString);
+ SaveableUnitPointer(const Unit *unit, quint32 temporaryFlags = Unit::StaticData) :
+ unit(unit),
+ temporaryFlags(temporaryFlags)
+ {
+ }
+
+ ~SaveableUnitPointer() = default;
+
+ template<typename Char>
+ bool saveToDisk(const std::function<bool(const Char *, quint32)> &writer) const
+ {
+ auto cleanup = qScopeGuard([this]() { mutableFlags() ^= temporaryFlags; });
+ mutableFlags() |= temporaryFlags;
+ return writer(data<Char>(), size());
+ }
+
+ static bool writeDataToFile(const QString &outputFileName, const char *data, quint32 size,
+ QString *errorString)
+ {
+#if QT_CONFIG(temporaryfile)
+ QSaveFile cacheFile(outputFileName);
+ if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)
+ || cacheFile.write(data, size) != size
+ || !cacheFile.commit()) {
+ *errorString = cacheFile.errorString();
+ return false;
+ }
+
+ errorString->clear();
+ return true;
#else
- bool saveToDisk(const QUrl &unitUrl, QString *errorString);
+ Q_UNUSED(outputFileName)
+ *errorString = QStringLiteral("features.temporaryfile is disabled.");
+ return false;
#endif
-};
-
-#ifndef V4_BOOTSTRAP
-struct ResolvedTypeReference
-{
- ResolvedTypeReference()
- : majorVersion(0)
- , minorVersion(0)
- , isFullyDynamicType(false)
- {}
+ }
- QQmlType type;
- QQmlRefPointer<QQmlPropertyCache> typePropertyCache;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
+private:
+ const Unit *unit;
+ quint32 temporaryFlags;
- int majorVersion;
- int minorVersion;
- // Types such as QQmlPropertyMap can add properties dynamically at run-time and
- // therefore cannot have a property cache installed when instantiated.
- bool isFullyDynamicType;
+ quint32_le &mutableFlags() const
+ {
+ return const_cast<Unit *>(unit)->flags;
+ }
- QQmlRefPointer<QQmlPropertyCache> propertyCache() const;
- QQmlRefPointer<QQmlPropertyCache> createPropertyCache(QQmlEngine *);
- bool addToHash(QCryptographicHash *hash, QQmlEngine *engine);
+ template<typename Char>
+ const Char *data() const
+ {
+ Q_STATIC_ASSERT(sizeof(Char) == 1);
+ const Char *dataPtr;
+ memcpy(&dataPtr, &unit, sizeof(dataPtr));
+ return dataPtr;
+ }
- void doDynamicTypeCheck();
+ quint32 size() const
+ {
+ return unit->unitSize;
+ }
};
-IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjectIndex)
-{
- auto it = namedObjectsPerComponentCache.find(componentObjectIndex);
- if (Q_UNLIKELY(it == namedObjectsPerComponentCache.end()))
- return createNamedObjectsPerComponent(componentObjectIndex);
- return *it;
-}
-#endif // V4_BOOTSTRAP
} // CompiledData namespace
} // QV4 namespace
diff --git a/src/qml/common/qv4staticvalue_p.h b/src/qml/common/qv4staticvalue_p.h
new file mode 100644
index 0000000000..0716f7ea20
--- /dev/null
+++ b/src/qml/common/qv4staticvalue_p.h
@@ -0,0 +1,559 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4STATICVALUE_P_H
+#define QV4STATICVALUE_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/private/qnumeric_p.h>
+#include <cstring>
+
+#ifdef QT_NO_DEBUG
+#define QV4_NEARLY_ALWAYS_INLINE Q_ALWAYS_INLINE
+#else
+#define QV4_NEARLY_ALWAYS_INLINE inline
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+// ReturnedValue is used to return values from runtime methods
+// the type has to be a primitive type (no struct or union), so that the compiler
+// will return it in a register on all platforms.
+// It will be returned in rax on x64, [eax,edx] on x86 and [r0,r1] on arm
+typedef quint64 ReturnedValue;
+
+struct Double {
+ quint64 d;
+
+ Double(double dbl) {
+ memcpy(&d, &dbl, sizeof(double));
+ }
+
+ int sign() const {
+ return (d >> 63) ? -1 : 1;
+ }
+
+ bool isDenormal() const {
+ return static_cast<int>((d << 1) >> 53) == 0;
+ }
+
+ int exponent() const {
+ return static_cast<int>((d << 1) >> 53) - 1023;
+ }
+
+ quint64 significant() const {
+ quint64 m = (d << 12) >> 12;
+ if (!isDenormal())
+ m |= (static_cast<quint64>(1) << 52);
+ return m;
+ }
+
+ static int toInt32(double d) {
+ int i = static_cast<int>(d);
+ if (i == d)
+ return i;
+ return Double(d).toInt32();
+ }
+
+ int toInt32() {
+ int e = exponent() - 52;
+ if (e < 0) {
+ if (e <= -53)
+ return 0;
+ return sign() * static_cast<int>(significant() >> -e);
+ } else {
+ if (e > 31)
+ return 0;
+ return sign() * (static_cast<int>(significant()) << e);
+ }
+ }
+};
+
+struct StaticValue
+{
+ StaticValue() = default;
+ constexpr StaticValue(quint64 val) : _val(val) {}
+
+ StaticValue &operator=(ReturnedValue v)
+ {
+ _val = v;
+ return *this;
+ }
+
+ template<typename Value>
+ StaticValue &operator=(const Value &);
+
+ template<typename Value>
+ const Value &asValue() const;
+
+ template<typename Value>
+ Value &asValue();
+
+ /*
+ We use 8 bytes for a value and a different variant of NaN boxing. A Double
+ NaN (actually -qNaN) is indicated by a number that has the top 13 bits set, and for a
+ signalling NaN it is the top 14 bits. The other values are usually set to 0 by the
+ processor, and are thus free for us to store other data. We keep pointers in there for
+ managed objects, and encode the other types using the free space given to use by the unused
+ bits for NaN values. This also works for pointers on 64 bit systems, as they all currently
+ only have 48 bits of addressable memory. (Note: we do leave the lower 49 bits available for
+ pointers.)
+
+ We xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will
+ get encoded with bits 63-49 all set to 0. We then use bit 48 to distinguish between
+ managed/undefined (0), or Null/Int/Bool/Empty (1). So, storing a 49 bit pointer will leave
+ the top 15 bits 0, which is exactly the 'natural' representation of pointers. If bit 49 is
+ set, bit 48 indicates Empty (0) or integer-convertible (1). Then the 3 bit below that are
+ used to encode Null/Int/Bool.
+
+ Undefined is encoded as a managed pointer with value 0. This is the same as a nullptr.
+
+ Specific bit-sequences:
+ 0 = always 0
+ 1 = always 1
+ x = stored value
+ a,b,c,d = specific bit values, see notes
+
+ 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 |
+ 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value
+ ------------------------------------------------------------------------+--------------
+ 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined
+ 00000000 0000000x xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer)
+ a0000000 0000bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf
+ dddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double
+ 00000000 00000010 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole)
+ 00000000 00000010 10000000 00000000 00000000 00000000 00000000 00000000 | Null
+ 00000000 00000011 00000000 00000000 00000000 00000000 00000000 0000000x | Bool
+ 00000000 00000011 10000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int
+
+ Notes:
+ - a: xor-ed signbit, always 1 for NaN
+ - bc, xor-ed values: 11 = inf, 10 = sNaN, 01 = qNaN, 00 = boxed value
+ - d: xor-ed bits, where at least one bit is set, so: (val >> (64-14)) > 0
+ - Undefined maps to C++ nullptr, so the "default" initialization is the same for both C++
+ and JS
+ - Managed has the left 15 bits set to 0, so: (val >> (64-15)) == 0
+ - empty, Null, Bool, and Int have the left 14 bits set to 0, and bit 49 set to 1,
+ so: (val >> (64-15)) == 1
+ - Null, Bool, and Int have bit 48 set, indicating integer-convertible
+ - xoring _val with NaNEncodeMask will convert to a double in "natural" representation, where
+ any non double results in a NaN
+ - on 32bit we can use the fact that addresses are 32bits wide, so the tag part (bits 32 to
+ 63) are zero. No need to shift.
+ */
+
+ quint64 _val;
+
+ QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 &rawValueRef() { return _val; }
+ QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 rawValue() const { return _val; }
+ QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setRawValue(quint64 raw) { _val = raw; }
+
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ static inline int valueOffset() { return 0; }
+ static inline int tagOffset() { return 4; }
+#else // !Q_LITTLE_ENDIAN
+ static inline int valueOffset() { return 4; }
+ static inline int tagOffset() { return 0; }
+#endif
+ static inline constexpr quint64 tagValue(quint32 tag, quint32 value) { return quint64(tag) << 32 | value; }
+ QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; }
+ QV4_NEARLY_ALWAYS_INLINE constexpr quint32 value() const { return _val & quint64(~quint32(0)); }
+ QV4_NEARLY_ALWAYS_INLINE constexpr quint32 tag() const { return _val >> 32; }
+ QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTag(quint32 tag) { setTagValue(tag, value()); }
+
+ QV4_NEARLY_ALWAYS_INLINE constexpr int int_32() const
+ {
+ return int(value());
+ }
+ QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setInt_32(int i)
+ {
+ setTagValue(quint32(ValueTypeInternal::Integer), quint32(i));
+ }
+ QV4_NEARLY_ALWAYS_INLINE uint uint_32() const { return value(); }
+
+ QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setEmpty()
+ {
+ setTagValue(quint32(ValueTypeInternal::Empty), 0);
+ }
+
+ // ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible
+ // and use negative numbers here
+ enum QuickType {
+ QT_ManagedOrUndefined = 0,
+ QT_ManagedOrUndefined1 = 1,
+ QT_ManagedOrUndefined2 = 2,
+ QT_ManagedOrUndefined3 = 3,
+ QT_Empty = 4,
+ QT_Null = 5,
+ QT_Bool = 6,
+ QT_Int = 7
+ // all other values are doubles
+ };
+
+ enum Type {
+ Undefined_Type = 0,
+ Managed_Type = 1,
+ Empty_Type = 4,
+ Null_Type = 5,
+ Boolean_Type = 6,
+ Integer_Type = 7,
+ Double_Type = 8
+ };
+
+ inline Type type() const {
+ int t = quickType();
+ if (t < QT_Empty)
+ return _val ? Managed_Type : Undefined_Type;
+ if (t > QT_Int)
+ return Double_Type;
+ return static_cast<Type>(t);
+ }
+
+ // Shared between 32-bit and 64-bit encoding
+ enum {
+ Tag_Shift = 32
+ };
+
+ // Used only by 64-bit encoding
+ static const quint64 NaNEncodeMask = 0xfffc000000000000ull;
+ enum {
+ IsDouble_Shift = 64-14,
+ IsManagedOrUndefined_Shift = 64-15,
+ IsIntegerConvertible_Shift = 64-15,
+ IsIntegerOrBool_Shift = 64-16,
+ QuickType_Shift = 64 - 17,
+ IsPositiveIntShift = 31
+ };
+
+ static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49
+
+ enum class ValueTypeInternal_64 {
+ Empty = Immediate_Mask_64 | 0,
+ Null = Immediate_Mask_64 | 0x08000u,
+ Boolean = Immediate_Mask_64 | 0x10000u,
+ Integer = Immediate_Mask_64 | 0x18000u
+ };
+
+ // Used only by 32-bit encoding
+ enum Masks {
+ SilentNaNBit = 0x00040000,
+ NotDouble_Mask = 0x7ffa0000,
+ };
+ static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit;
+
+ enum class ValueTypeInternal_32 {
+ Empty = Immediate_Mask_32 | 0,
+ Null = Immediate_Mask_32 | 0x08000u,
+ Boolean = Immediate_Mask_32 | 0x10000u,
+ Integer = Immediate_Mask_32 | 0x18000u
+ };
+
+ enum {
+ Managed_Type_Internal = 0
+ };
+
+ using ValueTypeInternal = ValueTypeInternal_64;
+
+ enum {
+ NaN_Mask = 0x7ff80000,
+ };
+
+ inline quint64 quickType() const { return (_val >> QuickType_Shift); }
+
+ // used internally in property
+ inline bool isEmpty() const { return tag() == quint32(ValueTypeInternal::Empty); }
+ inline bool isNull() const { return tag() == quint32(ValueTypeInternal::Null); }
+ inline bool isBoolean() const { return tag() == quint32(ValueTypeInternal::Boolean); }
+ inline bool isInteger() const { return tag() == quint32(ValueTypeInternal::Integer); }
+ inline bool isNullOrUndefined() const { return isNull() || isUndefined(); }
+ inline bool isNumber() const { return quickType() >= QT_Int; }
+
+ inline bool isUndefined() const { return _val == 0; }
+ inline bool isDouble() const { return (_val >> IsDouble_Shift); }
+ inline bool isManaged() const
+ {
+#if QT_POINTER_SIZE == 4
+ return value() && tag() == Managed_Type_Internal;
+#else
+ return _val && ((_val >> IsManagedOrUndefined_Shift) == 0);
+#endif
+ }
+ inline bool isManagedOrUndefined() const
+ {
+#if QT_POINTER_SIZE == 4
+ return tag() == Managed_Type_Internal;
+#else
+ return ((_val >> IsManagedOrUndefined_Shift) == 0);
+#endif
+ }
+
+ inline bool isIntOrBool() const {
+ return (_val >> IsIntegerOrBool_Shift) == 3;
+ }
+
+ inline bool integerCompatible() const {
+ Q_ASSERT(!isEmpty());
+ return (_val >> IsIntegerConvertible_Shift) == 1;
+ }
+
+ static inline bool integerCompatible(StaticValue a, StaticValue b) {
+ return a.integerCompatible() && b.integerCompatible();
+ }
+
+ static inline bool bothDouble(StaticValue a, StaticValue b) {
+ return a.isDouble() && b.isDouble();
+ }
+
+ inline bool isNaN() const
+ {
+ return (tag() & 0x7ffc0000 ) == 0x00040000;
+ }
+
+ inline bool isPositiveInt() const {
+#if QT_POINTER_SIZE == 4
+ return isInteger() && int_32() >= 0;
+#else
+ return (_val >> IsPositiveIntShift) == (quint64(ValueTypeInternal::Integer) << 1);
+#endif
+ }
+
+ QV4_NEARLY_ALWAYS_INLINE double doubleValue() const {
+ Q_ASSERT(isDouble());
+ double d;
+ StaticValue v = *this;
+ v._val ^= NaNEncodeMask;
+ memcpy(&d, &v._val, 8);
+ return d;
+ }
+
+ QV4_NEARLY_ALWAYS_INLINE void setDouble(double d) {
+ if (qt_is_nan(d))
+ d = qt_qnan();
+ memcpy(&_val, &d, 8);
+ _val ^= NaNEncodeMask;
+ Q_ASSERT(isDouble());
+ }
+
+ inline bool isInt32() {
+ if (tag() == quint32(ValueTypeInternal::Integer))
+ return true;
+ if (isDouble()) {
+ double d = doubleValue();
+ if (isInt32(d)) {
+ setInt_32(int(d));
+ return true;
+ }
+ }
+ return false;
+ }
+
+ QV4_NEARLY_ALWAYS_INLINE static bool isInt32(double d) {
+ int i = int(d);
+ return (i == d && !(d == 0 && std::signbit(d)));
+ }
+
+ double asDouble() const {
+ if (tag() == quint32(ValueTypeInternal::Integer))
+ return int_32();
+ return doubleValue();
+ }
+
+ bool booleanValue() const {
+ return int_32();
+ }
+
+ int integerValue() const {
+ return int_32();
+ }
+
+ inline bool tryIntegerConversion() {
+ bool b = integerCompatible();
+ if (b)
+ setTagValue(quint32(ValueTypeInternal::Integer), value());
+ return b;
+ }
+
+ bool toBoolean() const {
+ if (integerCompatible())
+ return static_cast<bool>(int_32());
+
+ if (isManagedOrUndefined())
+ return false;
+
+ // double
+ const double d = doubleValue();
+ return d && !std::isnan(d);
+ }
+
+ inline int toInt32() const
+ {
+ switch (type()) {
+ case Null_Type:
+ case Boolean_Type:
+ case Integer_Type:
+ return int_32();
+ case Double_Type:
+ return Double::toInt32(doubleValue());
+ case Empty_Type:
+ case Undefined_Type:
+ case Managed_Type:
+ break;
+ }
+ return Double::toInt32(std::numeric_limits<double>::quiet_NaN());
+ }
+
+ ReturnedValue *data_ptr() { return &_val; }
+ constexpr ReturnedValue asReturnedValue() const { return _val; }
+ constexpr static StaticValue fromReturnedValue(ReturnedValue val) { return {val}; }
+
+ inline static constexpr StaticValue emptyValue() { return { tagValue(quint32(ValueTypeInternal::Empty), 0) }; }
+ static inline constexpr StaticValue fromBoolean(bool b) { return { tagValue(quint32(ValueTypeInternal::Boolean), b) }; }
+ static inline constexpr StaticValue fromInt32(int i) { return { tagValue(quint32(ValueTypeInternal::Integer), quint32(i)) }; }
+ inline static constexpr StaticValue undefinedValue() { return { 0 }; }
+ static inline constexpr StaticValue nullValue() { return { tagValue(quint32(ValueTypeInternal::Null), 0) }; }
+
+ static inline StaticValue fromDouble(double d)
+ {
+ StaticValue v;
+ v.setDouble(d);
+ return v;
+ }
+
+ static inline StaticValue fromUInt32(uint i)
+ {
+ StaticValue v;
+ if (i < uint(std::numeric_limits<int>::max())) {
+ v.setTagValue(quint32(ValueTypeInternal::Integer), i);
+ } else {
+ v.setDouble(i);
+ }
+ return v;
+ }
+
+ static double toInteger(double d)
+ {
+ if (std::isnan(d))
+ return +0;
+ if (!d || std::isinf(d))
+ return d;
+ return d >= 0 ? std::floor(d) : std::ceil(d);
+ }
+
+ static int toInt32(double d)
+ {
+ return Double::toInt32(d);
+ }
+
+ static unsigned int toUInt32(double d)
+ {
+ return static_cast<uint>(toInt32(d));
+ }
+};
+Q_STATIC_ASSERT(std::is_trivial<StaticValue>::value);
+
+struct Encode {
+ static constexpr ReturnedValue undefined() {
+ return StaticValue::undefinedValue().asReturnedValue();
+ }
+ static constexpr ReturnedValue null() {
+ return StaticValue::nullValue().asReturnedValue();
+ }
+
+ explicit constexpr Encode(bool b)
+ : val(StaticValue::fromBoolean(b).asReturnedValue())
+ {
+ }
+ explicit Encode(double d) {
+ val = StaticValue::fromDouble(d).asReturnedValue();
+ }
+ explicit constexpr Encode(int i)
+ : val(StaticValue::fromInt32(i).asReturnedValue())
+ {
+ }
+ explicit Encode(uint i) {
+ val = StaticValue::fromUInt32(i).asReturnedValue();
+ }
+ explicit constexpr Encode(ReturnedValue v)
+ : val(v)
+ {
+ }
+ constexpr Encode(StaticValue v)
+ : val(v.asReturnedValue())
+ {
+ }
+
+ template<typename HeapBase>
+ explicit Encode(HeapBase *o);
+
+ explicit Encode(StaticValue *o) {
+ Q_ASSERT(o);
+ val = o->asReturnedValue();
+ }
+
+ static ReturnedValue smallestNumber(double d) {
+ if (StaticValue::isInt32(d))
+ return Encode(static_cast<int>(d));
+ else
+ return Encode(d);
+ }
+
+ constexpr operator ReturnedValue() const {
+ return val;
+ }
+ quint64 val;
+private:
+ explicit Encode(void *);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4STATICVALUE_P_H
diff --git a/src/qml/common/qv4stringtoarrayindex_p.h b/src/qml/common/qv4stringtoarrayindex_p.h
new file mode 100644
index 0000000000..61bd988d1e
--- /dev/null
+++ b/src/qml/common/qv4stringtoarrayindex_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4STRINGTOARRAYINDEX_P_H
+#define QV4STRINGTOARRAYINDEX_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/private/qnumeric_p.h>
+#include <QtCore/qstring.h>
+#include <limits>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+inline uint charToUInt(const QChar *ch) { return ch->unicode(); }
+inline uint charToUInt(const char *ch) { return static_cast<unsigned char>(*ch); }
+
+template <typename T>
+uint stringToArrayIndex(const T *ch, const T *end)
+{
+ uint i = charToUInt(ch) - '0';
+ if (i > 9)
+ return std::numeric_limits<uint>::max();
+ ++ch;
+ // reject "01", "001", ...
+ if (i == 0 && ch != end)
+ return std::numeric_limits<uint>::max();
+
+ while (ch < end) {
+ uint x = charToUInt(ch) - '0';
+ if (x > 9)
+ return std::numeric_limits<uint>::max();
+ if (mul_overflow(i, uint(10), &i) || add_overflow(i, x, &i)) // i = i * 10 + x
+ return std::numeric_limits<uint>::max();
+ ++ch;
+ }
+ return i;
+}
+
+inline uint stringToArrayIndex(const QString &str)
+{
+ return stringToArrayIndex(str.constData(), str.constData() + str.length());
+}
+
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4STRINGTOARRAYINDEX_P_H
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri
index da3c173545..4d6926d420 100644
--- a/src/qml/compiler/compiler.pri
+++ b/src/qml/compiler/compiler.pri
@@ -3,20 +3,19 @@ INCLUDEPATH += $$OUT_PWD
HEADERS += \
$$PWD/qv4bytecodegenerator_p.h \
- $$PWD/qv4compileddata_p.h \
$$PWD/qv4compiler_p.h \
$$PWD/qv4compilercontext_p.h \
$$PWD/qv4compilercontrolflow_p.h \
+ $$PWD/qv4compilerglobal_p.h \
$$PWD/qv4compilerscanfunctions_p.h \
$$PWD/qv4codegen_p.h \
$$PWD/qqmlirbuilder_p.h \
- $$PWD/qqmltypecompiler_p.h \
$$PWD/qv4instr_moth_p.h \
- $$PWD/qv4bytecodehandler_p.h
+ $$PWD/qv4bytecodehandler_p.h \
+ $$PWD/qv4util_p.h
SOURCES += \
$$PWD/qv4bytecodegenerator.cpp \
- $$PWD/qv4compileddata.cpp \
$$PWD/qv4compiler.cpp \
$$PWD/qv4compilercontext.cpp \
$$PWD/qv4compilerscanfunctions.cpp \
@@ -25,25 +24,6 @@ SOURCES += \
$$PWD/qv4instr_moth.cpp \
$$PWD/qv4bytecodehandler.cpp
-!qmldevtools_build {
-
-HEADERS += \
- $$PWD/qqmltypecompiler_p.h \
- $$PWD/qqmlpropertycachecreator_p.h \
- $$PWD/qqmlpropertyvalidator_p.h \
- $$PWD/qv4compilationunitmapper_p.h
-
-
-SOURCES += \
- $$PWD/qqmltypecompiler.cpp \
- $$PWD/qqmlpropertycachecreator.cpp \
- $$PWD/qqmlpropertyvalidator.cpp \
- $$PWD/qv4compilationunitmapper.cpp
-
-unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp
-else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp
-}
-
gcc {
equals(QT_GCC_MAJOR_VERSION, 5): QMAKE_CXXFLAGS += -fno-strict-aliasing
}
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index 9218c4a652..665d2633a7 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -39,7 +39,7 @@
#include "qqmlirbuilder_p.h"
-#include <private/qv4value_p.h>
+#include <private/qv4staticvalue_p.h>
#include <private/qv4compileddata_p.h>
#include <private/qqmljsparser_p.h>
#include <private/qqmljslexer_p.h>
@@ -48,12 +48,6 @@
#include <QCryptographicHash>
#include <cmath>
-#ifndef V4_BOOTSTRAP
-#include <private/qqmlglobal_p.h>
-#include <private/qqmltypeloader_p.h>
-#include <private/qqmlengine_p.h>
-#endif
-
#ifdef CONST
#undef CONST
#endif
@@ -61,12 +55,8 @@
QT_USE_NAMESPACE
static const quint32 emptyStringIndex = 0;
-
-#if 0 //ndef V4_BOOTSTRAP
-DEFINE_BOOL_CONFIG_OPTION(lookupHints, QML_LOOKUP_HINTS);
-#endif // V4_BOOTSTRAP
-
using namespace QmlIR;
+using namespace QQmlJS;
#define COMPILE_EXCEPTION(location, desc) \
{ \
@@ -74,6 +64,84 @@ using namespace QmlIR;
return false; \
}
+bool Parameter::init(QV4::Compiler::JSUnitGenerator *stringGenerator, const QString &parameterName,
+ const QString &typeName)
+{
+ return init(this, stringGenerator, stringGenerator->registerString(parameterName), stringGenerator->registerString(typeName));
+}
+
+bool Parameter::init(QV4::CompiledData::Parameter *param, const QV4::Compiler::JSUnitGenerator *stringGenerator,
+ int parameterNameIndex, int typeNameIndex)
+{
+ param->nameIndex = parameterNameIndex;
+ return initType(&param->type, stringGenerator, typeNameIndex);
+}
+
+bool Parameter::initType(QV4::CompiledData::ParameterType *paramType, const QV4::Compiler::JSUnitGenerator *stringGenerator, int typeNameIndex)
+{
+ paramType->indexIsBuiltinType = false;
+ paramType->typeNameIndexOrBuiltinType = 0;
+ const QString typeName = stringGenerator->stringForIndex(typeNameIndex);
+ auto builtinType = stringToBuiltinType(typeName);
+ if (builtinType == QV4::CompiledData::BuiltinType::InvalidBuiltin) {
+ if (typeName.isEmpty() || !typeName.at(0).isUpper())
+ return false;
+ paramType->indexIsBuiltinType = false;
+ paramType->typeNameIndexOrBuiltinType = typeNameIndex;
+ Q_ASSERT(quint32(typeNameIndex) < (1u << 31));
+ } else {
+ paramType->indexIsBuiltinType = true;
+ paramType->typeNameIndexOrBuiltinType = static_cast<quint32>(builtinType);
+ Q_ASSERT(quint32(builtinType) < (1u << 31));
+ }
+ return true;
+}
+
+QV4::CompiledData::BuiltinType Parameter::stringToBuiltinType(const QString &typeName)
+{
+ static const struct TypeNameToType {
+ const char *name;
+ size_t nameLength;
+ QV4::CompiledData::BuiltinType type;
+ } propTypeNameToTypes[] = {
+ { "int", strlen("int"), QV4::CompiledData::BuiltinType::Int },
+ { "bool", strlen("bool"), QV4::CompiledData::BuiltinType::Bool },
+ { "double", strlen("double"), QV4::CompiledData::BuiltinType::Real },
+ { "real", strlen("real"), QV4::CompiledData::BuiltinType::Real },
+ { "string", strlen("string"), QV4::CompiledData::BuiltinType::String },
+ { "url", strlen("url"), QV4::CompiledData::BuiltinType::Url },
+ { "color", strlen("color"), QV4::CompiledData::BuiltinType::Color },
+ // Internally QTime, QDate and QDateTime are all supported.
+ // To be more consistent with JavaScript we expose only
+ // QDateTime as it matches closely with the Date JS type.
+ // We also call it "date" to match.
+ // { "time", strlen("time"), Property::Time },
+ // { "date", strlen("date"), Property::Date },
+ { "date", strlen("date"), QV4::CompiledData::BuiltinType::DateTime },
+ { "rect", strlen("rect"), QV4::CompiledData::BuiltinType::Rect },
+ { "point", strlen("point"), QV4::CompiledData::BuiltinType::Point },
+ { "size", strlen("size"), QV4::CompiledData::BuiltinType::Size },
+ { "font", strlen("font"), QV4::CompiledData::BuiltinType::Font },
+ { "vector2d", strlen("vector2d"), QV4::CompiledData::BuiltinType::Vector2D },
+ { "vector3d", strlen("vector3d"), QV4::CompiledData::BuiltinType::Vector3D },
+ { "vector4d", strlen("vector4d"), QV4::CompiledData::BuiltinType::Vector4D },
+ { "quaternion", strlen("quaternion"), QV4::CompiledData::BuiltinType::Quaternion },
+ { "matrix4x4", strlen("matrix4x4"), QV4::CompiledData::BuiltinType::Matrix4x4 },
+ { "variant", strlen("variant"), QV4::CompiledData::BuiltinType::Variant },
+ { "var", strlen("var"), QV4::CompiledData::BuiltinType::Var }
+ };
+ static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) /
+ sizeof(propTypeNameToTypes[0]);
+
+ for (int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) {
+ const TypeNameToType *t = propTypeNameToTypes + typeIndex;
+ if (typeName == QLatin1String(t->name, static_cast<int>(t->nameLength))) {
+ return t->type;
+ }
+ }
+ return QV4::CompiledData::BuiltinType::InvalidBuiltin;
+}
+
void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QQmlJS::AST::SourceLocation &loc)
{
inheritedTypeNameIndex = typeNameIndex;
@@ -258,64 +326,11 @@ QStringList Signal::parameterStringList(const QV4::Compiler::StringTableGenerato
{
QStringList result;
result.reserve(parameters->count);
- for (SignalParameter *param = parameters->first; param; param = param->next)
+ for (Parameter *param = parameters->first; param; param = param->next)
result << stringPool->stringForIndex(param->nameIndex);
return result;
}
-static void replaceWithSpace(QString &str, int idx, int n)
-{
- QChar *data = str.data() + idx;
- const QChar space(QLatin1Char(' '));
- for (int ii = 0; ii < n; ++ii)
- *data++ = space;
-}
-
-void Document::removeScriptPragmas(QString &script)
-{
- const QLatin1String pragma("pragma");
- const QLatin1String library("library");
-
- QQmlJS::Lexer l(nullptr);
- l.setCode(script, 0);
-
- int token = l.lex();
-
- while (true) {
- if (token != QQmlJSGrammar::T_DOT)
- return;
-
- int startOffset = l.tokenOffset();
- int startLine = l.tokenStartLine();
-
- token = l.lex();
-
- if (token != QQmlJSGrammar::T_PRAGMA ||
- l.tokenStartLine() != startLine ||
- script.midRef(l.tokenOffset(), l.tokenLength()) != pragma)
- return;
-
- token = l.lex();
-
- if (token != QQmlJSGrammar::T_IDENTIFIER ||
- l.tokenStartLine() != startLine)
- return;
-
- const QStringRef pragmaValue = script.midRef(l.tokenOffset(), l.tokenLength());
- int endOffset = l.tokenLength() + l.tokenOffset();
-
- token = l.lex();
- if (l.tokenStartLine() == startLine)
- return;
-
- if (pragmaValue == library) {
- replaceWithSpace(script, startOffset, endOffset - startOffset);
- } else {
- return;
- }
- }
-}
-
Document::Document(bool debugMode)
: jsModule(debugMode)
, program(nullptr)
@@ -385,13 +400,12 @@ bool IRBuilder::generateFromQml(const QString &code, const QString &url, Documen
if (!parseResult || !diagnosticMessages.isEmpty()) {
// Extract errors from the parser
for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
-
if (m.isWarning()) {
- qWarning("%s:%d : %s", qPrintable(url), m.loc.startLine, qPrintable(m.message));
+ qWarning("%s:%d : %s", qPrintable(url), m.line, qPrintable(m.message));
continue;
}
- recordError(m.loc, m.message);
+ errors << m;
}
return false;
}
@@ -661,11 +675,9 @@ bool IRBuilder::visit(QQmlJS::AST::UiImport *node)
return false;
}
- if (node->versionToken.isValid()) {
- int major, minor;
- extractVersion(textRefAt(node->versionToken), &major, &minor);
- import->majorVersion = major;
- import->minorVersion = minor;
+ if (node->version) {
+ import->majorVersion = node->version->majorVersion;
+ import->minorVersion = node->version->minorVersion;
} else if (import->type == QV4::CompiledData::Import::ImportLibrary) {
recordError(node->importIdToken, QCoreApplication::translate("QQmlParser","Library import requires a version"));
return false;
@@ -778,40 +790,6 @@ bool IRBuilder::visit(QQmlJS::AST::UiEnumDeclaration *node)
bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
{
- static const struct TypeNameToType {
- const char *name;
- size_t nameLength;
- QV4::CompiledData::Property::Type type;
- } propTypeNameToTypes[] = {
- { "int", strlen("int"), QV4::CompiledData::Property::Int },
- { "bool", strlen("bool"), QV4::CompiledData::Property::Bool },
- { "double", strlen("double"), QV4::CompiledData::Property::Real },
- { "real", strlen("real"), QV4::CompiledData::Property::Real },
- { "string", strlen("string"), QV4::CompiledData::Property::String },
- { "url", strlen("url"), QV4::CompiledData::Property::Url },
- { "color", strlen("color"), QV4::CompiledData::Property::Color },
- // Internally QTime, QDate and QDateTime are all supported.
- // To be more consistent with JavaScript we expose only
- // QDateTime as it matches closely with the Date JS type.
- // We also call it "date" to match.
- // { "time", strlen("time"), Property::Time },
- // { "date", strlen("date"), Property::Date },
- { "date", strlen("date"), QV4::CompiledData::Property::DateTime },
- { "rect", strlen("rect"), QV4::CompiledData::Property::Rect },
- { "point", strlen("point"), QV4::CompiledData::Property::Point },
- { "size", strlen("size"), QV4::CompiledData::Property::Size },
- { "font", strlen("font"), QV4::CompiledData::Property::Font },
- { "vector2d", strlen("vector2d"), QV4::CompiledData::Property::Vector2D },
- { "vector3d", strlen("vector3d"), QV4::CompiledData::Property::Vector3D },
- { "vector4d", strlen("vector4d"), QV4::CompiledData::Property::Vector4D },
- { "quaternion", strlen("quaternion"), QV4::CompiledData::Property::Quaternion },
- { "matrix4x4", strlen("matrix4x4"), QV4::CompiledData::Property::Matrix4x4 },
- { "variant", strlen("variant"), QV4::CompiledData::Property::Variant },
- { "var", strlen("var"), QV4::CompiledData::Property::Var }
- };
- static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) /
- sizeof(propTypeNameToTypes[0]);
-
if (node->type == QQmlJS::AST::UiPublicMember::Signal) {
Signal *signal = New<Signal>();
QString signalName = node->name.toString();
@@ -821,7 +799,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
signal->location.line = loc.startLine;
signal->location.column = loc.startColumn;
- signal->parameters = New<PoolList<SignalParameter> >();
+ signal->parameters = New<PoolList<Parameter> >();
QQmlJS::AST::UiParameterList *p = node->parameters;
while (p) {
@@ -832,38 +810,13 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
return false;
}
- const TypeNameToType *type = nullptr;
- for (int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) {
- const TypeNameToType *t = propTypeNameToTypes + typeIndex;
- if (memberType == QLatin1String(t->name, static_cast<int>(t->nameLength))) {
- type = t;
- break;
- }
- }
-
- SignalParameter *param = New<SignalParameter>();
-
- if (!type) {
- if (memberType.at(0).isUpper()) {
- // Must be a QML object type.
- // Lazily determine type during compilation.
- param->type = QV4::CompiledData::Property::Custom;
- param->customTypeNameIndex = registerString(memberType);
- } else {
- QString errStr = QCoreApplication::translate("QQmlParser","Invalid signal parameter type: ");
- errStr.append(memberType);
- recordError(node->typeToken, errStr);
- return false;
- }
- } else {
- // the parameter is a known basic type
- param->type = type->type;
- param->customTypeNameIndex = emptyStringIndex;
+ Parameter *param = New<Parameter>();
+ if (!param->init(jsGenerator, p->name.toString(), memberType)) {
+ QString errStr = QCoreApplication::translate("QQmlParser","Invalid signal parameter type: ");
+ errStr.append(memberType);
+ recordError(node->typeToken, errStr);
+ return false;
}
-
- param->nameIndex = registerString(p->name.toString());
- param->location.line = p->identifierToken.startLine;
- param->location.column = p->identifierToken.startColumn;
signal->parameters->append(param);
p = p->next;
}
@@ -886,25 +839,21 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
} else {
const QStringRef &name = node->name;
- bool typeFound = false;
- QV4::CompiledData::Property::Type type = QV4::CompiledData::Property::Var;
+ Property *property = New<Property>();
+ property->isReadOnly = node->isReadonlyMember;
- for (int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) {
- const TypeNameToType *t = propTypeNameToTypes + ii;
- if (memberType == QLatin1String(t->name, static_cast<int>(t->nameLength))) {
- type = t->type;
- typeFound = true;
- }
- }
+ QV4::CompiledData::BuiltinType builtinPropertyType = Parameter::stringToBuiltinType(memberType);
+ bool typeFound = builtinPropertyType != QV4::CompiledData::BuiltinType::InvalidBuiltin;
+ if (typeFound)
+ property->setBuiltinType(builtinPropertyType);
if (!typeFound && memberType.at(0).isUpper()) {
const QStringRef &typeModifier = node->typeModifier;
- if (typeModifier.isEmpty()) {
- type = QV4::CompiledData::Property::Custom;
- } else if (typeModifier == QLatin1String("list")) {
- type = QV4::CompiledData::Property::CustomList;
- } else {
+ property->setCustomType(registerString(memberType));
+ if (typeModifier == QLatin1String("list")) {
+ property->isList = true;
+ } else if (!typeModifier.isEmpty()) {
recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Invalid property type modifier"));
return false;
}
@@ -919,16 +868,6 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
return false;
}
- Property *property = New<Property>();
- property->flags = 0;
- if (node->isReadonlyMember)
- property->flags |= QV4::CompiledData::Property::IsReadOnly;
- property->type = type;
- if (type >= QV4::CompiledData::Property::Custom)
- property->customTypeNameIndex = registerString(memberType);
- else
- property->customTypeNameIndex = emptyStringIndex;
-
const QString propName = name.toString();
property->nameIndex = registerString(propName);
@@ -969,7 +908,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node)
{
- if (QQmlJS::AST::FunctionDeclaration *funDecl = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration *>(node->sourceElement)) {
+ if (QQmlJS::AST::FunctionExpression *funDecl = node->sourceElement->asFunctionDefinition()) {
CompiledFunctionOrExpression *foe = New<CompiledFunctionOrExpression>();
foe->node = funDecl;
foe->parentNode = funDecl;
@@ -983,13 +922,16 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node)
f->index = index;
f->nameIndex = registerString(funDecl->name.toString());
- const QStringList formals = funDecl->formals ? funDecl->formals->formals() : QStringList();
+ QString returnTypeName = funDecl->typeAnnotation ? funDecl->typeAnnotation->type->toString() : QString();
+ Parameter::initType(&f->returnType, jsGenerator, registerString(returnTypeName));
+
+ const QQmlJS::AST::BoundNames formals = funDecl->formals ? funDecl->formals->formals() : QQmlJS::AST::BoundNames();
int formalsCount = formals.size();
f->formals.allocate(pool, formalsCount);
int i = 0;
- for (const QString &arg : formals) {
- f->formals[i] = registerString(arg);
+ for (const auto &arg : formals) {
+ f->formals[i].init(jsGenerator, arg.id, arg.typeName());
++i;
}
@@ -1022,7 +964,7 @@ QStringRef IRBuilder::asStringRef(QQmlJS::AST::Node *node)
return textRefAt(node->firstSourceLocation(), node->lastSourceLocation());
}
-void IRBuilder::extractVersion(QStringRef string, int *maj, int *min)
+void IRBuilder::extractVersion(const QStringRef &string, int *maj, int *min)
{
*maj = -1; *min = -1;
@@ -1051,7 +993,7 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST
binding->valueLocation.line = loc.startLine;
binding->valueLocation.column = loc.startColumn;
binding->type = QV4::CompiledData::Binding::Type_Invalid;
- if (_propertyDeclaration && (_propertyDeclaration->flags & QV4::CompiledData::Property::IsReadOnly))
+ if (_propertyDeclaration && _propertyDeclaration->isReadOnly)
binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration;
QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement);
@@ -1282,7 +1224,7 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo
binding->flags = 0;
- if (_propertyDeclaration && (_propertyDeclaration->flags & QV4::CompiledData::Property::IsReadOnly))
+ if (_propertyDeclaration && _propertyDeclaration->isReadOnly)
binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration;
// No type name on the initializer means it must be a group property
@@ -1513,7 +1455,8 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O
void IRBuilder::recordError(const QQmlJS::AST::SourceLocation &location, const QString &description)
{
QQmlJS::DiagnosticMessage error;
- error.loc = location;
+ error.line = location.startLine;
+ error.column = location.startColumn;
error.message = description;
errors << error;
}
@@ -1545,7 +1488,7 @@ bool IRBuilder::isStatementNodeScript(QQmlJS::AST::Statement *statement)
bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement)
{
- if (property->type != QV4::CompiledData::Property::Custom)
+ if (property->isBuiltinType || property->isList)
return false;
QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement);
if (!exprStmt)
@@ -1559,17 +1502,12 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen
output.jsGenerator.stringTable.registerString(output.jsModule.fileName);
output.jsGenerator.stringTable.registerString(output.jsModule.finalUrl);
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = output.javaScriptCompilationUnit;
-
QV4::CompiledData::Unit *jsUnit = nullptr;
- const bool finalize = !compilationUnit->data;
// We may already have unit data if we're loading an ahead-of-time generated cache file.
- if (!finalize) {
- jsUnit = const_cast<QV4::CompiledData::Unit *>(compilationUnit->data);
-#ifndef V4_BOOTSTRAP
- output.javaScriptCompilationUnit->dynamicStrings = output.jsGenerator.stringTable.allStrings();
-#endif
+ if (output.javaScriptCompilationUnit.data) {
+ jsUnit = const_cast<QV4::CompiledData::Unit *>(output.javaScriptCompilationUnit.data);
+ output.javaScriptCompilationUnit.dynamicStrings = output.jsGenerator.stringTable.allStrings();
} else {
QV4::CompiledData::Unit *createdUnit;
jsUnit = createdUnit = output.jsGenerator.generateUnit();
@@ -1581,22 +1519,15 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen
break;
}
}
- // This unit's memory was allocated with malloc on the heap, so it's
- // definitely not suitable for StaticData access.
- createdUnit->flags &= ~QV4::CompiledData::Unit::StaticData;
-#ifndef V4_BOOTSTRAP
if (dependencyHasher) {
- QCryptographicHash hash(QCryptographicHash::Md5);
- if (dependencyHasher(&hash)) {
- QByteArray checksum = hash.result();
- Q_ASSERT(checksum.size() == sizeof(createdUnit->dependencyMD5Checksum));
- memcpy(createdUnit->dependencyMD5Checksum, checksum.constData(), sizeof(createdUnit->dependencyMD5Checksum));
+ const QByteArray checksum = dependencyHasher();
+ if (checksum.size() == sizeof(createdUnit->dependencyMD5Checksum)) {
+ memcpy(createdUnit->dependencyMD5Checksum, checksum.constData(),
+ sizeof(createdUnit->dependencyMD5Checksum));
}
}
-#else
- Q_UNUSED(dependencyHasher);
-#endif
+
createdUnit->sourceFileIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.fileName);
createdUnit->finalUrlIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.finalUrl);
}
@@ -1730,7 +1661,7 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen
signalToWrite->nParameters = s->parameters->count;
QV4::CompiledData::Parameter *parameterToWrite = reinterpret_cast<QV4::CompiledData::Parameter*>(signalPtr + sizeof(*signalToWrite));
- for (SignalParameter *param = s->parameters->first; param; param = param->next, ++parameterToWrite)
+ for (Parameter *param = s->parameters->first; param; param = param->next, ++parameterToWrite)
*parameterToWrite = *param;
int size = QV4::CompiledData::Signal::calculateSize(s->parameters->count);
@@ -1765,14 +1696,14 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen
}
}
- if (finalize) {
+ if (!output.javaScriptCompilationUnit.data) {
// Combine the qml data into the general unit data.
jsUnit = static_cast<QV4::CompiledData::Unit *>(realloc(jsUnit, jsUnit->unitSize + totalSize));
jsUnit->offsetToQmlUnit = jsUnit->unitSize;
jsUnit->unitSize += totalSize;
memcpy(jsUnit->qmlUnit(), qmlUnit, totalSize);
free(qmlUnit);
- jsUnit->generateChecksum();
+ QV4::Compiler::JSUnitGenerator::generateUnitChecksum(jsUnit);
qmlUnit = jsUnit->qmlUnit();
}
@@ -1798,7 +1729,8 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen
qDebug() << " " << totalStringSize << "bytes total strings";
}
- compilationUnit->setUnitData(jsUnit, qmlUnit, output.jsModule.fileName, output.jsModule.finalUrl);
+ output.javaScriptCompilationUnit.setUnitData(jsUnit, qmlUnit, output.jsModule.fileName,
+ output.jsModule.finalUrl);
}
char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const
@@ -1815,19 +1747,11 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding
return bindingPtr;
}
-JSCodeGen::JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
- QV4::Compiler::Module *jsModule, QQmlJS::Engine *jsEngine,
- QQmlJS::AST::UiProgram *qmlRoot,
- const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames)
- : QV4::Compiler::Codegen(jsUnitGenerator, /*strict mode*/false)
- , sourceCode(sourceCode)
- , jsEngine(jsEngine)
- , qmlRoot(qmlRoot)
- , stringPool(stringPool)
+JSCodeGen::JSCodeGen(Document *document, const QSet<QString> &globalNames)
+ : QV4::Compiler::Codegen(&document->jsGenerator, /*strict mode*/false), document(document)
{
m_globalNames = globalNames;
-
- _module = jsModule;
+ _module = &document->jsModule;
_fileNameIsUrl = true;
}
@@ -1835,18 +1759,18 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
{
auto qmlName = [&](const CompiledFunctionOrExpression &c) {
if (c.nameIndex != 0)
- return stringPool->stringForIndex(c.nameIndex);
+ return document->stringAt(c.nameIndex);
else
return QStringLiteral("%qml-expression-entry");
};
QVector<int> runtimeFunctionIndices(functions.size());
- QV4::Compiler::ScanFunctions scan(this, sourceCode, QV4::Compiler::ContextType::Global);
+ QV4::Compiler::ScanFunctions scan(this, document->code, QV4::Compiler::ContextType::Global);
scan.enterGlobalEnvironment(QV4::Compiler::ContextType::Binding);
for (const CompiledFunctionOrExpression &f : functions) {
- Q_ASSERT(f.node != qmlRoot);
- Q_ASSERT(f.parentNode && f.parentNode != qmlRoot);
- QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(f.node);
+ Q_ASSERT(f.node != document->program);
+ Q_ASSERT(f.parentNode && f.parentNode != document->program);
+ auto function = f.node->asFunctionDefinition();
if (function) {
scan.enterQmlFunction(function);
@@ -1860,7 +1784,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
}
scan.leaveEnvironment();
- if (hasError)
+ if (hasError())
return QVector<int>();
_context = nullptr;
@@ -1868,9 +1792,9 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
for (int i = 0; i < functions.count(); ++i) {
const CompiledFunctionOrExpression &qmlFunction = functions.at(i);
QQmlJS::AST::Node *node = qmlFunction.node;
- Q_ASSERT(node != qmlRoot);
+ Q_ASSERT(node != document->program);
- QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(node);
+ QQmlJS::AST::FunctionExpression *function = node->asFunctionDefinition();
QString name;
if (function)
@@ -1883,7 +1807,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
body = function->body;
} else {
// Synthesize source elements.
- QQmlJS::MemoryPool *pool = jsEngine->pool();
+ QQmlJS::MemoryPool *pool = document->jsParserEngine.pool();
QQmlJS::AST::Statement *stmt = node->statementCast();
if (!stmt) {
@@ -1904,213 +1828,57 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
return runtimeFunctionIndices;
}
-#ifndef V4_BOOTSTRAP
-
-QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, RevisionCheck check) const
-{
- if (notInRevision) *notInRevision = false;
-
- QQmlPropertyData *d = cache->property(name, nullptr, nullptr);
-
- // Find the first property
- while (d && d->isFunction())
- d = cache->overrideData(d);
-
- if (check != IgnoreRevision && d && !cache->isAllowedInRevision(d)) {
- if (notInRevision) *notInRevision = true;
- return nullptr;
- } else {
- return d;
- }
-}
-
-
-QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision) const
+bool JSCodeGen::generateCodeForComponents(const QVector<quint32> &componentRoots)
{
- if (notInRevision) *notInRevision = false;
-
- QQmlPropertyData *d = cache->property(name, nullptr, nullptr);
- if (notInRevision) *notInRevision = false;
-
- while (d && !(d->isFunction()))
- d = cache->overrideData(d);
-
- if (d && !cache->isAllowedInRevision(d)) {
- if (notInRevision) *notInRevision = true;
- return nullptr;
- } else if (d && d->isSignal()) {
- return d;
- }
-
- if (name.endsWith(QLatin1String("Changed"))) {
- QString propName = name.mid(0, name.length() - static_cast<int>(strlen("Changed")));
-
- d = property(propName, notInRevision);
- if (d)
- return cache->signal(d->notifyIndex());
+ for (int i = 0; i < componentRoots.count(); ++i) {
+ if (!compileComponent(componentRoots.at(i)))
+ return false;
}
- return nullptr;
-}
-
-IRLoader::IRLoader(const QV4::CompiledData::Unit *qmlData, QmlIR::Document *output)
- : unit(qmlData)
- , output(output)
-{
- pool = output->jsParserEngine.pool();
+ return compileComponent(/*root object*/0);
}
-void IRLoader::load()
+bool JSCodeGen::compileComponent(int contextObject)
{
- output->jsGenerator.stringTable.initializeFromBackingUnit(unit);
-
- const QV4::CompiledData::QmlUnit *qmlUnit = unit->qmlUnit();
-
- for (quint32 i = 0; i < qmlUnit->nImports; ++i)
- output->imports << qmlUnit->importAt(i);
-
- if (unit->flags & QV4::CompiledData::Unit::IsSingleton) {
- QmlIR::Pragma *p = New<QmlIR::Pragma>();
- p->location = QV4::CompiledData::Location();
- p->type = QmlIR::Pragma::PragmaSingleton;
- output->pragmas << p;
+ const QmlIR::Object *obj = document->objects.at(contextObject);
+ if (obj->flags & QV4::CompiledData::Object::IsComponent) {
+ Q_ASSERT(obj->bindingCount() == 1);
+ const QV4::CompiledData::Binding *componentBinding = obj->firstBinding();
+ Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
+ contextObject = componentBinding->value.objectIndex;
}
- for (uint i = 0; i < qmlUnit->nObjects; ++i) {
- const QV4::CompiledData::Object *serializedObject = qmlUnit->objectAt(i);
- QmlIR::Object *object = loadObject(serializedObject);
- output->objects.append(object);
- }
+ return compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject);
}
-struct FakeExpression : public QQmlJS::AST::NullExpression
-{
- FakeExpression(int start, int length)
- : location(start, length)
- {}
-
- virtual QQmlJS::AST::SourceLocation firstSourceLocation() const
- { return location; }
-
- virtual QQmlJS::AST::SourceLocation lastSourceLocation() const
- { return location; }
-
-private:
- QQmlJS::AST::SourceLocation location;
-};
-
-QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedObject)
+bool JSCodeGen::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex)
{
- QmlIR::Object *object = pool->New<QmlIR::Object>();
- object->init(pool, serializedObject->inheritedTypeNameIndex, serializedObject->idNameIndex);
-
- object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias;
- object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias;
- object->flags = serializedObject->flags;
- object->id = serializedObject->id;
- object->location = serializedObject->location;
- object->locationOfIdProperty = serializedObject->locationOfIdProperty;
-
- QVector<int> functionIndices;
- functionIndices.reserve(serializedObject->nFunctions + serializedObject->nBindings / 2);
-
- for (uint i = 0; i < serializedObject->nBindings; ++i) {
- QmlIR::Binding *b = pool->New<QmlIR::Binding>();
- *static_cast<QV4::CompiledData::Binding*>(b) = serializedObject->bindingTable()[i];
- object->bindings->append(b);
- if (b->type == QV4::CompiledData::Binding::Type_Script) {
- functionIndices.append(b->value.compiledScriptIndex);
- b->value.compiledScriptIndex = functionIndices.count() - 1;
-
- QmlIR::CompiledFunctionOrExpression *foe = pool->New<QmlIR::CompiledFunctionOrExpression>();
- foe->nameIndex = 0;
-
- QQmlJS::AST::ExpressionNode *expr;
-
- if (b->stringIndex != quint32(0)) {
- const int start = output->code.length();
- const QString script = output->stringAt(b->stringIndex);
- const int length = script.length();
- output->code.append(script);
- expr = new (pool) FakeExpression(start, length);
- } else
- expr = new (pool) QQmlJS::AST::NullExpression();
- foe->node = new (pool) QQmlJS::AST::ExpressionStatement(expr); // dummy
- object->functionsAndExpressions->append(foe);
- }
- }
-
- Q_ASSERT(object->functionsAndExpressions->count == functionIndices.count());
+ QmlIR::Object *object = document->objects.at(objectIndex);
+ if (object->flags & QV4::CompiledData::Object::IsComponent)
+ return true;
- for (uint i = 0; i < serializedObject->nSignals; ++i) {
- const QV4::CompiledData::Signal *serializedSignal = serializedObject->signalAt(i);
- QmlIR::Signal *s = pool->New<QmlIR::Signal>();
- s->nameIndex = serializedSignal->nameIndex;
- s->location = serializedSignal->location;
- s->parameters = pool->New<QmlIR::PoolList<QmlIR::SignalParameter> >();
-
- for (uint i = 0; i < serializedSignal->nParameters; ++i) {
- QmlIR::SignalParameter *p = pool->New<QmlIR::SignalParameter>();
- *static_cast<QV4::CompiledData::Parameter*>(p) = *serializedSignal->parameterAt(i);
- s->parameters->append(p);
- }
-
- object->qmlSignals->append(s);
- }
-
- for (uint i = 0; i < serializedObject->nEnums; ++i) {
- const QV4::CompiledData::Enum *serializedEnum = serializedObject->enumAt(i);
- QmlIR::Enum *e = pool->New<QmlIR::Enum>();
- e->nameIndex = serializedEnum->nameIndex;
- e->location = serializedEnum->location;
- e->enumValues = pool->New<QmlIR::PoolList<QmlIR::EnumValue> >();
-
- for (uint i = 0; i < serializedEnum->nEnumValues; ++i) {
- QmlIR::EnumValue *v = pool->New<QmlIR::EnumValue>();
- *static_cast<QV4::CompiledData::EnumValue*>(v) = *serializedEnum->enumValueAt(i);
- e->enumValues->append(v);
- }
-
- object->qmlEnums->append(e);
- }
-
- const QV4::CompiledData::Property *serializedProperty = serializedObject->propertyTable();
- for (uint i = 0; i < serializedObject->nProperties; ++i, ++serializedProperty) {
- QmlIR::Property *p = pool->New<QmlIR::Property>();
- *static_cast<QV4::CompiledData::Property*>(p) = *serializedProperty;
- object->properties->append(p);
- }
+ if (object->functionsAndExpressions->count > 0) {
+ QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
+ for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next)
+ functionsToCompile << *foe;
+ const QVector<int> runtimeFunctionIndices = generateJSCodeForFunctionsAndBindings(functionsToCompile);
+ if (hasError())
+ return false;
- {
- const QV4::CompiledData::Alias *serializedAlias = serializedObject->aliasTable();
- for (uint i = 0; i < serializedObject->nAliases; ++i, ++serializedAlias) {
- QmlIR::Alias *a = pool->New<QmlIR::Alias>();
- *static_cast<QV4::CompiledData::Alias*>(a) = *serializedAlias;
- object->aliases->append(a);
- }
+ object->runtimeFunctionIndices.allocate(document->jsParserEngine.pool(),
+ runtimeFunctionIndices);
}
- const quint32_le *functionIdx = serializedObject->functionOffsetTable();
- for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) {
- QmlIR::Function *f = pool->New<QmlIR::Function>();
- const QV4::CompiledData::Function *compiledFunction = unit->functionAt(*functionIdx);
-
- functionIndices.append(*functionIdx);
- f->index = functionIndices.count() - 1;
- f->location = compiledFunction->location;
- f->nameIndex = compiledFunction->nameIndex;
+ for (const QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) {
+ if (binding->type < QV4::CompiledData::Binding::Type_Object)
+ continue;
- f->formals.allocate(pool, int(compiledFunction->nFormals));
- const quint32_le *formalNameIdx = compiledFunction->formalsTable();
- for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx)
- f->formals[i] = *formalNameIdx;
+ int target = binding->value.objectIndex;
+ int scope = binding->type == QV4::CompiledData::Binding::Type_Object ? target : scopeObjectIndex;
- object->functions->append(f);
+ if (!compileJavaScriptCodeInObjectsRecursively(binding->value.objectIndex, scope))
+ return false;
}
- object->runtimeFunctionIndices.allocate(pool, functionIndices);
-
- return object;
+ return true;
}
-
-#endif // V4_BOOTSTRAP
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index 22bc2d2953..4279f5b768 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -55,25 +55,22 @@
#include <private/qv4compiler_p.h>
#include <private/qv4compileddata_p.h>
#include <private/qqmljsmemorypool_p.h>
+#include <private/qqmljsfixedpoolarray_p.h>
#include <private/qv4codegen_p.h>
#include <private/qv4compiler_p.h>
#include <QTextStream>
#include <QCoreApplication>
-#ifndef V4_BOOTSTRAP
-#include <private/qqmlpropertycache_p.h>
-#endif
-
QT_BEGIN_NAMESPACE
class QQmlPropertyCache;
class QQmlContextData;
class QQmlTypeNameCache;
+struct QQmlIRLoader;
namespace QmlIR {
struct Document;
-struct IRLoader;
template <typename T>
struct PoolList
@@ -219,22 +216,30 @@ struct Enum
};
-struct SignalParameter : public QV4::CompiledData::Parameter
+struct Parameter : public QV4::CompiledData::Parameter
{
- SignalParameter *next;
+ Parameter *next;
+
+ bool init(QV4::Compiler::JSUnitGenerator *stringGenerator, const QString &parameterName, const QString &typeName);
+ static bool init(QV4::CompiledData::Parameter *param, const QV4::Compiler::JSUnitGenerator *stringGenerator,
+ int parameterNameIndex, int typeNameIndex);
+ static bool initType(QV4::CompiledData::ParameterType *paramType,
+ const QV4::Compiler::JSUnitGenerator *stringGenerator, int typeNameIndex);
+
+ static QV4::CompiledData::BuiltinType stringToBuiltinType(const QString &typeName);
};
struct Signal
{
int nameIndex;
QV4::CompiledData::Location location;
- PoolList<SignalParameter> *parameters;
+ PoolList<Parameter> *parameters;
QStringList parameterStringList(const QV4::Compiler::StringTableGenerator *stringPool) const;
int parameterCount() const { return parameters->count; }
- PoolList<SignalParameter>::Iterator parametersBegin() const { return parameters->begin(); }
- PoolList<SignalParameter>::Iterator parametersEnd() const { return parameters->end(); }
+ PoolList<Parameter>::Iterator parametersBegin() const { return parameters->begin(); }
+ PoolList<Parameter>::Iterator parametersEnd() const { return parameters->end(); }
Signal *next;
};
@@ -264,17 +269,18 @@ struct Function
QV4::CompiledData::Location location;
int nameIndex;
quint32 index; // index in parsedQML::functions
- FixedPoolArray<int> formals;
+ QQmlJS::FixedPoolArray<Parameter> formals;
+ QV4::CompiledData::ParameterType returnType;
// --- QQmlPropertyCacheCreator interface
- const int *formalsBegin() const { return formals.begin(); }
- const int *formalsEnd() const { return formals.end(); }
+ const Parameter *formalsBegin() const { return formals.begin(); }
+ const Parameter *formalsEnd() const { return formals.end(); }
// ---
Function *next;
};
-struct Q_QML_PRIVATE_EXPORT CompiledFunctionOrExpression
+struct Q_QMLCOMPILER_PRIVATE_EXPORT CompiledFunctionOrExpression
{
CompiledFunctionOrExpression()
{}
@@ -285,7 +291,7 @@ struct Q_QML_PRIVATE_EXPORT CompiledFunctionOrExpression
CompiledFunctionOrExpression *next = nullptr;
};
-struct Q_QML_PRIVATE_EXPORT Object
+struct Q_QMLCOMPILER_PRIVATE_EXPORT Object
{
Q_DECLARE_TR_FUNCTIONS(Object)
public:
@@ -344,14 +350,14 @@ public:
QString bindingAsString(Document *doc, int scriptIndex) const;
PoolList<CompiledFunctionOrExpression> *functionsAndExpressions;
- FixedPoolArray<int> runtimeFunctionIndices;
+ QQmlJS::FixedPoolArray<int> runtimeFunctionIndices;
- FixedPoolArray<quint32> namedObjectsInComponent;
+ QQmlJS::FixedPoolArray<quint32> namedObjectsInComponent;
int namedObjectsInComponentCount() const { return namedObjectsInComponent.size(); }
const quint32 *namedObjectsInComponentTable() const { return namedObjectsInComponent.begin(); }
private:
- friend struct IRLoader;
+ friend struct ::QQmlIRLoader;
PoolList<Property> *properties;
PoolList<Alias> *aliases;
@@ -361,7 +367,7 @@ private:
PoolList<Function> *functions;
};
-struct Q_QML_PRIVATE_EXPORT Pragma
+struct Q_QMLCOMPILER_PRIVATE_EXPORT Pragma
{
enum PragmaType {
PragmaSingleton = 0x1
@@ -371,7 +377,7 @@ struct Q_QML_PRIVATE_EXPORT Pragma
QV4::CompiledData::Location location;
};
-struct Q_QML_PRIVATE_EXPORT Document
+struct Q_QMLCOMPILER_PRIVATE_EXPORT Document
{
Document(bool debugMode);
QString code;
@@ -383,15 +389,13 @@ struct Q_QML_PRIVATE_EXPORT Document
QVector<Object*> objects;
QV4::Compiler::JSUnitGenerator jsGenerator;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> javaScriptCompilationUnit;
+ QV4::CompiledData::CompilationUnit javaScriptCompilationUnit;
int registerString(const QString &str) { return jsGenerator.registerString(str); }
QString stringAt(int index) const { return jsGenerator.stringForIndex(index); }
-
- static void removeScriptPragmas(QString &script);
};
-class Q_QML_PRIVATE_EXPORT ScriptDirectivesCollector : public QQmlJS::Directives
+class Q_QMLCOMPILER_PRIVATE_EXPORT ScriptDirectivesCollector : public QQmlJS::Directives
{
QmlIR::Document *document;
QQmlJS::Engine *engine;
@@ -405,7 +409,7 @@ public:
void importModule(const QString &uri, const QString &version, const QString &module, int lineNumber, int column) override;
};
-struct Q_QML_PRIVATE_EXPORT IRBuilder : public QQmlJS::AST::Visitor
+struct Q_QMLCOMPILER_PRIVATE_EXPORT IRBuilder : public QQmlJS::AST::Visitor
{
Q_DECLARE_TR_FUNCTIONS(QQmlCodeGenerator)
public:
@@ -436,7 +440,7 @@ public:
void throwRecursionDepthError() override
{
- recordError(AST::SourceLocation(),
+ recordError(QQmlJS::AST::SourceLocation(),
QStringLiteral("Maximum statement or expression depth exceeded"));
}
@@ -449,19 +453,25 @@ public:
static QString asString(QQmlJS::AST::UiQualifiedId *node);
QStringRef asStringRef(QQmlJS::AST::Node *node);
- static void extractVersion(QStringRef string, int *maj, int *min);
+ static void extractVersion(const QStringRef &string, int *maj, int *min);
QStringRef textRefAt(const QQmlJS::AST::SourceLocation &loc) const
{ return QStringRef(&sourceCode, loc.offset, loc.length); }
QStringRef textRefAt(const QQmlJS::AST::SourceLocation &first,
const QQmlJS::AST::SourceLocation &last) const;
- void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, AST::Node *parentNode);
+ void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement,
+ QQmlJS::AST::Node *parentNode);
void tryGeneratingTranslationBinding(const QStringRef &base, QQmlJS::AST::ArgumentList *args, QV4::CompiledData::Binding *binding);
- void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value, AST::Node *parentNode);
+ void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value,
+ QQmlJS::AST::Node *parentNode);
void appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment = false);
- void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value, AST::Node *parentNode);
- void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem = false, bool isOnAssignment = false);
+ void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation,
+ const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex,
+ QQmlJS::AST::Statement *value, QQmlJS::AST::Node *parentNode);
+ void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation,
+ const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex,
+ int objectIndex, bool isListItem = false, bool isOnAssignment = false);
bool appendAlias(QQmlJS::AST::UiPublicMember *node);
@@ -503,7 +513,7 @@ public:
QV4::Compiler::JSUnitGenerator *jsGenerator;
};
-struct Q_QML_PRIVATE_EXPORT QmlUnitGenerator
+struct Q_QMLCOMPILER_PRIVATE_EXPORT QmlUnitGenerator
{
void generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher = QV4::CompiledData::DependentTypesHasher());
@@ -512,76 +522,23 @@ private:
char *writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const;
};
-#ifndef V4_BOOTSTRAP
-struct Q_QML_EXPORT PropertyResolver
-{
- PropertyResolver(const QQmlRefPointer<QQmlPropertyCache> &cache)
- : cache(cache)
- {}
-
- QQmlPropertyData *property(int index) const
- {
- return cache->property(index);
- }
-
- enum RevisionCheck {
- CheckRevision,
- IgnoreRevision
- };
-
- QQmlPropertyData *property(const QString &name, bool *notInRevision = nullptr, RevisionCheck check = CheckRevision) const;
-
- // This code must match the semantics of QQmlPropertyPrivate::findSignalByName
- QQmlPropertyData *signal(const QString &name, bool *notInRevision) const;
-
- QQmlRefPointer<QQmlPropertyCache> cache;
-};
-#endif
-
-struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen
+struct Q_QMLCOMPILER_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen
{
- JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, QV4::Compiler::Module *jsModule,
- QQmlJS::Engine *jsEngine, QQmlJS::AST::UiProgram *qmlRoot,
- const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames);
+ JSCodeGen(Document *document, const QSet<QString> &globalNames);
// Returns mapping from input functions to index in IR::Module::functions / compiledData->runtimeFunctions
QVector<int> generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions);
-private:
- QString sourceCode;
- QQmlJS::Engine *jsEngine; // needed for memory pool
- QQmlJS::AST::UiProgram *qmlRoot;
- const QV4::Compiler::StringTableGenerator *stringPool;
-};
-
-struct Q_QML_PRIVATE_EXPORT IRLoader {
- IRLoader(const QV4::CompiledData::Unit *unit, QmlIR::Document *output);
-
- void load();
+ bool generateCodeForComponents(const QVector<quint32> &componentRoots);
+ bool compileComponent(int contextObject);
+ bool compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex);
private:
- QmlIR::Object *loadObject(const QV4::CompiledData::Object *serializedObject);
-
- template <typename _Tp> _Tp *New() { return pool->New<_Tp>(); }
-
- const QV4::CompiledData::Unit *unit;
- QmlIR::Document *output;
- QQmlJS::MemoryPool *pool;
+ Document *document;
};
} // namespace QmlIR
-struct QQmlCompileError
-{
- QQmlCompileError() {}
- QQmlCompileError(const QV4::CompiledData::Location &location, const QString &description)
- : location(location), description(description) {}
- QV4::CompiledData::Location location;
- QString description;
-
- bool isSet() const { return !description.isEmpty(); }
-};
-
QT_END_NAMESPACE
#endif // QQMLIRBUILDER_P_H
diff --git a/src/qml/compiler/qv4bytecodegenerator.cpp b/src/qml/compiler/qv4bytecodegenerator.cpp
index ea252a6013..7df1614ffe 100644
--- a/src/qml/compiler/qv4bytecodegenerator.cpp
+++ b/src/qml/compiler/qv4bytecodegenerator.cpp
@@ -206,7 +206,6 @@ int BytecodeGenerator::addInstructionHelper(Instr::Type type, const Instr &i, in
lastInstrType = int(type);
lastInstr = i;
-#if QT_CONFIG(qml_debug)
if (debugMode && type != Instr::Type::Debug) {
QT_WARNING_PUSH
QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instruction::Debug()
@@ -219,9 +218,6 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instru
}
QT_WARNING_POP
}
-#else
- Q_UNUSED(debugMode);
-#endif
const int pos = instructions.size();
diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h
index 1d0a57c536..8c509dd9f1 100644
--- a/src/qml/compiler/qv4bytecodegenerator_p.h
+++ b/src/qml/compiler/qv4bytecodegenerator_p.h
@@ -62,12 +62,15 @@ class SourceLocation;
}
namespace QV4 {
+
+namespace Compiler {
+struct Context;
+}
+
namespace Moth {
class BytecodeGenerator {
public:
- typedef CompiledData::Function::TraceInfoCount TraceInfoCount;
-
BytecodeGenerator(int line, bool debug)
: startLine(line), debugMode(debug) {}
@@ -164,15 +167,6 @@ public:
addInstructionHelper(Moth::Instr::Type(InstrT), genericInstr);
}
- // Same as addInstruction, but also add a trace slot. Move only, because the instruction cannot
- // be reused afterwards.
- template<int InstrT>
- void addTracingInstruction(InstrData<InstrT> data)
- {
- data.traceSlot = nextTraceInfo();
- addInstruction(data);
- }
-
Q_REQUIRED_RESULT Jump jump()
{
QT_WARNING_PUSH
@@ -184,12 +178,12 @@ QT_WARNING_POP
Q_REQUIRED_RESULT Jump jumpTrue()
{
- return addTracingJumpInstruction(Instruction::JumpTrue());
+ return addJumpInstruction(Instruction::JumpTrue());
}
Q_REQUIRED_RESULT Jump jumpFalse()
{
- return addTracingJumpInstruction(Instruction::JumpFalse());
+ return addJumpInstruction(Instruction::JumpFalse());
}
Q_REQUIRED_RESULT Jump jumpNotUndefined()
@@ -209,7 +203,7 @@ QT_WARNING_POP
Instruction::CmpStrictEqual cmp;
cmp.lhs = lhs;
addInstruction(std::move(cmp));
- addTracingJumpInstruction(Instruction::JumpTrue()).link(target);
+ addJumpInstruction(Instruction::JumpTrue()).link(target);
}
void jumpStrictNotEqual(const StackSlot &lhs, const Label &target)
@@ -217,7 +211,13 @@ QT_WARNING_POP
Instruction::CmpStrictNotEqual cmp;
cmp.lhs = lhs;
addInstruction(std::move(cmp));
- addTracingJumpInstruction(Instruction::JumpTrue()).link(target);
+ addJumpInstruction(Instruction::JumpTrue()).link(target);
+ }
+
+ void checkException()
+ {
+ Instruction::CheckException chk;
+ addInstruction(chk);
}
void setUnwindHandler(ExceptionHandler *handler)
@@ -258,13 +258,6 @@ QT_WARNING_POP
void finalize(Compiler::Context *context);
template<int InstrT>
- Jump addTracingJumpInstruction(InstrData<InstrT> &&data)
- {
- data.traceSlot = nextTraceInfo();
- return addJumpInstruction(data);
- }
-
- template<int InstrT>
Jump addJumpInstruction(const InstrData<InstrT> &data)
{
Instr genericInstr;
@@ -275,9 +268,9 @@ QT_WARNING_POP
void addCJumpInstruction(bool jumpOnFalse, const Label *trueLabel, const Label *falseLabel)
{
if (jumpOnFalse)
- addTracingJumpInstruction(Instruction::JumpFalse()).link(*falseLabel);
+ addJumpInstruction(Instruction::JumpFalse()).link(*falseLabel);
else
- addTracingJumpInstruction(Instruction::JumpTrue()).link(*trueLabel);
+ addJumpInstruction(Instruction::JumpTrue()).link(*trueLabel);
}
void clearLastInstruction()
@@ -285,27 +278,6 @@ QT_WARNING_POP
lastInstrType = -1;
}
- TraceInfoCount nextTraceInfo()
- {
- // If tracing is disabled, use slot 0 to unconditionally store all trace info
- if (nTraceInfos == CompiledData::Function::NoTracing())
- return TraceInfoCount(0);
- return nTraceInfos++;
- }
-
- void setTracing(bool onoff, int argumentCount)
- {
- if (onoff)
- nTraceInfos = argumentCount;
- else
- nTraceInfos = CompiledData::Function::NoTracing();
- }
-
- TraceInfoCount traceInfoCount() const
- {
- return nTraceInfos;
- }
-
void addLoopStart(const Label &start)
{
_labelInfos.push_back({ start.index });
@@ -346,8 +318,6 @@ private:
int lastInstrType = -1;
Moth::Instr lastInstr;
- TraceInfoCount nTraceInfos = TraceInfoCount(0);
-
struct LabelInfo {
int labelIndex;
};
diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp
index 92b112c2fa..f9f755b8c0 100644
--- a/src/qml/compiler/qv4bytecodehandler.cpp
+++ b/src/qml/compiler/qv4bytecodehandler.cpp
@@ -58,9 +58,10 @@ ByteCodeHandler::~ByteCodeHandler()
Q_UNUSED(base_ptr); \
_currentOffset = _nextOffset; \
_nextOffset = code - start; \
- startInstruction(Instr::Type::instr); \
- INSTR_##instr(DISPATCH) \
- endInstruction(Instr::Type::instr); \
+ if (startInstruction(Instr::Type::instr) == ProcessInstruction) { \
+ INSTR_##instr(DISPATCH) \
+ endInstruction(Instr::Type::instr); \
+ } \
continue; \
}
diff --git a/src/qml/compiler/qv4bytecodehandler_p.h b/src/qml/compiler/qv4bytecodehandler_p.h
index 797d25b8d0..f1e7c99447 100644
--- a/src/qml/compiler/qv4bytecodehandler_p.h
+++ b/src/qml/compiler/qv4bytecodehandler_p.h
@@ -105,7 +105,8 @@ public:
protected:
FOR_EACH_MOTH_INSTR(BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER)
- virtual void startInstruction(Moth::Instr::Type instr) = 0;
+ enum Verdict { ProcessInstruction, SkipInstruction };
+ virtual Verdict startInstruction(Moth::Instr::Type instr) = 0;
virtual void endInstruction(Moth::Instr::Type instr) = 0;
private:
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 5acc64bd81..c43ea64e2e 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -43,18 +43,18 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QStringList>
#include <QtCore/QStack>
+#include <QtCore/qurl.h>
#include <QScopeGuard>
#include <private/qqmljsast_p.h>
-#include <private/qv4string_p.h>
-#include <private/qv4value_p.h>
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsparser_p.h>
+#include <private/qv4staticvalue_p.h>
#include <private/qv4compilercontext_p.h>
#include <private/qv4compilercontrolflow_p.h>
#include <private/qv4bytecodegenerator_p.h>
#include <private/qv4compilerscanfunctions_p.h>
-
-#ifndef V4_BOOTSTRAP
-# include <qqmlerror.h>
-#endif
+#include <private/qv4stringtoarrayindex_p.h>
+#include <private/qqmljsdiagnosticmessage_p.h>
#include <cmath>
#include <iostream>
@@ -68,6 +68,7 @@ static const bool disable_lookups = false;
QT_USE_NAMESPACE
using namespace QV4;
using namespace QV4::Compiler;
+using namespace QQmlJS;
using namespace QQmlJS::AST;
static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGenerator,
@@ -97,7 +98,6 @@ Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict)
, jsUnitGenerator(jsUnitGenerator)
, _strictMode(strict)
, _fileNameIsUrl(false)
- , hasError(false)
{
jsUnitGenerator->codeGeneratorName = QStringLiteral("moth");
pushExpr();
@@ -190,7 +190,7 @@ void Codegen::generateFromProgram(const QString &fileName,
ScanFunctions scan(this, sourceCode, contextType);
scan(node);
- if (hasError)
+ if (hasError())
return;
defineFunction(QStringLiteral("%entry"), node, nullptr, node->statements);
@@ -214,7 +214,7 @@ void Codegen::generateFromModule(const QString &fileName,
ScanFunctions scan(this, sourceCode, ContextType::ESModule);
scan(node);
- if (hasError)
+ if (hasError())
return;
{
@@ -264,17 +264,30 @@ Context *Codegen::enterBlock(Node *node)
Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
{
- if (hasError)
+ if (hasError())
return exprResult();
if (expr.isConstant()) {
- auto v = Value::fromReturnedValue(expr.constant);
+ auto v = StaticValue::fromReturnedValue(expr.constant);
if (v.isNumber()) {
switch (op) {
case Not:
return Reference::fromConst(this, Encode(!v.toBoolean()));
case UMinus:
- return Reference::fromConst(this, Runtime::method_uMinus(v));
+ // This duplicates some of the logic from Runtime::UMinus::call()
+ ReturnedValue r;
+ if (v.isInteger()) {
+ int intVal = v.integerValue();
+ if (intVal && intVal != std::numeric_limits<int>::min())
+ r = QV4::Encode(-intVal);
+ else
+ r = QV4::Encode(-double(intVal));
+ } else if (v.isDouble()) {
+ r = QV4::Encode(-v.doubleValue());
+ } else {
+ r = QV4::Encode(-v.int_32());
+ }
+ return Reference::fromConst(this, r);
case UPlus:
return expr;
case Compl:
@@ -289,12 +302,12 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
case UMinus: {
expr.loadInAccumulator();
Instruction::UMinus uminus = {};
- bytecodeGenerator->addTracingInstruction(uminus);
+ bytecodeGenerator->addInstruction(uminus);
return Reference::fromAccumulator(this);
}
case UPlus: {
expr.loadInAccumulator();
- Instruction::UPlus uplus;
+ Instruction::UPlus uplus = {};
bytecodeGenerator->addInstruction(uplus);
return Reference::fromAccumulator(this);
}
@@ -314,11 +327,11 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
if (!exprAccept(nx) || requiresReturnValue) {
Reference e = expr.asLValue();
e.loadInAccumulator();
- Instruction::UPlus uplus;
+ Instruction::UPlus uplus = {};
bytecodeGenerator->addInstruction(uplus);
Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator();
Instruction::Increment inc = {};
- bytecodeGenerator->addTracingInstruction(inc);
+ bytecodeGenerator->addInstruction(inc);
e.storeConsumeAccumulator();
return originalValue;
} else {
@@ -330,7 +343,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
Reference e = expr.asLValue();
e.loadInAccumulator();
Instruction::Increment inc = {};
- bytecodeGenerator->addTracingInstruction(inc);
+ bytecodeGenerator->addInstruction(inc);
if (exprAccept(nx))
return e.storeConsumeAccumulator();
else
@@ -340,11 +353,11 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
if (!exprAccept(nx) || requiresReturnValue) {
Reference e = expr.asLValue();
e.loadInAccumulator();
- Instruction::UPlus uplus;
+ Instruction::UPlus uplus = {};
bytecodeGenerator->addInstruction(uplus);
Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator();
Instruction::Decrement dec = {};
- bytecodeGenerator->addTracingInstruction(dec);
+ bytecodeGenerator->addInstruction(dec);
e.storeConsumeAccumulator();
return originalValue;
} else {
@@ -356,7 +369,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
Reference e = expr.asLValue();
e.loadInAccumulator();
Instruction::Decrement dec = {};
- bytecodeGenerator->addTracingInstruction(dec);
+ bytecodeGenerator->addInstruction(dec);
if (exprAccept(nx))
return e.storeConsumeAccumulator();
else
@@ -402,7 +415,7 @@ void Codegen::statement(ExpressionNode *ast)
qSwap(_volatileMemoryLocations, vLocs);
Reference result = popResult();
- if (hasError)
+ if (hasError())
return;
if (result.loadTriggersSideEffect())
result.loadInAccumulator(); // triggers side effects
@@ -412,7 +425,7 @@ void Codegen::statement(ExpressionNode *ast)
void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *iftrue,
const BytecodeGenerator::Label *iffalse, bool trueBlockFollowsCondition)
{
- if (hasError)
+ if (hasError())
return;
if (!ast)
@@ -422,7 +435,7 @@ void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *ift
accept(ast);
Result r = popExpr();
- if (hasError)
+ if (hasError())
return;
if (r.format() == ex) {
@@ -576,7 +589,7 @@ Codegen::Reference Codegen::targetForPatternElement(AST::PatternElement *p)
if (!p->bindingTarget || p->destructuringPattern())
return Codegen::Reference::fromStackSlot(this);
Reference lhs = expression(p->bindingTarget);
- if (hasError)
+ if (hasError())
return lhs;
if (!lhs.isLValue()) {
throwReferenceError(p->bindingTarget->firstSourceLocation(), QStringLiteral("Binding target is not a reference."));
@@ -594,14 +607,16 @@ void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, con
Reference varToStore = targetForPatternElement(e);
if (isDefinition)
varToStore.isReferenceToConst = false;
- if (hasError)
+ if (hasError())
return;
+ accept(e->typeAnnotation);
+
if (e->initializer) {
if (!baseRef.isValid()) {
// assignment
Reference expr = expression(e->initializer);
- if (hasError)
+ if (hasError())
return;
expr.loadInAccumulator();
varToStore.storeConsumeAccumulator();
@@ -609,7 +624,7 @@ void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, con
baseRef.loadInAccumulator();
BytecodeGenerator::Jump jump = bytecodeGenerator->jumpNotUndefined();
Reference expr = expression(e->initializer);
- if (hasError) {
+ if (hasError()) {
jump.link();
return;
}
@@ -620,7 +635,7 @@ void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, con
baseRef.loadInAccumulator();
BytecodeGenerator::Jump jump = bytecodeGenerator->jumpNotUndefined();
Reference expr = expression(e->initializer);
- if (hasError) {
+ if (hasError()) {
jump.link();
return;
}
@@ -657,7 +672,7 @@ Codegen::Reference Codegen::referenceForPropertyName(const Codegen::Reference &o
Reference property;
if (cname) {
Reference computedName = expression(cname->expression);
- if (hasError)
+ if (hasError())
return Reference();
computedName = computedName.storeOnStack();
property = Reference::fromSubscript(object, computedName).asLValue();
@@ -680,10 +695,10 @@ void Codegen::destructurePropertyList(const Codegen::Reference &object, PatternP
PatternProperty *p = it->property;
RegisterScope scope(this);
Reference property = referenceForPropertyName(object, p->name);
- if (hasError)
+ if (hasError())
return;
initializeAndDestructureBindingElement(p, property, isDefinition);
- if (hasError)
+ if (hasError())
return;
}
}
@@ -739,7 +754,7 @@ void Codegen::destructureElementList(const Codegen::Reference &array, PatternEle
next.done = iteratorDone.stackSlot();
bytecodeGenerator->addInstruction(next);
initializeAndDestructureBindingElement(e, iteratorValue, isDefinition);
- if (hasError)
+ if (hasError())
return;
}
}
@@ -872,6 +887,12 @@ bool Codegen::visit(ExportDeclaration *ast)
return false;
}
+bool Codegen::visit(TypeAnnotation *ast)
+{
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Type annotations are not supported (yet)."));
+ return false;
+}
+
bool Codegen::visit(StatementList *)
{
Q_UNREACHABLE();
@@ -1003,11 +1024,11 @@ bool Codegen::visit(ClassExpression *ast)
if (ast->heritage) {
bytecodeGenerator->setLocation(ast->heritage->firstSourceLocation());
Reference r = expression(ast->heritage);
- if (hasError)
+ if (hasError())
return false;
r.storeOnStack(heritage.stackSlot());
} else {
- Reference::fromConst(this, Value::emptyValue().asReturnedValue()).loadInAccumulator();
+ Reference::fromConst(this, StaticValue::emptyValue().asReturnedValue()).loadInAccumulator();
heritage.storeConsumeAccumulator();
}
@@ -1022,7 +1043,7 @@ bool Codegen::visit(ClassExpression *ast)
RegisterScope scope(this);
bytecodeGenerator->setLocation(cname->firstSourceLocation());
Reference computedName = expression(cname->expression);
- if (hasError)
+ if (hasError())
return false;
computedName.storeOnStack(member->isStatic ? currentStaticName++ : currentNonStaticName++);
}
@@ -1055,7 +1076,7 @@ bool Codegen::visit(ClassDeclaration *ast)
bool Codegen::visit(Expression *ast)
{
- if (hasError)
+ if (hasError())
return false;
TailCallBlocker blockTailCalls(this);
@@ -1068,7 +1089,7 @@ bool Codegen::visit(Expression *ast)
bool Codegen::visit(ArrayPattern *ast)
{
- if (hasError)
+ if (hasError())
return false;
TailCallBlocker blockTailCalls(this);
@@ -1085,12 +1106,12 @@ bool Codegen::visit(ArrayPattern *ast)
if (args == -1)
args = temp;
if (!arg) {
- auto c = Reference::fromConst(this, Value::emptyValue().asReturnedValue());
+ auto c = Reference::fromConst(this, StaticValue::emptyValue().asReturnedValue());
(void) c.storeOnStack(temp);
} else {
RegisterScope scope(this);
Reference r = expression(arg);
- if (hasError)
+ if (hasError())
return;
(void) r.storeOnStack(temp);
}
@@ -1108,7 +1129,7 @@ bool Codegen::visit(ArrayPattern *ast)
continue;
push(e->initializer);
- if (hasError)
+ if (hasError())
return false;
}
@@ -1140,13 +1161,14 @@ bool Codegen::visit(ArrayPattern *ast)
index.loadInAccumulator();
Instruction::Increment inc = {};
- bytecodeGenerator->addTracingInstruction(inc);
+ bytecodeGenerator->addInstruction(inc);
index.storeConsumeAccumulator();
};
while (it) {
for (Elision *elision = it->elision; elision; elision = elision->next) {
- Reference::fromConst(this, Value::emptyValue().asReturnedValue()).loadInAccumulator();
+ Reference::fromConst(
+ this, StaticValue::emptyValue().asReturnedValue()).loadInAccumulator();
pushAccumulator();
}
@@ -1168,7 +1190,7 @@ bool Codegen::visit(ArrayPattern *ast)
{
RegisterScope innerScope(this);
Reference expr = expression(it->element->initializer);
- if (hasError)
+ if (hasError())
return false;
expr.loadInAccumulator();
@@ -1202,13 +1224,14 @@ bool Codegen::visit(ArrayPattern *ast)
lhsValue.loadInAccumulator();
pushAccumulator();
+ bytecodeGenerator->checkException();
bytecodeGenerator->jump().link(in);
end.link();
}
} else {
RegisterScope innerScope(this);
Reference expr = expression(it->element->initializer);
- if (hasError)
+ if (hasError())
return false;
expr.loadInAccumulator();
@@ -1226,12 +1249,12 @@ bool Codegen::visit(ArrayPattern *ast)
bool Codegen::visit(ArrayMemberExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
TailCallBlocker blockTailCalls(this);
Reference base = expression(ast->base);
- if (hasError)
+ if (hasError())
return false;
if (base.isSuper()) {
Reference index = expression(ast->expression).storeOnStack();
@@ -1239,11 +1262,11 @@ bool Codegen::visit(ArrayMemberExpression *ast)
return false;
}
base = base.storeOnStack();
- if (hasError)
+ if (hasError())
return false;
if (AST::StringLiteral *str = AST::cast<AST::StringLiteral *>(ast->expression)) {
QString s = str->value.toString();
- uint arrayIndex = QV4::String::toArrayIndex(s);
+ uint arrayIndex = stringToArrayIndex(s);
if (arrayIndex == UINT_MAX) {
setExprResult(Reference::fromMember(base, str->value.toString()));
return false;
@@ -1253,7 +1276,7 @@ bool Codegen::visit(ArrayMemberExpression *ast)
return false;
}
Reference index = expression(ast->expression);
- if (hasError)
+ if (hasError())
return false;
setExprResult(Reference::fromSubscript(base, index));
return false;
@@ -1280,7 +1303,7 @@ static QSOperator::Op baseOp(int op)
bool Codegen::visit(BinaryExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
TailCallBlocker blockTailCalls(this);
@@ -1298,7 +1321,7 @@ bool Codegen::visit(BinaryExpression *ast)
auto endif = bytecodeGenerator->newLabel();
Reference left = expression(ast->left);
- if (hasError)
+ if (hasError())
return false;
left.loadInAccumulator();
@@ -1308,7 +1331,7 @@ bool Codegen::visit(BinaryExpression *ast)
blockTailCalls.unblock();
Reference right = expression(ast->right);
- if (hasError)
+ if (hasError())
return false;
right.loadInAccumulator();
@@ -1329,7 +1352,7 @@ bool Codegen::visit(BinaryExpression *ast)
auto endif = bytecodeGenerator->newLabel();
Reference left = expression(ast->left);
- if (hasError)
+ if (hasError())
return false;
left.loadInAccumulator();
@@ -1339,7 +1362,7 @@ bool Codegen::visit(BinaryExpression *ast)
blockTailCalls.unblock();
Reference right = expression(ast->right);
- if (hasError)
+ if (hasError())
return false;
right.loadInAccumulator();
@@ -1352,7 +1375,7 @@ bool Codegen::visit(BinaryExpression *ast)
if (AST::Pattern *p = ast->left->patternCast()) {
RegisterScope scope(this);
Reference right = expression(ast->right);
- if (hasError)
+ if (hasError())
return false;
right = right.storeOnStack();
destructurePattern(p, right);
@@ -1363,7 +1386,7 @@ bool Codegen::visit(BinaryExpression *ast)
return false;
}
Reference left = expression(ast->left);
- if (hasError)
+ if (hasError())
return false;
if (!left.isLValue()) {
@@ -1375,7 +1398,7 @@ bool Codegen::visit(BinaryExpression *ast)
return false;
blockTailCalls.unblock();
Reference r = expression(ast->right);
- if (hasError)
+ if (hasError())
return false;
r.loadInAccumulator();
if (exprAccept(nx))
@@ -1386,7 +1409,7 @@ bool Codegen::visit(BinaryExpression *ast)
}
Reference left = expression(ast->left);
- if (hasError)
+ if (hasError())
return false;
switch (ast->op) {
@@ -1420,7 +1443,7 @@ bool Codegen::visit(BinaryExpression *ast)
Reference tempLeft = left.storeOnStack();
Reference right = expression(ast->right);
- if (hasError)
+ if (hasError())
return false;
binopHelper(baseOp(ast->op), tempLeft, right).loadInAccumulator();
@@ -1434,7 +1457,7 @@ bool Codegen::visit(BinaryExpression *ast)
case QSOperator::BitXor:
if (left.isConstant()) {
Reference right = expression(ast->right);
- if (hasError)
+ if (hasError())
return false;
setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left));
break;
@@ -1467,7 +1490,7 @@ bool Codegen::visit(BinaryExpression *ast)
left = left.storeOnStack(); // force any loads of the lhs, so the rhs won't clobber it
right = expression(ast->right);
}
- if (hasError)
+ if (hasError())
return false;
setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right));
@@ -1488,20 +1511,20 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
right.loadInAccumulator();
Instruction::Add add;
add.lhs = left.stackSlot();
- bytecodeGenerator->addTracingInstruction(add);
+ bytecodeGenerator->addInstruction(add);
break;
}
case QSOperator::Sub: {
if (right.isConstant() && right.constant == Encode(int(1))) {
left.loadInAccumulator();
Instruction::Decrement dec = {};
- bytecodeGenerator->addTracingInstruction(dec);
+ bytecodeGenerator->addInstruction(dec);
} else {
left = left.storeOnStack();
right.loadInAccumulator();
Instruction::Sub sub;
sub.lhs = left.stackSlot();
- bytecodeGenerator->addTracingInstruction(sub);
+ bytecodeGenerator->addInstruction(sub);
}
break;
}
@@ -1518,7 +1541,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
right.loadInAccumulator();
Instruction::Mul mul;
mul.lhs = left.stackSlot();
- bytecodeGenerator->addTracingInstruction(mul);
+ bytecodeGenerator->addInstruction(mul);
break;
}
case QSOperator::Div: {
@@ -1534,14 +1557,14 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
right.loadInAccumulator();
Instruction::Mod mod;
mod.lhs = left.stackSlot();
- bytecodeGenerator->addTracingInstruction(mod);
+ bytecodeGenerator->addInstruction(mod);
break;
}
case QSOperator::BitAnd:
if (right.isConstant()) {
- int rightAsInt = Value::fromReturnedValue(right.constant).toInt32();
+ int rightAsInt = StaticValue::fromReturnedValue(right.constant).toInt32();
if (left.isConstant()) {
- int result = Value::fromReturnedValue(left.constant).toInt32() & rightAsInt;
+ int result = StaticValue::fromReturnedValue(left.constant).toInt32() & rightAsInt;
return Reference::fromConst(this, Encode(result));
}
left.loadInAccumulator();
@@ -1557,9 +1580,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
case QSOperator::BitOr:
if (right.isConstant()) {
- int rightAsInt = Value::fromReturnedValue(right.constant).toInt32();
+ int rightAsInt = StaticValue::fromReturnedValue(right.constant).toInt32();
if (left.isConstant()) {
- int result = Value::fromReturnedValue(left.constant).toInt32() | rightAsInt;
+ int result = StaticValue::fromReturnedValue(left.constant).toInt32() | rightAsInt;
return Reference::fromConst(this, Encode(result));
}
left.loadInAccumulator();
@@ -1575,9 +1598,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
case QSOperator::BitXor:
if (right.isConstant()) {
- int rightAsInt = Value::fromReturnedValue(right.constant).toInt32();
+ int rightAsInt = StaticValue::fromReturnedValue(right.constant).toInt32();
if (left.isConstant()) {
- int result = Value::fromReturnedValue(left.constant).toInt32() ^ rightAsInt;
+ int result = StaticValue::fromReturnedValue(left.constant).toInt32() ^ rightAsInt;
return Reference::fromConst(this, Encode(result));
}
left.loadInAccumulator();
@@ -1595,7 +1618,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
if (right.isConstant()) {
left.loadInAccumulator();
Instruction::UShrConst ushr;
- ushr.rhs = Value::fromReturnedValue(right.constant).toInt32() & 0x1f;
+ ushr.rhs = StaticValue::fromReturnedValue(right.constant).toInt32() & 0x1f;
bytecodeGenerator->addInstruction(ushr);
} else {
right.loadInAccumulator();
@@ -1608,7 +1631,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
if (right.isConstant()) {
left.loadInAccumulator();
Instruction::ShrConst shr;
- shr.rhs = Value::fromReturnedValue(right.constant).toInt32() & 0x1f;
+ shr.rhs = StaticValue::fromReturnedValue(right.constant).toInt32() & 0x1f;
bytecodeGenerator->addInstruction(shr);
} else {
right.loadInAccumulator();
@@ -1621,7 +1644,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
if (right.isConstant()) {
left.loadInAccumulator();
Instruction::ShlConst shl;
- shl.rhs = Value::fromReturnedValue(right.constant).toInt32() & 0x1f;
+ shl.rhs = StaticValue::fromReturnedValue(right.constant).toInt32() & 0x1f;
bytecodeGenerator->addInstruction(shl);
} else {
right.loadInAccumulator();
@@ -1749,7 +1772,7 @@ Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Refe
qSwap(left, right); // null==a -> a==null
if (right.isConstant()) {
- Value c = Value::fromReturnedValue(right.constant);
+ StaticValue c = StaticValue::fromReturnedValue(right.constant);
if (c.isNull() || c.isUndefined()) {
left.loadInAccumulator();
if (oper == QSOperator::Equal) {
@@ -1851,7 +1874,7 @@ Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Refe
bool Codegen::visit(CallExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
RegisterScope scope(this);
@@ -1859,7 +1882,7 @@ bool Codegen::visit(CallExpression *ast)
Reference base = expression(ast->base);
- if (hasError)
+ if (hasError())
return false;
switch (base.type) {
case Reference::Member:
@@ -1882,7 +1905,7 @@ bool Codegen::visit(CallExpression *ast)
int functionObject = bytecodeGenerator->newRegister();
auto calldata = pushArgs(ast->arguments);
- if (hasError)
+ if (hasError())
return false;
blockTailCalls.unblock();
@@ -1903,7 +1926,7 @@ bool Codegen::visit(CallExpression *ast)
call.thisObject = baseObject.stackSlot();
call.argc = calldata.argc;
call.argv = calldata.argv;
- bytecodeGenerator->addTracingInstruction(call);
+ bytecodeGenerator->addInstruction(call);
} else {
Instruction::TailCall call;
call.func = base.stackSlot();
@@ -1932,14 +1955,14 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio
call.lookupIndex = registerGetterLookup(base.propertyNameIndex);
call.argc = calldata.argc;
call.argv = calldata.argv;
- bytecodeGenerator->addTracingInstruction(call);
+ bytecodeGenerator->addInstruction(call);
} else {
Instruction::CallProperty call;
call.base = base.propertyBase.stackSlot();
call.name = base.propertyNameIndex;
call.argc = calldata.argc;
call.argv = calldata.argv;
- bytecodeGenerator->addTracingInstruction(call);
+ bytecodeGenerator->addInstruction(call);
}
} else if (base.type == Reference::Subscript) {
Instruction::CallElement call;
@@ -1947,33 +1970,33 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio
call.index = base.elementSubscript.stackSlot();
call.argc = calldata.argc;
call.argv = calldata.argv;
- bytecodeGenerator->addTracingInstruction(call);
+ bytecodeGenerator->addInstruction(call);
} else if (base.type == Reference::Name) {
if (base.name == QStringLiteral("eval")) {
Instruction::CallPossiblyDirectEval call;
call.argc = calldata.argc;
call.argv = calldata.argv;
- bytecodeGenerator->addTracingInstruction(call);
+ bytecodeGenerator->addInstruction(call);
} else if (!disable_lookups && useFastLookups && base.global) {
if (base.qmlGlobal) {
Instruction::CallQmlContextPropertyLookup call;
call.index = registerQmlContextPropertyGetterLookup(base.nameAsIndex());
call.argc = calldata.argc;
call.argv = calldata.argv;
- bytecodeGenerator->addTracingInstruction(call);
+ bytecodeGenerator->addInstruction(call);
} else {
Instruction::CallGlobalLookup call;
call.index = registerGlobalGetterLookup(base.nameAsIndex());
call.argc = calldata.argc;
call.argv = calldata.argv;
- bytecodeGenerator->addTracingInstruction(call);
+ bytecodeGenerator->addInstruction(call);
}
} else {
Instruction::CallName call;
call.name = base.nameAsIndex();
call.argc = calldata.argc;
call.argv = calldata.argv;
- bytecodeGenerator->addTracingInstruction(call);
+ bytecodeGenerator->addInstruction(call);
}
} else if (base.type == Reference::SuperProperty) {
Reference receiver = base.baseObject();
@@ -1990,14 +2013,14 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio
call.thisObject = receiver.stackSlot();
call.argc = calldata.argc;
call.argv = calldata.argv;
- bytecodeGenerator->addTracingInstruction(call);
+ bytecodeGenerator->addInstruction(call);
} else {
Q_ASSERT(base.isStackSlot());
Instruction::CallValue call;
call.name = base.stackSlot();
call.argc = calldata.argc;
call.argv = calldata.argv;
- bytecodeGenerator->addTracingInstruction(call);
+ bytecodeGenerator->addInstruction(call);
}
setExprResult(Reference::fromAccumulator(this));
@@ -2023,12 +2046,14 @@ Codegen::Arguments Codegen::pushArgs(ArgumentList *args)
argc = 0;
for (ArgumentList *it = args; it; it = it->next) {
if (it->isSpreadElement) {
- Reference::fromConst(this, Value::emptyValue().asReturnedValue()).storeOnStack(calldata + argc);
+ Reference::fromConst(
+ this,
+ StaticValue::emptyValue().asReturnedValue()).storeOnStack(calldata + argc);
++argc;
}
RegisterScope scope(this);
Reference e = expression(it->expression);
- if (hasError)
+ if (hasError())
break;
if (!argc && !it->next && !hasSpread) {
// avoid copy for functions taking a single argument
@@ -2057,7 +2082,7 @@ Codegen::Arguments Codegen::pushTemplateArgs(TemplateLiteral *args)
for (TemplateLiteral *it = args; it && it->expression; it = it->next) {
RegisterScope scope(this);
Reference e = expression(it->expression);
- if (hasError)
+ if (hasError())
break;
(void) e.storeOnStack(calldata + argc);
++argc;
@@ -2068,7 +2093,7 @@ Codegen::Arguments Codegen::pushTemplateArgs(TemplateLiteral *args)
bool Codegen::visit(ConditionalExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
RegisterScope scope(this);
@@ -2082,14 +2107,14 @@ bool Codegen::visit(ConditionalExpression *ast)
iftrue.link();
Reference ok = expression(ast->ok);
- if (hasError)
+ if (hasError())
return false;
ok.loadInAccumulator();
BytecodeGenerator::Jump jump_endif = bytecodeGenerator->jump();
iffalse.link();
Reference ko = expression(ast->ko);
- if (hasError) {
+ if (hasError()) {
jump_endif.link(); // dummy link, to prevent assert in Jump destructor from triggering
return false;
}
@@ -2103,13 +2128,13 @@ bool Codegen::visit(ConditionalExpression *ast)
bool Codegen::visit(DeleteExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
RegisterScope scope(this);
TailCallBlocker blockTailCalls(this);
Reference expr = expression(ast->expression);
- if (hasError)
+ if (hasError())
return false;
switch (expr.type) {
@@ -2174,7 +2199,7 @@ bool Codegen::visit(DeleteExpression *ast)
bool Codegen::visit(FalseLiteral *)
{
- if (hasError)
+ if (hasError())
return false;
setExprResult(Reference::fromConst(this, QV4::Encode(false)));
@@ -2183,7 +2208,7 @@ bool Codegen::visit(FalseLiteral *)
bool Codegen::visit(SuperLiteral *)
{
- if (hasError)
+ if (hasError())
return false;
setExprResult(Reference::fromSuper(this));
@@ -2192,7 +2217,7 @@ bool Codegen::visit(SuperLiteral *)
bool Codegen::visit(FieldMemberExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
TailCallBlocker blockTailCalls(this);
@@ -2215,7 +2240,7 @@ bool Codegen::visit(FieldMemberExpression *ast)
}
Reference base = expression(ast->base);
- if (hasError)
+ if (hasError())
return false;
if (base.isSuper()) {
Instruction::LoadRuntimeString load;
@@ -2231,7 +2256,7 @@ bool Codegen::visit(FieldMemberExpression *ast)
bool Codegen::visit(TaggedTemplate *ast)
{
- if (hasError)
+ if (hasError())
return false;
RegisterScope scope(this);
@@ -2240,7 +2265,7 @@ bool Codegen::visit(TaggedTemplate *ast)
bool Codegen::handleTaggedTemplate(Reference base, TaggedTemplate *ast)
{
- if (hasError)
+ if (hasError())
return false;
int functionObject = -1, thisObject = -1;
@@ -2264,7 +2289,7 @@ bool Codegen::handleTaggedTemplate(Reference base, TaggedTemplate *ast)
int templateObjectTemp = Reference::fromAccumulator(this).storeOnStack().stackSlot();
Q_UNUSED(templateObjectTemp);
auto calldata = pushTemplateArgs(ast->templateLiteral);
- if (hasError)
+ if (hasError())
return false;
++calldata.argc;
Q_ASSERT(calldata.argv == templateObjectTemp + 1);
@@ -2293,7 +2318,7 @@ void Codegen::createTemplateObject(TemplateLiteral *t)
bool Codegen::visit(FunctionExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
TailCallBlocker blockTailCalls(this);
@@ -2301,7 +2326,7 @@ bool Codegen::visit(FunctionExpression *ast)
RegisterScope scope(this);
int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body);
- if (hasError)
+ if (hasError())
return false;
loadClosure(function);
setExprResult(Reference::fromAccumulator(this));
@@ -2357,7 +2382,7 @@ void Codegen::loadClosure(int closureId)
bool Codegen::visit(IdentifierExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
setExprResult(referenceForName(ast->name.toString(), false, ast->firstSourceLocation()));
@@ -2366,7 +2391,7 @@ bool Codegen::visit(IdentifierExpression *ast)
bool Codegen::visit(NestedExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
accept(ast->expression);
@@ -2385,7 +2410,7 @@ void Codegen::handleConstruct(const Reference &base, ArgumentList *arguments)
}
auto calldata = pushArgs(arguments);
- if (hasError)
+ if (hasError())
return;
if (base.isSuper())
@@ -2415,14 +2440,14 @@ void Codegen::handleConstruct(const Reference &base, ArgumentList *arguments)
bool Codegen::visit(NewExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
RegisterScope scope(this);
TailCallBlocker blockTailCalls(this);
Reference base = expression(ast->expression);
- if (hasError)
+ if (hasError())
return false;
if (base.isSuper()) {
throwSyntaxError(ast->expression->firstSourceLocation(), QStringLiteral("Cannot use new with super."));
@@ -2435,14 +2460,14 @@ bool Codegen::visit(NewExpression *ast)
bool Codegen::visit(NewMemberExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
RegisterScope scope(this);
TailCallBlocker blockTailCalls(this);
Reference base = expression(ast->base);
- if (hasError)
+ if (hasError())
return false;
if (base.isSuper()) {
throwSyntaxError(ast->base->firstSourceLocation(), QStringLiteral("Cannot use new with super."));
@@ -2455,7 +2480,7 @@ bool Codegen::visit(NewMemberExpression *ast)
bool Codegen::visit(NotExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
TailCallBlocker blockTailCalls(this);
@@ -2465,7 +2490,7 @@ bool Codegen::visit(NotExpression *ast)
bool Codegen::visit(NullExpression *)
{
- if (hasError)
+ if (hasError())
return false;
if (exprAccept(cx))
@@ -2478,7 +2503,7 @@ bool Codegen::visit(NullExpression *)
bool Codegen::visit(NumericLiteral *ast)
{
- if (hasError)
+ if (hasError())
return false;
setExprResult(Reference::fromConst(this, QV4::Encode::smallestNumber(ast->value)));
@@ -2487,7 +2512,7 @@ bool Codegen::visit(NumericLiteral *ast)
bool Codegen::visit(ObjectPattern *ast)
{
- if (hasError)
+ if (hasError())
return false;
TailCallBlocker blockTailCalls(this);
@@ -2513,7 +2538,7 @@ bool Codegen::visit(ObjectPattern *ast)
if (cname || p->type != PatternProperty::Literal)
break;
QString name = p->name->asString();
- uint arrayIndex = QV4::String::toArrayIndex(name);
+ uint arrayIndex = stringToArrayIndex(name);
if (arrayIndex != UINT_MAX)
break;
if (members.contains(name))
@@ -2523,7 +2548,7 @@ bool Codegen::visit(ObjectPattern *ast)
{
RegisterScope innerScope(this);
Reference value = expression(p->initializer, name);
- if (hasError)
+ if (hasError())
return false;
value.loadInAccumulator();
}
@@ -2550,7 +2575,7 @@ bool Codegen::visit(ObjectPattern *ast)
if (cname) {
RegisterScope innerScope(this);
Reference name = expression(cname->expression);
- if (hasError)
+ if (hasError())
return false;
name.loadInAccumulator();
} else {
@@ -2575,12 +2600,12 @@ bool Codegen::visit(ObjectPattern *ast)
FunctionExpression *f = p->initializer->asFunctionDefinition();
Q_ASSERT(f);
int function = defineFunction(f->name.toString(), f, f->formals, f->body);
- if (hasError)
+ if (hasError())
return false;
Reference::fromConst(this, Encode(function)).loadInAccumulator();
} else {
Reference value = expression(p->initializer);
- if (hasError)
+ if (hasError())
return false;
value.loadInAccumulator();
}
@@ -2599,11 +2624,11 @@ bool Codegen::visit(ObjectPattern *ast)
bool Codegen::visit(PostDecrementExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
Reference expr = expression(ast->base);
- if (hasError)
+ if (hasError())
return false;
if (!expr.isLValue()) {
throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation"));
@@ -2619,11 +2644,11 @@ bool Codegen::visit(PostDecrementExpression *ast)
bool Codegen::visit(PostIncrementExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
Reference expr = expression(ast->base);
- if (hasError)
+ if (hasError())
return false;
if (!expr.isLValue()) {
throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation"));
@@ -2637,11 +2662,11 @@ bool Codegen::visit(PostIncrementExpression *ast)
}
bool Codegen::visit(PreDecrementExpression *ast)
-{ if (hasError)
+{ if (hasError())
return false;
Reference expr = expression(ast->expression);
- if (hasError)
+ if (hasError())
return false;
if (!expr.isLValue()) {
throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference."));
@@ -2656,11 +2681,11 @@ bool Codegen::visit(PreDecrementExpression *ast)
bool Codegen::visit(PreIncrementExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
Reference expr = expression(ast->expression);
- if (hasError)
+ if (hasError())
return false;
if (!expr.isLValue()) {
throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference."));
@@ -2675,7 +2700,7 @@ bool Codegen::visit(PreIncrementExpression *ast)
bool Codegen::visit(RegExpLiteral *ast)
{
- if (hasError)
+ if (hasError())
return false;
auto r = Reference::fromStackSlot(this);
@@ -2691,7 +2716,7 @@ bool Codegen::visit(RegExpLiteral *ast)
bool Codegen::visit(StringLiteral *ast)
{
- if (hasError)
+ if (hasError())
return false;
auto r = Reference::fromAccumulator(this);
@@ -2706,7 +2731,7 @@ bool Codegen::visit(StringLiteral *ast)
bool Codegen::visit(TemplateLiteral *ast)
{
- if (hasError)
+ if (hasError())
return false;
TailCallBlocker blockTailCalls(this);
@@ -2723,7 +2748,7 @@ bool Codegen::visit(TemplateLiteral *ast)
bytecodeGenerator->addInstruction(store);
Reference expr = expression(ast->expression);
- if (hasError)
+ if (hasError())
return false;
if (ast->next) {
@@ -2733,14 +2758,14 @@ bool Codegen::visit(TemplateLiteral *ast)
Instruction::Add instr;
instr.lhs = temp2;
- bytecodeGenerator->addTracingInstruction(instr);
+ bytecodeGenerator->addInstruction(instr);
} else {
expr.loadInAccumulator();
}
Instruction::Add instr;
instr.lhs = temp;
- bytecodeGenerator->addTracingInstruction(instr);
+ bytecodeGenerator->addInstruction(instr);
}
auto r = Reference::fromAccumulator(this);
@@ -2753,7 +2778,7 @@ bool Codegen::visit(TemplateLiteral *ast)
bool Codegen::visit(ThisExpression *)
{
- if (hasError)
+ if (hasError())
return false;
if (_context->isArrowFunction) {
@@ -2768,7 +2793,7 @@ bool Codegen::visit(ThisExpression *)
bool Codegen::visit(TildeExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
TailCallBlocker blockTailCalls(this);
@@ -2778,7 +2803,7 @@ bool Codegen::visit(TildeExpression *ast)
bool Codegen::visit(TrueLiteral *)
{
- if (hasError)
+ if (hasError())
return false;
setExprResult(Reference::fromConst(this, QV4::Encode(true)));
@@ -2787,14 +2812,14 @@ bool Codegen::visit(TrueLiteral *)
bool Codegen::visit(TypeOfExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
RegisterScope scope(this);
TailCallBlocker blockTailCalls(this);
Reference expr = expression(ast->expression);
- if (hasError)
+ if (hasError())
return false;
if (expr.type == Reference::Name) {
@@ -2814,7 +2839,7 @@ bool Codegen::visit(TypeOfExpression *ast)
bool Codegen::visit(UnaryMinusExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
TailCallBlocker blockTailCalls(this);
@@ -2824,7 +2849,7 @@ bool Codegen::visit(UnaryMinusExpression *ast)
bool Codegen::visit(UnaryPlusExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
TailCallBlocker blockTailCalls(this);
@@ -2834,7 +2859,7 @@ bool Codegen::visit(UnaryPlusExpression *ast)
bool Codegen::visit(VoidExpression *ast)
{
- if (hasError)
+ if (hasError())
return false;
RegisterScope scope(this);
@@ -2847,7 +2872,7 @@ bool Codegen::visit(VoidExpression *ast)
bool Codegen::visit(FunctionDeclaration * ast)
{
- if (hasError)
+ if (hasError())
return false;
// no need to block tail calls: the function body isn't visited here.
@@ -2869,7 +2894,7 @@ bool Codegen::visit(YieldExpression *ast)
RegisterScope scope(this);
TailCallBlocker blockTailCalls(this);
Reference expr = ast->expression ? expression(ast->expression) : Reference::fromConst(this, Encode::undefined());
- if (hasError)
+ if (hasError())
return false;
Reference acc = Reference::fromAccumulator(this);
@@ -2998,10 +3023,10 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
bool savedFunctionEndsWithReturn = functionEndsWithReturn;
functionEndsWithReturn = endsWithReturn(_module, body);
- bytecodeGenerator->setTracing(_functionContext->canUseTracingJit(), _context->arguments.size());
// reserve the js stack frame (Context & js Function & accumulator)
- bytecodeGenerator->newRegisterArray(sizeof(CallData)/sizeof(Value) - 1 + _context->arguments.size());
+ bytecodeGenerator->newRegisterArray(
+ sizeof(CallData) / sizeof(StaticValue) - 1 + _context->arguments.size());
bool _inFormalParameterList = false;
qSwap(_inFormalParameterList, inFormalParameterList);
@@ -3047,7 +3072,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
} else {
if (e->bindingTarget || e->initializer) {
initializeAndDestructureBindingElement(e, arg);
- if (hasError)
+ if (hasError())
break;
}
}
@@ -3063,7 +3088,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
statementList(body);
- if (!hasError) {
+ if (!hasError()) {
bytecodeGenerator->setLocation(ast->lastSourceLocation());
_context->emitBlockFooter(this);
@@ -3085,7 +3110,6 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
Q_ASSERT(_context == _functionContext);
bytecodeGenerator->finalize(_context);
_context->registerCountInFunction = bytecodeGenerator->registerCount();
- _context->nTraceInfos = bytecodeGenerator->traceInfoCount();
static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
if (showCode) {
qDebug() << "=== Bytecode for" << _context->name << "strict mode" << _context->isStrict
@@ -3111,7 +3135,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
bool Codegen::visit(Block *ast)
{
- if (hasError)
+ if (hasError())
return false;
RegisterScope scope(this);
@@ -3123,7 +3147,7 @@ bool Codegen::visit(Block *ast)
bool Codegen::visit(BreakStatement *ast)
{
- if (hasError)
+ if (hasError())
return false;
// no need to block tail calls here: children aren't visited
@@ -3148,7 +3172,7 @@ bool Codegen::visit(BreakStatement *ast)
bool Codegen::visit(ContinueStatement *ast)
{
- if (hasError)
+ if (hasError())
return false;
// no need to block tail calls here: children aren't visited
@@ -3181,7 +3205,7 @@ bool Codegen::visit(DebuggerStatement *)
bool Codegen::visit(DoWhileStatement *ast)
{
- if (hasError)
+ if (hasError())
return false;
RegisterScope scope(this);
@@ -3191,22 +3215,28 @@ bool Codegen::visit(DoWhileStatement *ast)
BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
ControlFlowLoop flow(this, &end, &cond);
- bytecodeGenerator->jump().link(body);
-
- cond.link();
- bytecodeGenerator->addLoopStart(cond);
- if (!AST::cast<TrueLiteral *>(ast->expression)) {
- TailCallBlocker blockTailCalls(this);
- condition(ast->expression, &body, &end, true);
- }
+ // special case that is not a loop:
+ // do {...} while (false)
+ if (!AST::cast<FalseLiteral *>(ast->expression))
+ bytecodeGenerator->addLoopStart(body);
body.link();
statement(ast->statement);
setJumpOutLocation(bytecodeGenerator, ast->statement, ast->semicolonToken);
- if (!AST::cast<FalseLiteral *>(ast->expression))
- bytecodeGenerator->jump().link(cond);
+ cond.link();
+ if (AST::cast<TrueLiteral *>(ast->expression)) {
+ // do {} while (true) -> just jump back to the loop body, no need to generate a condition
+ bytecodeGenerator->checkException();
+ bytecodeGenerator->jump().link(body);
+ } else if (AST::cast<FalseLiteral *>(ast->expression)) {
+ // do {} while (false) -> fall through, no need to generate a condition
+ } else {
+ TailCallBlocker blockTailCalls(this);
+ bytecodeGenerator->checkException();
+ condition(ast->expression, &body, &end, false);
+ }
end.link();
@@ -3220,7 +3250,7 @@ bool Codegen::visit(EmptyStatement *)
bool Codegen::visit(ExpressionStatement *ast)
{
- if (hasError)
+ if (hasError())
return false;
RegisterScope scope(this);
@@ -3228,7 +3258,7 @@ bool Codegen::visit(ExpressionStatement *ast)
if (requiresReturnValue) {
Reference e = expression(ast->expression);
- if (hasError)
+ if (hasError())
return false;
(void) e.storeOnStack(_returnAddress);
} else {
@@ -3239,7 +3269,7 @@ bool Codegen::visit(ExpressionStatement *ast)
bool Codegen::visit(ForEachStatement *ast)
{
- if (hasError)
+ if (hasError())
return false;
RegisterScope scope(this);
@@ -3255,7 +3285,7 @@ bool Codegen::visit(ForEachStatement *ast)
RegisterScope innerScope(this);
ControlFlowBlock controlFlow(this, ast);
Reference expr = expression(ast->expression);
- if (hasError)
+ if (hasError())
return false;
expr.loadInAccumulator();
@@ -3285,7 +3315,7 @@ bool Codegen::visit(ForEachStatement *ast)
next.value = lhsValue.stackSlot();
next.done = iteratorDone.stackSlot();
bytecodeGenerator->addInstruction(next);
- bytecodeGenerator->addTracingJumpInstruction(Instruction::JumpTrue()).link(end);
+ bytecodeGenerator->addJumpInstruction(Instruction::JumpTrue()).link(end);
// each iteration gets it's own context, as per spec
{
@@ -3298,7 +3328,7 @@ bool Codegen::visit(ForEachStatement *ast)
destructurePattern(p, lhsValue);
} else {
Reference lhs = expression(e);
- if (hasError)
+ if (hasError())
goto error;
if (!lhs.isLValue()) {
throwReferenceError(e->firstSourceLocation(), QStringLiteral("Invalid left-hand side expression for 'in' expression"));
@@ -3310,7 +3340,7 @@ bool Codegen::visit(ForEachStatement *ast)
}
} else if (PatternElement *p = AST::cast<PatternElement *>(ast->lhs)) {
initializeAndDestructureBindingElement(p, lhsValue, /*isDefinition =*/ true);
- if (hasError)
+ if (hasError())
goto error;
} else {
Q_UNREACHABLE();
@@ -3321,6 +3351,7 @@ bool Codegen::visit(ForEachStatement *ast)
setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
}
+ bytecodeGenerator->checkException();
bytecodeGenerator->jump().link(in);
error:
@@ -3335,7 +3366,7 @@ bool Codegen::visit(ForEachStatement *ast)
bool Codegen::visit(ForStatement *ast)
{
- if (hasError)
+ if (hasError())
return false;
RegisterScope scope(this);
@@ -3369,6 +3400,7 @@ bool Codegen::visit(ForStatement *ast)
bytecodeGenerator->addInstruction(clone);
}
statement(ast->expression);
+ bytecodeGenerator->checkException();
bytecodeGenerator->jump().link(cond);
end.link();
@@ -3378,7 +3410,7 @@ bool Codegen::visit(ForStatement *ast)
bool Codegen::visit(IfStatement *ast)
{
- if (hasError)
+ if (hasError())
return false;
RegisterScope scope(this);
@@ -3410,7 +3442,7 @@ bool Codegen::visit(IfStatement *ast)
bool Codegen::visit(LabelledStatement *ast)
{
- if (hasError)
+ if (hasError())
return false;
RegisterScope scope(this);
@@ -3458,7 +3490,7 @@ void Codegen::emitReturn(const Reference &expr)
bool Codegen::visit(ReturnStatement *ast)
{
- if (hasError)
+ if (hasError())
return false;
if (_functionContext->contextType != ContextType::Function && _functionContext->contextType != ContextType::Binding) {
@@ -3468,7 +3500,7 @@ bool Codegen::visit(ReturnStatement *ast)
Reference expr;
if (ast->expression) {
expr = expression(ast->expression);
- if (hasError)
+ if (hasError())
return false;
} else {
expr = Reference::fromConst(this, Encode::undefined());
@@ -3481,7 +3513,7 @@ bool Codegen::visit(ReturnStatement *ast)
bool Codegen::visit(SwitchStatement *ast)
{
- if (hasError)
+ if (hasError())
return false;
if (requiresReturnValue)
@@ -3494,7 +3526,7 @@ bool Codegen::visit(SwitchStatement *ast)
BytecodeGenerator::Label switchEnd = bytecodeGenerator->newLabel();
Reference lhs = expression(ast->expression);
- if (hasError)
+ if (hasError())
return false;
lhs = lhs.storeOnStack();
@@ -3513,7 +3545,7 @@ bool Codegen::visit(SwitchStatement *ast)
for (CaseClauses *it = ast->block->clauses; it; it = it->next) {
CaseClause *clause = it->clause;
Reference rhs = expression(clause->expression);
- if (hasError)
+ if (hasError())
return false;
rhs.loadInAccumulator();
bytecodeGenerator->jumpStrictEqual(lhs.stackSlot(), blockMap.value(clause));
@@ -3522,7 +3554,7 @@ bool Codegen::visit(SwitchStatement *ast)
for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) {
CaseClause *clause = it->clause;
Reference rhs = expression(clause->expression);
- if (hasError)
+ if (hasError())
return false;
rhs.loadInAccumulator();
bytecodeGenerator->jumpStrictEqual(lhs.stackSlot(), blockMap.value(clause));
@@ -3568,14 +3600,14 @@ bool Codegen::visit(SwitchStatement *ast)
bool Codegen::visit(ThrowStatement *ast)
{
- if (hasError)
+ if (hasError())
return false;
RegisterScope scope(this);
TailCallBlocker blockTailCalls(this);
Reference expr = expression(ast->expression);
- if (hasError)
+ if (hasError())
return false;
expr.loadInAccumulator();
@@ -3612,7 +3644,7 @@ void Codegen::handleTryFinally(TryStatement *ast)
bool Codegen::visit(TryStatement *ast)
{
- if (hasError)
+ if (hasError())
return false;
RegisterScope scope(this);
@@ -3628,7 +3660,7 @@ bool Codegen::visit(TryStatement *ast)
bool Codegen::visit(VariableStatement *ast)
{
- if (hasError)
+ if (hasError())
return false;
variableDeclarationList(ast->declarations);
@@ -3637,7 +3669,7 @@ bool Codegen::visit(VariableStatement *ast)
bool Codegen::visit(WhileStatement *ast)
{
- if (hasError)
+ if (hasError())
return false;
if (AST::cast<FalseLiteral *>(ast->expression))
@@ -3651,6 +3683,8 @@ bool Codegen::visit(WhileStatement *ast)
ControlFlowLoop flow(this, &end, &cond);
bytecodeGenerator->addLoopStart(cond);
+ bytecodeGenerator->checkException();
+
if (!AST::cast<TrueLiteral *>(ast->expression)) {
TailCallBlocker blockTailCalls(this);
condition(ast->expression, &start, &end, true);
@@ -3667,14 +3701,14 @@ bool Codegen::visit(WhileStatement *ast)
bool Codegen::visit(WithStatement *ast)
{
- if (hasError)
+ if (hasError())
return false;
RegisterScope scope(this);
TailCallBlocker blockTailCalls(this);
Reference src = expression(ast->expression);
- if (hasError)
+ if (hasError())
return false;
src = src.storeOnStack(); // trigger load before we setup the exception handler, so exceptions here go to the right place
src.loadInAccumulator();
@@ -3744,52 +3778,79 @@ bool Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r,
return isArgOrEval;
}
-void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail)
+void Codegen::throwError(ErrorType errorType, const SourceLocation &loc, const QString &detail)
{
- if (hasError)
+ if (hasError())
return;
- hasError = true;
- QQmlJS::DiagnosticMessage error;
- error.message = detail;
- error.loc = loc;
- _errors << error;
+ _errorType = errorType;
+ _error.message = detail;
+ _error.line = loc.startLine;
+ _error.column = loc.startColumn;
}
-void Codegen::throwReferenceError(const SourceLocation &loc, const QString &detail)
+void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail)
{
- if (hasError)
- return;
-
- hasError = true;
- QQmlJS::DiagnosticMessage error;
- error.message = detail;
- error.loc = loc;
- _errors << error;
+ throwError(SyntaxError, loc, detail);
}
-QList<QQmlJS::DiagnosticMessage> Codegen::errors() const
+void Codegen::throwReferenceError(const SourceLocation &loc, const QString &detail)
{
- return _errors;
+ throwError(ReferenceError, loc, detail);
}
-QQmlRefPointer<CompiledData::CompilationUnit> Codegen::generateCompilationUnit(bool generateUnitData)
+QQmlJS::DiagnosticMessage Codegen::error() const
{
- CompiledData::Unit *unitData = nullptr;
- if (generateUnitData)
- unitData = jsUnitGenerator->generateUnit();
- CompiledData::CompilationUnit *compilationUnit = new CompiledData::CompilationUnit(unitData);
+ return _error;
+}
- QQmlRefPointer<CompiledData::CompilationUnit> unit;
- unit.adopt(compilationUnit);
- return unit;
+QV4::CompiledData::CompilationUnit Codegen::generateCompilationUnit(
+ bool generateUnitData)
+{
+ return QV4::CompiledData::CompilationUnit(
+ generateUnitData ? jsUnitGenerator->generateUnit() : nullptr);
}
-QQmlRefPointer<CompiledData::CompilationUnit> Codegen::createUnitForLoading()
+CompiledData::CompilationUnit Codegen::compileModule(
+ bool debugMode, const QString &url, const QString &sourceCode,
+ const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics)
{
- QQmlRefPointer<CompiledData::CompilationUnit> result;
- result.adopt(new CompiledData::CompilationUnit);
- return result;
+ QQmlJS::Engine ee;
+ QQmlJS::Lexer lexer(&ee);
+ lexer.setCode(sourceCode, /*line*/1, /*qml mode*/false);
+ QQmlJS::Parser parser(&ee);
+
+ const bool parsed = parser.parseModule();
+
+ if (diagnostics)
+ *diagnostics = parser.diagnosticMessages();
+
+ if (!parsed)
+ return CompiledData::CompilationUnit();
+
+ QQmlJS::AST::ESModule *moduleNode = QQmlJS::AST::cast<QQmlJS::AST::ESModule*>(parser.rootNode());
+ if (!moduleNode) {
+ // if parsing was successful, and we have no module, then
+ // the file was empty.
+ if (diagnostics)
+ diagnostics->clear();
+ return nullptr;
+ }
+
+ using namespace QV4::Compiler;
+ Compiler::Module compilerModule(debugMode);
+ compilerModule.unitFlags |= CompiledData::Unit::IsESModule;
+ compilerModule.sourceTimeStamp = sourceTimeStamp;
+ JSUnitGenerator jsGenerator(&compilerModule);
+ Codegen cg(&jsGenerator, /*strictMode*/true);
+ cg.generateFromModule(url, url, sourceCode, moduleNode, &compilerModule);
+ if (cg.hasError()) {
+ if (diagnostics)
+ *diagnostics << cg.error();
+ return CompiledData::CompilationUnit();
+ }
+
+ return cg.generateCompilationUnit();
}
class Codegen::VolatileMemoryLocationScanner: protected QQmlJS::AST::Visitor
@@ -3905,34 +3966,11 @@ Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node
return scanner.scan(ast);
}
-
-#ifndef V4_BOOTSTRAP
-
-QList<QQmlError> Codegen::qmlErrors() const
+QUrl Codegen::url() const
{
- QList<QQmlError> qmlErrors;
-
- // Short circuit to avoid costly (de)heap allocation of QUrl if there are no errors.
- if (_errors.size() == 0)
- return qmlErrors;
-
- qmlErrors.reserve(_errors.size());
-
- QUrl url(_fileNameIsUrl ? QUrl(_module->fileName) : QUrl::fromLocalFile(_module->fileName));
- for (const QQmlJS::DiagnosticMessage &msg: qAsConst(_errors)) {
- QQmlError e;
- e.setUrl(url);
- e.setLine(msg.loc.startLine);
- e.setColumn(msg.loc.startColumn);
- e.setDescription(msg.message);
- qmlErrors << e;
- }
-
- return qmlErrors;
+ return QUrl(_fileNameIsUrl ? QUrl(_module->fileName) : QUrl::fromLocalFile(_module->fileName));
}
-#endif // V4_BOOTSTRAP
-
bool Codegen::RValue::operator==(const RValue &other) const
{
switch (type) {
@@ -4222,7 +4260,7 @@ void Codegen::Reference::storeAccumulator() const
Instruction::StoreElement store;
store.base = elementBase;
store.index = elementSubscript.stackSlot();
- codegen->bytecodeGenerator->addTracingInstruction(store);
+ codegen->bytecodeGenerator->addInstruction(store);
} return;
case Invalid:
case Accumulator:
@@ -4280,7 +4318,7 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty str
Instruction::LoadUndefined load;
codegen->bytecodeGenerator->addInstruction(load);
} else {
- Value p = Value::fromReturnedValue(constant);
+ StaticValue p = StaticValue::fromReturnedValue(constant);
if (p.isNumber()) {
double d = p.asDouble();
int i = static_cast<int>(d);
@@ -4291,7 +4329,7 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty str
return;
}
Instruction::LoadInt load;
- load.value = Value::fromReturnedValue(constant).toInt32();
+ load.value = StaticValue::fromReturnedValue(constant).toInt32();
codegen->bytecodeGenerator->addInstruction(load);
return;
}
@@ -4312,12 +4350,12 @@ QT_WARNING_POP
if (!scope) {
Instruction::LoadLocal load;
load.index = index;
- codegen->bytecodeGenerator->addTracingInstruction(load);
+ codegen->bytecodeGenerator->addInstruction(load);
} else {
Instruction::LoadScopedLocal load;
load.index = index;
load.scope = scope;
- codegen->bytecodeGenerator->addTracingInstruction(load);
+ codegen->bytecodeGenerator->addInstruction(load);
}
tdzCheck(requiresTDZCheck);
return;
@@ -4341,16 +4379,16 @@ QT_WARNING_POP
if (qmlGlobal) {
Instruction::LoadQmlContextPropertyLookup load;
load.index = codegen->registerQmlContextPropertyGetterLookup(nameAsIndex());
- codegen->bytecodeGenerator->addTracingInstruction(load);
+ codegen->bytecodeGenerator->addInstruction(load);
} else {
Instruction::LoadGlobalLookup load;
load.index = codegen->registerGlobalGetterLookup(nameAsIndex());
- codegen->bytecodeGenerator->addTracingInstruction(load);
+ codegen->bytecodeGenerator->addInstruction(load);
}
} else {
Instruction::LoadName load;
load.name = nameAsIndex();
- codegen->bytecodeGenerator->addTracingInstruction(load);
+ codegen->bytecodeGenerator->addInstruction(load);
}
return;
case Member:
@@ -4359,11 +4397,11 @@ QT_WARNING_POP
if (!disable_lookups && codegen->useFastLookups) {
Instruction::GetLookup load;
load.index = codegen->registerGetterLookup(propertyNameIndex);
- codegen->bytecodeGenerator->addTracingInstruction(load);
+ codegen->bytecodeGenerator->addInstruction(load);
} else {
Instruction::LoadProperty load;
load.name = propertyNameIndex;
- codegen->bytecodeGenerator->addTracingInstruction(load);
+ codegen->bytecodeGenerator->addInstruction(load);
}
return;
case Import: {
@@ -4378,7 +4416,7 @@ QT_WARNING_POP
tdzCheck(subscriptRequiresTDZCheck);
Instruction::LoadElement load;
load.base = elementBase;
- codegen->bytecodeGenerator->addTracingInstruction(load);
+ codegen->bytecodeGenerator->addInstruction(load);
} return;
case Invalid:
break;
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index 958dd16816..82a4fc3289 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -50,19 +50,18 @@
// We mean it.
//
-#include "private/qv4global_p.h"
#include <private/qqmljsastvisitor_p.h>
+#include <private/qqmljsengine_p.h>
#include <private/qqmljsast_p.h>
+#include <private/qqmljsdiagnosticmessage_p.h>
#include <private/qv4compiler_p.h>
#include <private/qv4compilercontext_p.h>
#include <private/qv4util_p.h>
#include <private/qv4bytecodegenerator_p.h>
-#include <private/qv4stackframe_p.h>
+#include <private/qv4calldata_p.h>
QT_BEGIN_NAMESPACE
-using namespace QQmlJS;
-
namespace QV4 {
namespace Moth {
@@ -79,7 +78,7 @@ struct ControlFlow;
struct ControlFlowCatch;
struct ControlFlowFinally;
-class Q_QML_PRIVATE_EXPORT Codegen: protected QQmlJS::AST::Visitor
+class Q_QMLCOMPILER_PRIVATE_EXPORT Codegen: protected QQmlJS::AST::Visitor
{
protected:
using BytecodeGenerator = QV4::Moth::BytecodeGenerator;
@@ -91,14 +90,14 @@ public:
void generateFromProgram(const QString &fileName,
const QString &finalUrl,
const QString &sourceCode,
- AST::Program *ast,
+ QQmlJS::AST::Program *ast,
Module *module,
ContextType contextType = ContextType::Global);
void generateFromModule(const QString &fileName,
const QString &finalUrl,
const QString &sourceCode,
- AST::ESModule *ast,
+ QQmlJS::AST::ESModule *ast,
Module *module);
public:
@@ -256,7 +255,8 @@ public:
}
static Reference fromArgument(Codegen *cg, int index, bool isVolatile) {
Reference r(cg, StackSlot);
- r.theStackSlot = Moth::StackSlot::createRegister(index + sizeof(CallData)/sizeof(Value) - 1);
+ r.theStackSlot = Moth::StackSlot::createRegister(
+ index + sizeof(CallData) / sizeof(StaticValue) - 1);
r.stackSlotIsLocalOrArgument = true;
r.isVolatile = isVolatile;
return r;
@@ -484,10 +484,10 @@ protected:
}
};
- void enterContext(AST::Node *node);
+ void enterContext(QQmlJS::AST::Node *node);
int leaveContext();
public:
- Context *enterBlock(AST::Node *node);
+ Context *enterBlock(QQmlJS::AST::Node *node);
int leaveBlock() { return leaveContext(); }
protected:
void leaveLoop();
@@ -518,20 +518,20 @@ public:
int registerQmlContextPropertyGetterLookup(int nameIndex) { return jsUnitGenerator->registerQmlContextPropertyGetterLookup(nameIndex); }
// Returns index in _module->functions
- virtual int defineFunction(const QString &name, AST::Node *ast,
- AST::FormalParameterList *formals,
- AST::StatementList *body);
+ virtual int defineFunction(const QString &name, QQmlJS::AST::Node *ast,
+ QQmlJS::AST::FormalParameterList *formals,
+ QQmlJS::AST::StatementList *body);
protected:
- void statement(AST::Statement *ast);
- void statement(AST::ExpressionNode *ast);
- void condition(AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue,
+ void statement(QQmlJS::AST::Statement *ast);
+ void statement(QQmlJS::AST::ExpressionNode *ast);
+ void condition(QQmlJS::AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue,
const BytecodeGenerator::Label *iffalse,
bool trueBlockFollowsCondition);
- inline Reference expression(AST::ExpressionNode *ast, const QString &name = QString())
+ inline Reference expression(QQmlJS::AST::ExpressionNode *ast, const QString &name = QString())
{
- if (!ast || hasError)
+ if (!ast || hasError())
return Reference();
pushExpr(name);
@@ -539,160 +539,173 @@ protected:
return popResult();
}
- inline void accept(AST::Node *node)
+ inline void accept(QQmlJS::AST::Node *node)
{
- if (!hasError && node)
+ if (!hasError() && node)
node->accept(this);
}
- void program(AST::Program *ast);
- void statementList(AST::StatementList *ast);
- void variableDeclaration(AST::PatternElement *ast);
- void variableDeclarationList(AST::VariableDeclarationList *ast);
+ void program(QQmlJS::AST::Program *ast);
+ void statementList(QQmlJS::AST::StatementList *ast);
+ void variableDeclaration(QQmlJS::AST::PatternElement *ast);
+ void variableDeclarationList(QQmlJS::AST::VariableDeclarationList *ast);
- Reference targetForPatternElement(AST::PatternElement *p);
- void initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &baseRef = Reference(), bool isDefinition = false);
- void destructurePropertyList(const Reference &object, AST::PatternPropertyList *bindingList, bool isDefinition = false);
- void destructureElementList(const Reference &array, AST::PatternElementList *bindingList, bool isDefinition = false);
- void destructurePattern(AST::Pattern *p, const Reference &rhs);
+ Reference targetForPatternElement(QQmlJS::AST::PatternElement *p);
+ void initializeAndDestructureBindingElement(QQmlJS::AST::PatternElement *e, const Reference &baseRef = Reference(), bool isDefinition = false);
+ void destructurePropertyList(const Reference &object, QQmlJS::AST::PatternPropertyList *bindingList, bool isDefinition = false);
+ void destructureElementList(const Reference &array, QQmlJS::AST::PatternElementList *bindingList, bool isDefinition = false);
+ void destructurePattern(QQmlJS::AST::Pattern *p, const Reference &rhs);
- Reference referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name);
+ Reference referenceForPropertyName(const Codegen::Reference &object, QQmlJS::AST::PropertyName *name);
void emitReturn(const Reference &expr);
// nodes
- bool visit(AST::ArgumentList *ast) override;
- bool visit(AST::CaseBlock *ast) override;
- bool visit(AST::CaseClause *ast) override;
- bool visit(AST::CaseClauses *ast) override;
- bool visit(AST::Catch *ast) override;
- bool visit(AST::DefaultClause *ast) override;
- bool visit(AST::Elision *ast) override;
- bool visit(AST::Finally *ast) override;
- bool visit(AST::FormalParameterList *ast) override;
- bool visit(AST::Program *ast) override;
- bool visit(AST::StatementList *ast) override;
- bool visit(AST::UiArrayMemberList *ast) override;
- bool visit(AST::UiImport *ast) override;
- bool visit(AST::UiHeaderItemList *ast) override;
- bool visit(AST::UiPragma *ast) override;
- bool visit(AST::UiObjectInitializer *ast) override;
- bool visit(AST::UiObjectMemberList *ast) override;
- bool visit(AST::UiParameterList *ast) override;
- bool visit(AST::UiProgram *ast) override;
- bool visit(AST::UiQualifiedId *ast) override;
- bool visit(AST::VariableDeclarationList *ast) override;
-
- bool visit(AST::PatternElement *ast) override;
- bool visit(AST::PatternElementList *ast) override;
- bool visit(AST::PatternProperty *ast) override;
- bool visit(AST::PatternPropertyList *ast) override;
-
- bool visit(AST::ExportDeclaration *ast) override;
+ bool visit(QQmlJS::AST::ArgumentList *ast) override;
+ bool visit(QQmlJS::AST::CaseBlock *ast) override;
+ bool visit(QQmlJS::AST::CaseClause *ast) override;
+ bool visit(QQmlJS::AST::CaseClauses *ast) override;
+ bool visit(QQmlJS::AST::Catch *ast) override;
+ bool visit(QQmlJS::AST::DefaultClause *ast) override;
+ bool visit(QQmlJS::AST::Elision *ast) override;
+ bool visit(QQmlJS::AST::Finally *ast) override;
+ bool visit(QQmlJS::AST::FormalParameterList *ast) override;
+ bool visit(QQmlJS::AST::Program *ast) override;
+ bool visit(QQmlJS::AST::StatementList *ast) override;
+ bool visit(QQmlJS::AST::UiArrayMemberList *ast) override;
+ bool visit(QQmlJS::AST::UiImport *ast) override;
+ bool visit(QQmlJS::AST::UiHeaderItemList *ast) override;
+ bool visit(QQmlJS::AST::UiPragma *ast) override;
+ bool visit(QQmlJS::AST::UiObjectInitializer *ast) override;
+ bool visit(QQmlJS::AST::UiObjectMemberList *ast) override;
+ bool visit(QQmlJS::AST::UiParameterList *ast) override;
+ bool visit(QQmlJS::AST::UiProgram *ast) override;
+ bool visit(QQmlJS::AST::UiQualifiedId *ast) override;
+ bool visit(QQmlJS::AST::VariableDeclarationList *ast) override;
+
+ bool visit(QQmlJS::AST::PatternElement *ast) override;
+ bool visit(QQmlJS::AST::PatternElementList *ast) override;
+ bool visit(QQmlJS::AST::PatternProperty *ast) override;
+ bool visit(QQmlJS::AST::PatternPropertyList *ast) override;
+
+ bool visit(QQmlJS::AST::ExportDeclaration *ast) override;
+
+ bool visit(QQmlJS::AST::TypeAnnotation *ast) override;
// expressions
- bool visit(AST::Expression *ast) override;
- bool visit(AST::ArrayPattern *ast) override;
- bool visit(AST::ArrayMemberExpression *ast) override;
- bool visit(AST::BinaryExpression *ast) override;
- bool visit(AST::CallExpression *ast) override;
- bool visit(AST::ConditionalExpression *ast) override;
- bool visit(AST::DeleteExpression *ast) override;
- bool visit(AST::FalseLiteral *ast) override;
- bool visit(AST::SuperLiteral *ast) override;
- bool visit(AST::FieldMemberExpression *ast) override;
- bool visit(AST::TaggedTemplate *ast) override;
- bool visit(AST::FunctionExpression *ast) override;
- bool visit(AST::IdentifierExpression *ast) override;
- bool visit(AST::NestedExpression *ast) override;
- bool visit(AST::NewExpression *ast) override;
- bool visit(AST::NewMemberExpression *ast) override;
- bool visit(AST::NotExpression *ast) override;
- bool visit(AST::NullExpression *ast) override;
- bool visit(AST::NumericLiteral *ast) override;
- bool visit(AST::ObjectPattern *ast) override;
- bool visit(AST::PostDecrementExpression *ast) override;
- bool visit(AST::PostIncrementExpression *ast) override;
- bool visit(AST::PreDecrementExpression *ast) override;
- bool visit(AST::PreIncrementExpression *ast) override;
- bool visit(AST::RegExpLiteral *ast) override;
- bool visit(AST::StringLiteral *ast) override;
- bool visit(AST::TemplateLiteral *ast) override;
- bool visit(AST::ThisExpression *ast) override;
- bool visit(AST::TildeExpression *ast) override;
- bool visit(AST::TrueLiteral *ast) override;
- bool visit(AST::TypeOfExpression *ast) override;
- bool visit(AST::UnaryMinusExpression *ast) override;
- bool visit(AST::UnaryPlusExpression *ast) override;
- bool visit(AST::VoidExpression *ast) override;
- bool visit(AST::FunctionDeclaration *ast) override;
- bool visit(AST::YieldExpression *ast) override;
- bool visit(AST::ClassExpression *ast) override;
- bool visit(AST::ClassDeclaration *ast) override;
+ bool visit(QQmlJS::AST::Expression *ast) override;
+ bool visit(QQmlJS::AST::ArrayPattern *ast) override;
+ bool visit(QQmlJS::AST::ArrayMemberExpression *ast) override;
+ bool visit(QQmlJS::AST::BinaryExpression *ast) override;
+ bool visit(QQmlJS::AST::CallExpression *ast) override;
+ bool visit(QQmlJS::AST::ConditionalExpression *ast) override;
+ bool visit(QQmlJS::AST::DeleteExpression *ast) override;
+ bool visit(QQmlJS::AST::FalseLiteral *ast) override;
+ bool visit(QQmlJS::AST::SuperLiteral *ast) override;
+ bool visit(QQmlJS::AST::FieldMemberExpression *ast) override;
+ bool visit(QQmlJS::AST::TaggedTemplate *ast) override;
+ bool visit(QQmlJS::AST::FunctionExpression *ast) override;
+ bool visit(QQmlJS::AST::IdentifierExpression *ast) override;
+ bool visit(QQmlJS::AST::NestedExpression *ast) override;
+ bool visit(QQmlJS::AST::NewExpression *ast) override;
+ bool visit(QQmlJS::AST::NewMemberExpression *ast) override;
+ bool visit(QQmlJS::AST::NotExpression *ast) override;
+ bool visit(QQmlJS::AST::NullExpression *ast) override;
+ bool visit(QQmlJS::AST::NumericLiteral *ast) override;
+ bool visit(QQmlJS::AST::ObjectPattern *ast) override;
+ bool visit(QQmlJS::AST::PostDecrementExpression *ast) override;
+ bool visit(QQmlJS::AST::PostIncrementExpression *ast) override;
+ bool visit(QQmlJS::AST::PreDecrementExpression *ast) override;
+ bool visit(QQmlJS::AST::PreIncrementExpression *ast) override;
+ bool visit(QQmlJS::AST::RegExpLiteral *ast) override;
+ bool visit(QQmlJS::AST::StringLiteral *ast) override;
+ bool visit(QQmlJS::AST::TemplateLiteral *ast) override;
+ bool visit(QQmlJS::AST::ThisExpression *ast) override;
+ bool visit(QQmlJS::AST::TildeExpression *ast) override;
+ bool visit(QQmlJS::AST::TrueLiteral *ast) override;
+ bool visit(QQmlJS::AST::TypeOfExpression *ast) override;
+ bool visit(QQmlJS::AST::UnaryMinusExpression *ast) override;
+ bool visit(QQmlJS::AST::UnaryPlusExpression *ast) override;
+ bool visit(QQmlJS::AST::VoidExpression *ast) override;
+ bool visit(QQmlJS::AST::FunctionDeclaration *ast) override;
+ bool visit(QQmlJS::AST::YieldExpression *ast) override;
+ bool visit(QQmlJS::AST::ClassExpression *ast) override;
+ bool visit(QQmlJS::AST::ClassDeclaration *ast) override;
// statements
- bool visit(AST::Block *ast) override;
- bool visit(AST::BreakStatement *ast) override;
- bool visit(AST::ContinueStatement *ast) override;
- bool visit(AST::DebuggerStatement *ast) override;
- bool visit(AST::DoWhileStatement *ast) override;
- bool visit(AST::EmptyStatement *ast) override;
- bool visit(AST::ExpressionStatement *ast) override;
- bool visit(AST::ForEachStatement *ast) override;
- bool visit(AST::ForStatement *ast) override;
- bool visit(AST::IfStatement *ast) override;
- bool visit(AST::LabelledStatement *ast) override;
- bool visit(AST::ReturnStatement *ast) override;
- bool visit(AST::SwitchStatement *ast) override;
- bool visit(AST::ThrowStatement *ast) override;
- bool visit(AST::TryStatement *ast) override;
- bool visit(AST::VariableStatement *ast) override;
- bool visit(AST::WhileStatement *ast) override;
- bool visit(AST::WithStatement *ast) override;
+ bool visit(QQmlJS::AST::Block *ast) override;
+ bool visit(QQmlJS::AST::BreakStatement *ast) override;
+ bool visit(QQmlJS::AST::ContinueStatement *ast) override;
+ bool visit(QQmlJS::AST::DebuggerStatement *ast) override;
+ bool visit(QQmlJS::AST::DoWhileStatement *ast) override;
+ bool visit(QQmlJS::AST::EmptyStatement *ast) override;
+ bool visit(QQmlJS::AST::ExpressionStatement *ast) override;
+ bool visit(QQmlJS::AST::ForEachStatement *ast) override;
+ bool visit(QQmlJS::AST::ForStatement *ast) override;
+ bool visit(QQmlJS::AST::IfStatement *ast) override;
+ bool visit(QQmlJS::AST::LabelledStatement *ast) override;
+ bool visit(QQmlJS::AST::ReturnStatement *ast) override;
+ bool visit(QQmlJS::AST::SwitchStatement *ast) override;
+ bool visit(QQmlJS::AST::ThrowStatement *ast) override;
+ bool visit(QQmlJS::AST::TryStatement *ast) override;
+ bool visit(QQmlJS::AST::VariableStatement *ast) override;
+ bool visit(QQmlJS::AST::WhileStatement *ast) override;
+ bool visit(QQmlJS::AST::WithStatement *ast) override;
// ui object members
- bool visit(AST::UiArrayBinding *ast) override;
- bool visit(AST::UiObjectBinding *ast) override;
- bool visit(AST::UiObjectDefinition *ast) override;
- bool visit(AST::UiPublicMember *ast) override;
- bool visit(AST::UiScriptBinding *ast) override;
- bool visit(AST::UiSourceElement *ast) override;
-
- bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, const AST::SourceLocation &loc);
- virtual void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail);
- virtual void throwReferenceError(const AST::SourceLocation &loc, const QString &detail);
+ bool visit(QQmlJS::AST::UiArrayBinding *ast) override;
+ bool visit(QQmlJS::AST::UiObjectBinding *ast) override;
+ bool visit(QQmlJS::AST::UiObjectDefinition *ast) override;
+ bool visit(QQmlJS::AST::UiPublicMember *ast) override;
+ bool visit(QQmlJS::AST::UiScriptBinding *ast) override;
+ bool visit(QQmlJS::AST::UiSourceElement *ast) override;
+
+ bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r,
+ const QQmlJS::AST::SourceLocation &loc);
+ virtual void throwSyntaxError(const QQmlJS::AST::SourceLocation &loc, const QString &detail);
+ virtual void throwReferenceError(const QQmlJS::AST::SourceLocation &loc, const QString &detail);
void throwRecursionDepthError() override
{
- throwSyntaxError(AST::SourceLocation(),
+ throwSyntaxError(QQmlJS::AST::SourceLocation(),
QStringLiteral("Maximum statement or expression depth exceeded"));
}
public:
- QList<DiagnosticMessage> errors() const;
-#ifndef V4_BOOTSTRAP
- QList<QQmlError> qmlErrors() const;
-#endif
+ enum ErrorType {
+ NoError,
+ SyntaxError,
+ ReferenceError
+ };
+
+ ErrorType errorType() const { return _errorType; }
+ bool hasError() const { return _errorType != NoError; }
+ QQmlJS::DiagnosticMessage error() const;
+ QUrl url() const;
Reference binopHelper(QSOperator::Op oper, Reference &left, Reference &right);
Reference jumpBinop(QSOperator::Op oper, Reference &left, Reference &right);
struct Arguments { int argc; int argv; bool hasSpread; };
- Arguments pushArgs(AST::ArgumentList *args);
+ Arguments pushArgs(QQmlJS::AST::ArgumentList *args);
void handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject);
- Arguments pushTemplateArgs(AST::TemplateLiteral *args);
- bool handleTaggedTemplate(Reference base, AST::TaggedTemplate *ast);
- void createTemplateObject(AST::TemplateLiteral *t);
+ Arguments pushTemplateArgs(QQmlJS::AST::TemplateLiteral *args);
+ bool handleTaggedTemplate(Reference base, QQmlJS::AST::TaggedTemplate *ast);
+ void createTemplateObject(QQmlJS::AST::TemplateLiteral *t);
void setUseFastLookups(bool b) { useFastLookups = b; }
- void handleTryCatch(AST::TryStatement *ast);
- void handleTryFinally(AST::TryStatement *ast);
+ void handleTryCatch(QQmlJS::AST::TryStatement *ast);
+ void handleTryFinally(QQmlJS::AST::TryStatement *ast);
- Reference referenceForName(const QString &name, bool lhs, const QQmlJS::AST::SourceLocation &accessLocation = QQmlJS::AST::SourceLocation());
+ Reference referenceForName(
+ const QString &name, bool lhs,
+ const QQmlJS::AST::SourceLocation &accessLocation = QQmlJS::AST::SourceLocation());
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> generateCompilationUnit(bool generateUnitData = true);
- static QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading();
+ QV4::CompiledData::CompilationUnit generateCompilationUnit(bool generateUnitData = true);
+ static QV4::CompiledData::CompilationUnit compileModule(
+ bool debugMode, const QString &url, const QString &sourceCode,
+ const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics);
Context *currentContext() const { return _context; }
BytecodeGenerator *generator() const { return bytecodeGenerator; }
@@ -751,7 +764,7 @@ protected:
int _returnAddress;
Context *_context;
Context *_functionContext = nullptr;
- AST::LabelledStatement *_labelledStatement;
+ QQmlJS::AST::LabelledStatement *_labelledStatement;
QV4::Compiler::JSUnitGenerator *jsUnitGenerator;
BytecodeGenerator *bytecodeGenerator = nullptr;
Moth::BytecodeGenerator::Label *_returnLabel = nullptr;
@@ -767,8 +780,8 @@ protected:
ControlFlow *controlFlow = nullptr;
bool _fileNameIsUrl;
- bool hasError;
- QList<QQmlJS::DiagnosticMessage> _errors;
+ ErrorType _errorType = NoError;
+ QQmlJS::DiagnosticMessage _error;
class TailCallBlocker
{
@@ -795,8 +808,10 @@ protected:
};
private:
- VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast);
- void handleConstruct(const Reference &base, AST::ArgumentList *args);
+ VolatileMemoryLocations scanVolatileMemoryLocations(QQmlJS::AST::Node *ast);
+ void handleConstruct(const Reference &base, QQmlJS::AST::ArgumentList *args);
+ void throwError(ErrorType errorType, const QQmlJS::AST::SourceLocation &loc,
+ const QString &detail);
};
}
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index 01c033cb2a..acc4b02e96 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -38,16 +38,24 @@
****************************************************************************/
#include <qv4compiler_p.h>
-#include <qv4compileddata_p.h>
#include <qv4codegen_p.h>
-#include <private/qv4string_p.h>
-#include <private/qv4value_p.h>
+#include <private/qv4compileddata_p.h>
+#include <private/qv4staticvalue_p.h>
#include <private/qv4alloca_p.h>
#include <private/qqmljslexer_p.h>
#include <private/qqmljsast_p.h>
-#include <wtf/MathExtras.h>
+#include <private/qml_compile_hash_p.h>
+#include <private/qqmlirbuilder_p.h>
#include <QCryptographicHash>
+// Efficient implementation that takes advantage of powers of two.
+static inline size_t roundUpToMultipleOf(size_t divisor, size_t x)
+{
+ Q_ASSERT(divisor && !(divisor & (divisor - 1)));
+ const size_t remainderMask = divisor - 1;
+ return (x + remainderMask) & ~remainderMask;
+}
+
QV4::Compiler::StringTableGenerator::StringTableGenerator()
{
clear();
@@ -92,7 +100,7 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit)
{
char *dataStart = reinterpret_cast<char *>(unit);
quint32_le *stringTable = reinterpret_cast<quint32_le *>(dataStart + unit->offsetToStringTable);
- char *stringData = reinterpret_cast<char *>(stringTable) + WTF::roundUpToMultipleOf(8, unit->stringTableSize * sizeof(uint));
+ char *stringData = reinterpret_cast<char *>(stringTable) + roundUpToMultipleOf(8, unit->stringTableSize * sizeof(uint));
for (int i = backingUnitTableSize ; i < strings.size(); ++i) {
const int index = i - backingUnitTableSize;
stringTable[index] = stringData - dataStart;
@@ -119,6 +127,25 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit)
}
}
+void QV4::Compiler::JSUnitGenerator::generateUnitChecksum(QV4::CompiledData::Unit *unit)
+{
+#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
+ QCryptographicHash hash(QCryptographicHash::Md5);
+
+ const int checksummableDataOffset
+ = offsetof(QV4::CompiledData::Unit, md5Checksum) + sizeof(unit->md5Checksum);
+
+ const char *dataPtr = reinterpret_cast<const char *>(unit) + checksummableDataOffset;
+ hash.addData(dataPtr, unit->unitSize - checksummableDataOffset);
+
+ QByteArray checksum = hash.result();
+ Q_ASSERT(checksum.size() == sizeof(unit->md5Checksum));
+ memcpy(unit->md5Checksum, checksum.constData(), sizeof(unit->md5Checksum));
+#else
+ memset(unit->md5Checksum, 0, sizeof(unit->md5Checksum));
+#endif
+}
+
QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::Compiler::Module *module)
: module(module)
{
@@ -242,8 +269,11 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
registerString(module->finalUrl);
for (Context *f : qAsConst(module->functions)) {
registerString(f->name);
- for (int i = 0; i < f->arguments.size(); ++i)
- registerString(f->arguments.at(i));
+ registerString(f->returnType);
+ for (int i = 0; i < f->arguments.size(); ++i) {
+ registerString(f->arguments.at(i).id);
+ registerString(f->arguments.at(i).typeName());
+ }
for (int i = 0; i < f->locals.size(); ++i)
registerString(f->locals.at(i));
}
@@ -385,7 +415,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
if (option == GenerateWithStringTable)
stringTable.serialize(unit);
- unit->generateChecksum();
+ generateUnitChecksum(unit);
return unit;
}
@@ -394,7 +424,7 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
{
QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f;
- quint32 currentOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, sizeof(*function)));
+ quint32 currentOffset = static_cast<quint32>(roundUpToMultipleOf(8, sizeof(*function)));
function->nameIndex = getStringId(irFunction->name);
function->flags = 0;
@@ -410,7 +440,9 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
function->length = irFunction->formals ? irFunction->formals->length() : 0;
function->nFormals = irFunction->arguments.size();
function->formalsOffset = currentOffset;
- currentOffset += function->nFormals * sizeof(quint32);
+ currentOffset += function->nFormals * sizeof(CompiledData::Parameter);
+
+ QmlIR::Parameter::initType(&function->returnType, this, getStringId(irFunction->returnType));
function->sizeOfLocalTemporalDeadZone = irFunction->sizeOfLocalTemporalDeadZone;
function->sizeOfRegisterTemporalDeadZone = irFunction->sizeOfRegisterTemporalDeadZone;
@@ -424,7 +456,6 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
Q_ASSERT(function->lineNumberOffset() == currentOffset);
currentOffset += function->nLineNumbers * sizeof(CompiledData::CodeOffsetToLine);
- function->nTraceInfos = irFunction->nTraceInfos;
function->nRegisters = irFunction->registerCountInFunction;
if (!irFunction->labelInfo.empty()) {
@@ -440,9 +471,11 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
function->codeSize = irFunction->code.size();
// write formals
- quint32_le *formals = (quint32_le *)(f + function->formalsOffset);
- for (int i = 0; i < irFunction->arguments.size(); ++i)
- formals[i] = getStringId(irFunction->arguments.at(i));
+ CompiledData::Parameter *formals = (CompiledData::Parameter *)(f + function->formalsOffset);
+ for (int i = 0; i < irFunction->arguments.size(); ++i) {
+ QmlIR::Parameter::init(&formals[i], this, getStringId(irFunction->arguments.at(i).id),
+ getStringId(irFunction->arguments.at(i).typeName()));
+ }
// write locals
quint32_le *locals = (quint32_le *)(f + function->localsOffset);
@@ -545,7 +578,7 @@ void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context
{
QV4::CompiledData::Block *block = reinterpret_cast<QV4::CompiledData::Block *>(b);
- quint32 currentOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, sizeof(*block)));
+ quint32 currentOffset = static_cast<quint32>(roundUpToMultipleOf(8, sizeof(*block)));
block->sizeOfLocalTemporalDeadZone = irBlock->sizeOfLocalTemporalDeadZone;
block->nLocals = irBlock->locals.size();
@@ -575,7 +608,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
unit.flags |= module->unitFlags;
unit.version = QV4_DATA_STRUCTURE_VERSION;
unit.qtVersion = QT_VERSION;
- qstrcpy(unit.libraryVersionHash, CompiledData::qml_compile_hash);
+ qstrcpy(unit.libraryVersionHash, QML_COMPILE_HASH);
memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum));
memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum));
@@ -608,7 +641,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
unit.constantTableSize = constants.size();
// Ensure we load constants from well-aligned addresses into for example SSE registers.
- nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(16, nextOffset));
+ nextOffset = static_cast<quint32>(roundUpToMultipleOf(16, nextOffset));
unit.offsetToConstantTable = nextOffset;
nextOffset += unit.constantTableSize * sizeof(ReturnedValue);
@@ -619,19 +652,19 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
*jsClassDataOffset = nextOffset;
nextOffset += jsClassData.size();
- nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset));
+ nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset));
unit.translationTableSize = translations.count();
unit.offsetToTranslationTable = nextOffset;
nextOffset += unit.translationTableSize * sizeof(CompiledData::TranslationData);
- nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset));
+ nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset));
const auto reserveExportTable = [&nextOffset](int count, quint32_le *tableSizePtr, quint32_le *offsetPtr) {
*tableSizePtr = count;
*offsetPtr = nextOffset;
nextOffset += count * sizeof(CompiledData::ExportEntry);
- nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset));
+ nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset));
};
reserveExportTable(module->localExportEntries.count(), &unit.localExportEntryTableSize, &unit.offsetToLocalExportEntryTable);
@@ -641,12 +674,12 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
unit.importEntryTableSize = module->importEntries.count();
unit.offsetToImportEntryTable = nextOffset;
nextOffset += unit.importEntryTableSize * sizeof(CompiledData::ImportEntry);
- nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset));
+ nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset));
unit.moduleRequestTableSize = module->moduleRequests.count();
unit.offsetToModuleRequestTable = nextOffset;
nextOffset += unit.moduleRequestTableSize * sizeof(uint);
- nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset));
+ nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset));
quint32 functionSize = 0;
for (int i = 0; i < module->functions.size(); ++i) {
@@ -686,7 +719,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
if (option == GenerateWithStringTable) {
unit.stringTableSize = stringTable.stringCount();
- nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset));
+ nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset));
unit.offsetToStringTable = nextOffset;
nextOffset += stringTable.sizeOfTableAndData();
} else {
diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h
index 49e334bb81..4f3c718175 100644
--- a/src/qml/compiler/qv4compiler_p.h
+++ b/src/qml/compiler/qv4compiler_p.h
@@ -53,9 +53,10 @@
#include <QtCore/qstring.h>
#include <QtCore/qhash.h>
#include <QtCore/qstringlist.h>
-#include <private/qv4global_p.h>
+#include <private/qv4compilerglobal_p.h>
#include <private/qqmljsastfwd_p.h>
#include <private/qv4compileddata_p.h>
+#include <private/qv4staticvalue_p.h>
QT_BEGIN_NAMESPACE
@@ -72,10 +73,12 @@ struct JSClassMember;
namespace Compiler {
+struct Context;
+struct Module;
struct Class;
struct TemplateObject;
-struct Q_QML_PRIVATE_EXPORT StringTableGenerator {
+struct Q_QMLCOMPILER_PRIVATE_EXPORT StringTableGenerator {
StringTableGenerator();
int registerString(const QString &str);
@@ -102,7 +105,9 @@ private:
bool frozen = false;
};
-struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
+struct Q_QMLCOMPILER_PRIVATE_EXPORT JSUnitGenerator {
+ static void generateUnitChecksum(CompiledData::Unit *unit);
+
struct MemberInfo {
QString name;
bool isAccessor;
diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp
index ca2d5128f4..88837b0feb 100644
--- a/src/qml/compiler/qv4compilercontext.cpp
+++ b/src/qml/compiler/qv4compilercontext.cpp
@@ -156,7 +156,7 @@ Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::AS
result.isConst = false;
return result;
} else {
- result.index = argIdx + sizeof(CallData)/sizeof(Value) - 1;
+ result.index = argIdx + sizeof(CallData) / sizeof(StaticValue) - 1;
result.scope = 0;
result.type = ResolvedName::Stack;
result.isConst = false;
@@ -410,28 +410,4 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)
nRegisters = bytecodeGenerator->currentRegister() - registerOffset;
}
-bool Context::canUseTracingJit() const
-{
-#if QT_CONFIG(qml_tracing)
- static bool forceTracing = !qEnvironmentVariableIsEmpty("QV4_FORCE_TRACING");
- if (forceTracing) //### we can probably remove this when tracing is turned on by default
- return true; // to be used by unittests
-
- static bool disableTracing = !qEnvironmentVariableIsEmpty("QV4_DISABLE_TRACING");
- if (disableTracing)
- return false;
-
- static QStringList onlyTrace =
- qEnvironmentVariable("QV4_ONLY_TRACE").split(QLatin1Char(','), QString::SkipEmptyParts);
- if (!onlyTrace.isEmpty())
- return onlyTrace.contains(name);
-
- //### the next condition should be refined and have the IR distinguish between escaping and
- // non-escaping locals
- return !requiresExecutionContext && !hasNestedFunctions;
-#else
- return false;
-#endif
-}
-
QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h
index 57ef4be36e..8c124ac409 100644
--- a/src/qml/compiler/qv4compilercontext_p.h
+++ b/src/qml/compiler/qv4compilercontext_p.h
@@ -50,7 +50,6 @@
// We mean it.
//
-#include "private/qv4global_p.h"
#include <private/qqmljsast_p.h>
#include <private/qv4compileddata_p.h>
#include <QtCore/QStringList>
@@ -62,8 +61,13 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
+namespace Moth {
+class BytecodeGenerator;
+}
+
namespace Compiler {
+class Codegen;
struct ControlFlow;
enum class ContextType {
@@ -162,7 +166,6 @@ struct Context {
int line = 0;
int column = 0;
int registerCountInFunction = 0;
- uint nTraceInfos = 0;
int functionIndex = -1;
int blockIndex = -1;
@@ -190,7 +193,8 @@ struct Context {
MemberMap members;
QSet<QString> usedVariables;
QQmlJS::AST::FormalParameterList *formals = nullptr;
- QStringList arguments;
+ QQmlJS::AST::BoundNames arguments;
+ QString returnType;
QStringList locals;
QStringList moduleRequests;
QVector<ImportEntry> importEntries;
@@ -289,7 +293,7 @@ struct Context {
{
// search backwards to handle duplicate argument names correctly
for (int i = arguments.size() - 1; i >= 0; --i) {
- if (arguments.at(i) == name)
+ if (arguments.at(i).id == name)
return i;
}
return -1;
@@ -363,8 +367,6 @@ struct Context {
return parent->canHaveTailCalls();
return false;
}
-
- bool canUseTracingJit() const;
};
diff --git a/src/qml/compiler/qv4compilercontrolflow_p.h b/src/qml/compiler/qv4compilercontrolflow_p.h
index 5b622e81d8..5623473726 100644
--- a/src/qml/compiler/qv4compilercontrolflow_p.h
+++ b/src/qml/compiler/qv4compilercontrolflow_p.h
@@ -50,7 +50,6 @@
// We mean it.
//
-#include <private/qv4global_p.h>
#include <private/qv4codegen_p.h>
#include <private/qqmljsast_p.h>
#include <private/qv4bytecodegenerator_p.h>
@@ -279,7 +278,7 @@ struct ControlFlowWith : public ControlFlowUnwind
struct ControlFlowBlock : public ControlFlowUnwind
{
- ControlFlowBlock(Codegen *cg, AST::Node *ast)
+ ControlFlowBlock(Codegen *cg, QQmlJS::AST::Node *ast)
: ControlFlowUnwind(cg, Block)
{
block = cg->enterBlock(ast);
@@ -314,11 +313,11 @@ struct ControlFlowBlock : public ControlFlowUnwind
struct ControlFlowCatch : public ControlFlowUnwind
{
- AST::Catch *catchExpression;
+ QQmlJS::AST::Catch *catchExpression;
bool insideCatch = false;
BytecodeGenerator::ExceptionHandler exceptionLabel;
- ControlFlowCatch(Codegen *cg, AST::Catch *catchExpression)
+ ControlFlowCatch(Codegen *cg, QQmlJS::AST::Catch *catchExpression)
: ControlFlowUnwind(cg, Catch), catchExpression(catchExpression),
exceptionLabel(generator()->newExceptionHandler())
{
@@ -372,10 +371,10 @@ struct ControlFlowCatch : public ControlFlowUnwind
struct ControlFlowFinally : public ControlFlowUnwind
{
- AST::Finally *finally;
+ QQmlJS::AST::Finally *finally;
bool insideFinally = false;
- ControlFlowFinally(Codegen *cg, AST::Finally *finally)
+ ControlFlowFinally(Codegen *cg, QQmlJS::AST::Finally *finally)
: ControlFlowUnwind(cg, Finally), finally(finally)
{
Q_ASSERT(finally != nullptr);
diff --git a/src/qml/qmldirparser/qqmlsourcecoordinate_p.h b/src/qml/compiler/qv4compilerglobal_p.h
index 76ac741ae8..3478074827 100644
--- a/src/qml/qmldirparser/qqmlsourcecoordinate_p.h
+++ b/src/qml/compiler/qv4compilerglobal_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QQMLSOURCECOORDINATE_P_H
-#define QQMLSOURCECOORDINATE_P_H
+#ifndef QV4COMPILERGLOBAL_H
+#define QV4COMPILERGLOBAL_H
//
// W A R N I N G
@@ -52,21 +52,23 @@
//
#include <QtCore/qglobal.h>
+#include <QString>
-#include <climits>
+#include <private/qtqmlcompilerglobal_p.h>
QT_BEGIN_NAMESPACE
-inline quint16 qmlSourceCoordinate(int n)
-{
- return (n > 0 && n <= static_cast<int>(USHRT_MAX)) ? static_cast<quint16>(n) : 0;
-}
+namespace QV4 {
-inline int qmlSourceCoordinate(quint16 n)
-{
- return (n == 0) ? -1 : static_cast<int>(n);
-}
+enum class ObjectLiteralArgument {
+ Value,
+ Method,
+ Getter,
+ Setter
+};
+
+} // namespace QV4
QT_END_NAMESPACE
-#endif // QQMLSOURCECOORDINATE_P_H
+#endif // QV4COMPILERGLOBAL_H
diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp
index 8984b6931e..ab0ebf3d4b 100644
--- a/src/qml/compiler/qv4compilerscanfunctions.cpp
+++ b/src/qml/compiler/qv4compilerscanfunctions.cpp
@@ -44,18 +44,26 @@
#include <QtCore/QSet>
#include <QtCore/QBuffer>
#include <QtCore/QBitArray>
-#include <QtCore/QLinkedList>
#include <QtCore/QStack>
#include <private/qqmljsast_p.h>
#include <private/qv4compilercontext_p.h>
#include <private/qv4codegen_p.h>
-#include <private/qv4string_p.h>
QT_USE_NAMESPACE
using namespace QV4;
using namespace QV4::Compiler;
+using namespace QQmlJS;
using namespace QQmlJS::AST;
+static CompiledData::Location location(const QQmlJS::AST::SourceLocation &astLocation)
+{
+ CompiledData::Location target;
+ target.line = astLocation.startLine;
+ target.column = astLocation.startColumn;
+ return target;
+}
+
+
ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType)
: QQmlJS::AST::Visitor(cg->recursionDepth())
, _cg(cg)
@@ -177,7 +185,7 @@ bool ScanFunctions::visit(ExportDeclaration *declaration)
Compiler::ExportEntry entry;
entry.moduleRequest = declaration->fromClause->moduleSpecifier.toString();
entry.importName = QStringLiteral("*");
- entry.location = declaration->firstSourceLocation();
+ entry.location = location(declaration->firstSourceLocation());
_context->exportEntries << entry;
} else if (declaration->exportClause) {
for (ExportsList *it = declaration->exportClause->exportsList; it; it = it->next) {
@@ -190,22 +198,22 @@ bool ScanFunctions::visit(ExportDeclaration *declaration)
entry.moduleRequest = module;
entry.exportName = spec->exportedIdentifier.toString();
- entry.location = it->firstSourceLocation();
+ entry.location = location(it->firstSourceLocation());
_context->exportEntries << entry;
}
} else if (auto *vstmt = AST::cast<AST::VariableStatement*>(declaration->variableStatementOrDeclaration)) {
- QStringList boundNames;
+ BoundNames boundNames;
for (VariableDeclarationList *it = vstmt->declarations; it; it = it->next) {
if (!it->declaration)
continue;
it->declaration->boundNames(&boundNames);
}
- for (const QString &name: boundNames) {
+ for (const auto &name: boundNames) {
Compiler::ExportEntry entry;
- entry.localName = name;
- entry.exportName = name;
- entry.location = vstmt->firstSourceLocation();
+ entry.localName = name.id;
+ entry.exportName = name.id;
+ entry.location = location(vstmt->firstSourceLocation());
_context->exportEntries << entry;
}
} else if (auto *classDecl = AST::cast<AST::ClassDeclaration*>(declaration->variableStatementOrDeclaration)) {
@@ -214,7 +222,7 @@ bool ScanFunctions::visit(ExportDeclaration *declaration)
Compiler::ExportEntry entry;
entry.localName = name;
entry.exportName = name;
- entry.location = classDecl->firstSourceLocation();
+ entry.location = location(classDecl->firstSourceLocation());
_context->exportEntries << entry;
if (declaration->exportDefault)
localNameForDefaultExport = entry.localName;
@@ -233,7 +241,7 @@ bool ScanFunctions::visit(ExportDeclaration *declaration)
Compiler::ExportEntry entry;
entry.localName = functionName;
entry.exportName = functionName;
- entry.location = fdef->firstSourceLocation();
+ entry.location = location(fdef->firstSourceLocation());
_context->exportEntries << entry;
if (declaration->exportDefault)
localNameForDefaultExport = entry.localName;
@@ -245,7 +253,7 @@ bool ScanFunctions::visit(ExportDeclaration *declaration)
entry.localName = localNameForDefaultExport;
_context->localNameForDefaultExport = localNameForDefaultExport;
entry.exportName = QStringLiteral("default");
- entry.location = declaration->firstSourceLocation();
+ entry.location = location(declaration->firstSourceLocation());
_context->exportEntries << entry;
}
@@ -270,7 +278,7 @@ bool ScanFunctions::visit(ImportDeclaration *declaration)
entry.moduleRequest = module;
entry.importName = QStringLiteral("default");
entry.localName = import->importedDefaultBinding.toString();
- entry.location = declaration->firstSourceLocation();
+ entry.location = location(declaration->firstSourceLocation());
_context->importEntries << entry;
}
@@ -279,7 +287,7 @@ bool ScanFunctions::visit(ImportDeclaration *declaration)
entry.moduleRequest = module;
entry.importName = QStringLiteral("*");
entry.localName = import->nameSpaceImport->importedBinding.toString();
- entry.location = declaration->firstSourceLocation();
+ entry.location = location(declaration->firstSourceLocation());
_context->importEntries << entry;
}
@@ -292,7 +300,7 @@ bool ScanFunctions::visit(ImportDeclaration *declaration)
entry.importName = it->importSpecifier->identifier.toString();
else
entry.importName = entry.localName;
- entry.location = declaration->firstSourceLocation();
+ entry.location = location(declaration->firstSourceLocation());
_context->importEntries << entry;
}
}
@@ -319,26 +327,26 @@ bool ScanFunctions::visit(PatternElement *ast)
if (!ast->isVariableDeclaration())
return true;
- QStringList names;
+ BoundNames names;
ast->boundNames(&names);
QQmlJS::AST::SourceLocation lastInitializerLocation = ast->lastSourceLocation();
if (_context->lastBlockInitializerLocation.isValid())
lastInitializerLocation = _context->lastBlockInitializerLocation;
- for (const QString &name : qAsConst(names)) {
- if (_context->isStrict && (name == QLatin1String("eval") || name == QLatin1String("arguments")))
+ for (const auto &name : qAsConst(names)) {
+ if (_context->isStrict && (name.id == QLatin1String("eval") || name.id == QLatin1String("arguments")))
_cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode"));
- checkName(QStringRef(&name), ast->identifierToken);
- if (name == QLatin1String("arguments"))
+ checkName(QStringRef(&name.id), ast->identifierToken);
+ if (name.id == QLatin1String("arguments"))
_context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
if (ast->scope == VariableScope::Const && !ast->initializer && !ast->isForDeclaration && !ast->destructuringPattern()) {
_cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration"));
return false;
}
- if (!_context->addLocalVar(name, ast->initializer ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope,
+ if (!_context->addLocalVar(name.id, ast->initializer ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope,
/*function*/nullptr, lastInitializerLocation)) {
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name));
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name.id));
return false;
}
}
@@ -672,6 +680,9 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete
_context->isArrowFunction = true;
else if (expr->isGenerator)
_context->isGenerator = true;
+
+ if (expr->typeAnnotation)
+ _context->returnType = expr->typeAnnotation->type->toString();
}
@@ -684,11 +695,11 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete
bool isSimpleParameterList = formals && formals->isSimpleParameterList();
- _context->arguments = formals ? formals->formals() : QStringList();
+ _context->arguments = formals ? formals->formals() : BoundNames();
- const QStringList boundNames = formals ? formals->boundNames() : QStringList();
+ const BoundNames boundNames = formals ? formals->boundNames() : BoundNames();
for (int i = 0; i < boundNames.size(); ++i) {
- const QString &arg = boundNames.at(i);
+ const QString &arg = boundNames.at(i).id;
if (_context->isStrict || !isSimpleParameterList) {
bool duplicate = (boundNames.indexOf(arg, i + 1) != -1);
if (duplicate) {
@@ -705,6 +716,7 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete
if (!_context->arguments.contains(arg))
_context->addLocalVar(arg, Context::VariableDefinition, VariableScope::Var);
}
+
return true;
}
diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h
index 0f7bf1818a..2de80eac44 100644
--- a/src/qml/compiler/qv4compilerscanfunctions_p.h
+++ b/src/qml/compiler/qv4compilerscanfunctions_p.h
@@ -50,7 +50,7 @@
// We mean it.
//
-#include "private/qv4global_p.h"
+#include <private/qtqmlcompilerglobal_p.h>
#include <private/qqmljsastvisitor_p.h>
#include <private/qqmljsast_p.h>
#include <private/qqmljsengine_p.h>
@@ -62,8 +62,6 @@
QT_BEGIN_NAMESPACE
-using namespace QQmlJS;
-
namespace QV4 {
namespace Moth {
@@ -83,84 +81,87 @@ class ScanFunctions: protected QQmlJS::AST::Visitor
typedef QScopedValueRollback<bool> TemporaryBoolAssignment;
public:
ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType);
- void operator()(AST::Node *node);
+ void operator()(QQmlJS::AST::Node *node);
void enterGlobalEnvironment(ContextType compilationMode);
- void enterEnvironment(AST::Node *node, ContextType compilationMode, const QString &name);
+ void enterEnvironment(QQmlJS::AST::Node *node, ContextType compilationMode,
+ const QString &name);
void leaveEnvironment();
- void enterQmlFunction(AST::FunctionDeclaration *ast)
+ void enterQmlFunction(QQmlJS::AST::FunctionExpression *ast)
{ enterFunction(ast, false); }
protected:
using Visitor::visit;
using Visitor::endVisit;
- void checkDirectivePrologue(AST::StatementList *ast);
+ void checkDirectivePrologue(QQmlJS::AST::StatementList *ast);
- void checkName(const QStringRef &name, const AST::SourceLocation &loc);
+ void checkName(const QStringRef &name, const QQmlJS::AST::SourceLocation &loc);
- bool visit(AST::Program *ast) override;
- void endVisit(AST::Program *) override;
+ bool visit(QQmlJS::AST::Program *ast) override;
+ void endVisit(QQmlJS::AST::Program *) override;
- bool visit(AST::ESModule *ast) override;
- void endVisit(AST::ESModule *) override;
+ bool visit(QQmlJS::AST::ESModule *ast) override;
+ void endVisit(QQmlJS::AST::ESModule *) override;
- bool visit(AST::ExportDeclaration *declaration) override;
- bool visit(AST::ImportDeclaration *declaration) override;
+ bool visit(QQmlJS::AST::ExportDeclaration *declaration) override;
+ bool visit(QQmlJS::AST::ImportDeclaration *declaration) override;
- bool visit(AST::CallExpression *ast) override;
- bool visit(AST::PatternElement *ast) override;
- bool visit(AST::IdentifierExpression *ast) override;
- bool visit(AST::ExpressionStatement *ast) override;
- bool visit(AST::FunctionExpression *ast) override;
- bool visit(AST::TemplateLiteral *ast) override;
- bool visit(AST::SuperLiteral *) override;
- bool visit(AST::FieldMemberExpression *) override;
- bool visit(AST::ArrayPattern *) override;
+ bool visit(QQmlJS::AST::CallExpression *ast) override;
+ bool visit(QQmlJS::AST::PatternElement *ast) override;
+ bool visit(QQmlJS::AST::IdentifierExpression *ast) override;
+ bool visit(QQmlJS::AST::ExpressionStatement *ast) override;
+ bool visit(QQmlJS::AST::FunctionExpression *ast) override;
+ bool visit(QQmlJS::AST::TemplateLiteral *ast) override;
+ bool visit(QQmlJS::AST::SuperLiteral *) override;
+ bool visit(QQmlJS::AST::FieldMemberExpression *) override;
+ bool visit(QQmlJS::AST::ArrayPattern *) override;
- bool enterFunction(AST::FunctionExpression *ast, bool enterName);
+ bool enterFunction(QQmlJS::AST::FunctionExpression *ast, bool enterName);
- void endVisit(AST::FunctionExpression *) override;
+ void endVisit(QQmlJS::AST::FunctionExpression *) override;
- bool visit(AST::ObjectPattern *ast) override;
+ bool visit(QQmlJS::AST::ObjectPattern *ast) override;
- bool visit(AST::PatternProperty *ast) override;
- void endVisit(AST::PatternProperty *) override;
+ bool visit(QQmlJS::AST::PatternProperty *ast) override;
+ void endVisit(QQmlJS::AST::PatternProperty *) override;
- bool visit(AST::FunctionDeclaration *ast) override;
- void endVisit(AST::FunctionDeclaration *) override;
+ bool visit(QQmlJS::AST::FunctionDeclaration *ast) override;
+ void endVisit(QQmlJS::AST::FunctionDeclaration *) override;
- bool visit(AST::ClassExpression *ast) override;
- void endVisit(AST::ClassExpression *) override;
+ bool visit(QQmlJS::AST::ClassExpression *ast) override;
+ void endVisit(QQmlJS::AST::ClassExpression *) override;
- bool visit(AST::ClassDeclaration *ast) override;
- void endVisit(AST::ClassDeclaration *) override;
+ bool visit(QQmlJS::AST::ClassDeclaration *ast) override;
+ void endVisit(QQmlJS::AST::ClassDeclaration *) override;
- bool visit(AST::DoWhileStatement *ast) override;
- bool visit(AST::ForStatement *ast) override;
- void endVisit(AST::ForStatement *) override;
- bool visit(AST::ForEachStatement *ast) override;
- void endVisit(AST::ForEachStatement *) override;
+ bool visit(QQmlJS::AST::DoWhileStatement *ast) override;
+ bool visit(QQmlJS::AST::ForStatement *ast) override;
+ void endVisit(QQmlJS::AST::ForStatement *) override;
+ bool visit(QQmlJS::AST::ForEachStatement *ast) override;
+ void endVisit(QQmlJS::AST::ForEachStatement *) override;
- bool visit(AST::ThisExpression *ast) override;
+ bool visit(QQmlJS::AST::ThisExpression *ast) override;
- bool visit(AST::Block *ast) override;
- void endVisit(AST::Block *ast) override;
+ bool visit(QQmlJS::AST::Block *ast) override;
+ void endVisit(QQmlJS::AST::Block *ast) override;
- bool visit(AST::CaseBlock *ast) override;
- void endVisit(AST::CaseBlock *ast) override;
+ bool visit(QQmlJS::AST::CaseBlock *ast) override;
+ void endVisit(QQmlJS::AST::CaseBlock *ast) override;
- bool visit(AST::Catch *ast) override;
- void endVisit(AST::Catch *ast) override;
+ bool visit(QQmlJS::AST::Catch *ast) override;
+ void endVisit(QQmlJS::AST::Catch *ast) override;
- bool visit(AST::WithStatement *ast) override;
- void endVisit(AST::WithStatement *ast) override;
+ bool visit(QQmlJS::AST::WithStatement *ast) override;
+ void endVisit(QQmlJS::AST::WithStatement *ast) override;
void throwRecursionDepthError() override;
protected:
- bool enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::StatementList *body, bool enterName);
+ bool enterFunction(QQmlJS::AST::Node *ast, const QString &name,
+ QQmlJS::AST::FormalParameterList *formals,
+ QQmlJS::AST::StatementList *body, bool enterName);
void calcEscapingVariables();
// fields:
@@ -173,7 +174,7 @@ protected:
ContextType defaultProgramType;
private:
- static constexpr AST::Node *astNodeForGlobalEnvironment = nullptr;
+ static constexpr QQmlJS::AST::Node *astNodeForGlobalEnvironment = nullptr;
};
}
diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp
index 345f03ae8a..640a908dd3 100644
--- a/src/qml/compiler/qv4instr_moth.cpp
+++ b/src/qml/compiler/qv4instr_moth.cpp
@@ -39,7 +39,9 @@
#include "qv4instr_moth_p.h"
#include <private/qv4compileddata_p.h>
-#include <private/qv4stackframe_p.h>
+#include <private/qv4calldata_p.h>
+
+#include <QtCore/qdebug.h>
using namespace QV4;
using namespace QV4::Moth;
@@ -56,9 +58,7 @@ int InstrInfo::size(Instr::Type type)
static QByteArray alignedNumber(int n) {
QByteArray number = QByteArray::number(n);
- while (number.size() < 8)
- number.prepend(' ');
- return number;
+ return number.prepend(8 - number.size(), ' ');
}
static QByteArray alignedLineNumber(int line) {
@@ -83,25 +83,6 @@ static QByteArray rawBytes(const char *data, int n)
return ba;
}
-static QString toString(QV4::ReturnedValue v)
-{
-#ifdef V4_BOOTSTRAP
- return QStringLiteral("string-const(%1)").arg(v);
-#else // !V4_BOOTSTRAP
- Value val = Value::fromReturnedValue(v);
- QString result;
- if (val.isInt32())
- result = QLatin1String("int ");
- else if (val.isDouble())
- result = QLatin1String("double ");
- if (val.isEmpty())
- result += QLatin1String("empty");
- else
- result += val.toQStringNoThrow();
- return result;
-#endif // V4_BOOTSTRAP
-}
-
#define ABSOLUTE_OFFSET() \
(code - start + offset)
@@ -128,22 +109,12 @@ const int InstrInfo::argumentCount[] = {
FOR_EACH_MOTH_INSTR_ALL(MOTH_COLLECT_NARGS)
};
-
-void dumpConstantTable(const Value *constants, uint count)
-{
- QDebug d = qDebug();
- d.nospace();
- for (uint i = 0; i < count; ++i)
- d << alignedNumber(int(i)).constData() << ": "
- << toString(constants[i].asReturnedValue()).toUtf8().constData() << "\n";
-}
-
QString dumpRegister(int reg, int nFormals)
{
Q_STATIC_ASSERT(offsetof(CallData, function) == 0);
- Q_STATIC_ASSERT(offsetof(CallData, context) == sizeof(Value));
- Q_STATIC_ASSERT(offsetof(CallData, accumulator) == 2*sizeof(Value));
- Q_STATIC_ASSERT(offsetof(CallData, thisObject) == 3*sizeof(Value));
+ Q_STATIC_ASSERT(offsetof(CallData, context) == sizeof(StaticValue));
+ Q_STATIC_ASSERT(offsetof(CallData, accumulator) == 2*sizeof(StaticValue));
+ Q_STATIC_ASSERT(offsetof(CallData, thisObject) == 3*sizeof(StaticValue));
if (reg == CallData::Function)
return QStringLiteral("(function)");
else if (reg == CallData::Context)
@@ -171,8 +142,6 @@ QString dumpArguments(int argc, int argv, int nFormals)
return QStringLiteral("(") + dumpRegister(argv, nFormals) + QStringLiteral(", ") + QString::number(argc) + QStringLiteral(")");
}
-#define TRACE_SLOT QStringLiteral(" {%1}").arg(traceSlot)
-
void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*startLine*/, const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping)
{
MOTH_JUMP_TABLE;
@@ -241,9 +210,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_BEGIN_INSTR(LoadLocal)
if (index < nLocals)
- d << "l" << index << TRACE_SLOT;
+ d << "l" << index;
else
- d << "a" << (index - nLocals) << TRACE_SLOT;
+ d << "a" << (index - nLocals);
MOTH_END_INSTR(LoadLocal)
MOTH_BEGIN_INSTR(StoreLocal)
@@ -255,9 +224,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_BEGIN_INSTR(LoadScopedLocal)
if (index < nLocals)
- d << "l" << index << "@" << scope << TRACE_SLOT;
+ d << "l" << index << "@" << scope;
else
- d << "a" << (index - nLocals) << "@" << scope << TRACE_SLOT;
+ d << "a" << (index - nLocals) << "@" << scope;
MOTH_END_INSTR(LoadScopedLocal)
MOTH_BEGIN_INSTR(StoreScopedLocal)
@@ -280,15 +249,15 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(LoadClosure)
MOTH_BEGIN_INSTR(LoadName)
- d << name << TRACE_SLOT;
+ d << name;
MOTH_END_INSTR(LoadName)
MOTH_BEGIN_INSTR(LoadGlobalLookup)
- d << index << TRACE_SLOT;
+ d << index;
MOTH_END_INSTR(LoadGlobalLookup)
MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup)
- d << index << TRACE_SLOT;
+ d << index;
MOTH_END_INSTR(LoadQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(StoreNameSloppy)
@@ -300,20 +269,19 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(StoreNameStrict)
MOTH_BEGIN_INSTR(LoadElement)
- d << dumpRegister(base, nFormals) << "[acc]" << TRACE_SLOT;
+ d << dumpRegister(base, nFormals) << "[acc]";
MOTH_END_INSTR(LoadElement)
MOTH_BEGIN_INSTR(StoreElement)
- d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"
- << TRACE_SLOT;
+ d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]";
MOTH_END_INSTR(StoreElement)
MOTH_BEGIN_INSTR(LoadProperty)
- d << "acc[" << name << "]" << TRACE_SLOT;
+ d << "acc[" << name << "]";
MOTH_END_INSTR(LoadProperty)
MOTH_BEGIN_INSTR(GetLookup)
- d << "acc(" << index << ")" << TRACE_SLOT;
+ d << "acc(" << index << ")";
MOTH_END_INSTR(GetLookup)
MOTH_BEGIN_INSTR(StoreProperty)
@@ -343,49 +311,48 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(Resume)
MOTH_BEGIN_INSTR(CallValue)
- d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
+ d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallValue)
MOTH_BEGIN_INSTR(CallWithReceiver)
d << dumpRegister(name, nFormals) << dumpRegister(thisObject, nFormals)
- << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
+ << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallWithReceiver)
MOTH_BEGIN_INSTR(CallProperty)
d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals)
- << TRACE_SLOT;
+ ;
MOTH_END_INSTR(CallProperty)
MOTH_BEGIN_INSTR(CallPropertyLookup)
d << dumpRegister(base, nFormals) << "." << lookupIndex
- << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
+ << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallPropertyLookup)
MOTH_BEGIN_INSTR(CallElement)
d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"
- << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
+ << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallElement)
MOTH_BEGIN_INSTR(CallName)
- d << name << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
+ d << name << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallName)
MOTH_BEGIN_INSTR(CallPossiblyDirectEval)
- d << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
+ d << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallPossiblyDirectEval)
MOTH_BEGIN_INSTR(CallGlobalLookup)
- d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
+ d << index << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallGlobalLookup)
MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup)
- d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
+ d << index << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(CallWithSpread)
d << "new " << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals)
- << dumpArguments(argc, argv, nFormals)
- << TRACE_SLOT;
+ << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallWithSpread)
MOTH_BEGIN_INSTR(Construct)
@@ -528,11 +495,11 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(Jump)
MOTH_BEGIN_INSTR(JumpTrue)
- d << ABSOLUTE_OFFSET() << TRACE_SLOT;
+ d << ABSOLUTE_OFFSET();
MOTH_END_INSTR(JumpTrue)
MOTH_BEGIN_INSTR(JumpFalse)
- d << ABSOLUTE_OFFSET() << TRACE_SLOT;
+ d << ABSOLUTE_OFFSET();
MOTH_END_INSTR(JumpFalse)
MOTH_BEGIN_INSTR(JumpNotUndefined)
@@ -543,6 +510,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << ABSOLUTE_OFFSET();
MOTH_END_INSTR(JumpNoException)
+ MOTH_BEGIN_INSTR(CheckException)
+ MOTH_END_INSTR(CheckException)
+
MOTH_BEGIN_INSTR(CmpEqNull)
MOTH_END_INSTR(CmpEqNull)
@@ -596,22 +566,19 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(UPlus)
MOTH_BEGIN_INSTR(UMinus)
- d << TRACE_SLOT;
MOTH_END_INSTR(UMinus)
MOTH_BEGIN_INSTR(UCompl)
MOTH_END_INSTR(UCompl)
MOTH_BEGIN_INSTR(Increment)
- d << TRACE_SLOT;
MOTH_END_INSTR(Increment)
MOTH_BEGIN_INSTR(Decrement)
- d << TRACE_SLOT;
MOTH_END_INSTR(Decrement)
MOTH_BEGIN_INSTR(Add)
- d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT;
+ d << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Add)
MOTH_BEGIN_INSTR(BitAnd)
@@ -667,7 +634,7 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(Exp)
MOTH_BEGIN_INSTR(Mul)
- d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT;
+ d << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Mul)
MOTH_BEGIN_INSTR(Div)
@@ -675,11 +642,11 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(Div)
MOTH_BEGIN_INSTR(Mod)
- d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT;
+ d << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Mod)
MOTH_BEGIN_INSTR(Sub)
- d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT;
+ d << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Sub)
MOTH_BEGIN_INSTR(CmpIn)
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index 26901c297c..c0dd696b8a 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -50,9 +50,8 @@
//
// We mean it.
//
-#include <private/qv4global_p.h>
-#include <private/qv4value_p.h>
-#include <private/qv4runtime_p.h>
+
+#include <private/qv4staticvalue_p.h>
#include <private/qv4compileddata_p.h> // for CompiledData::CodeOffsetToLine used by the dumper
#include <qendian.h>
@@ -77,20 +76,20 @@ QT_BEGIN_NAMESPACE
#define INSTR_StoreReg(op) INSTRUCTION(op, StoreReg, 1, reg)
#define INSTR_MoveReg(op) INSTRUCTION(op, MoveReg, 2, srcReg, destReg)
#define INSTR_LoadImport(op) INSTRUCTION(op, LoadImport, 1, index)
-#define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 2, index, traceSlot)
+#define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 1, index)
#define INSTR_StoreLocal(op) INSTRUCTION(op, StoreLocal, 1, index)
-#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 3, scope, index, traceSlot)
+#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 2, scope, index)
#define INSTR_StoreScopedLocal(op) INSTRUCTION(op, StoreScopedLocal, 2, scope, index)
#define INSTR_LoadRuntimeString(op) INSTRUCTION(op, LoadRuntimeString, 1, stringId)
#define INSTR_MoveRegExp(op) INSTRUCTION(op, MoveRegExp, 2, regExpId, destReg)
#define INSTR_LoadClosure(op) INSTRUCTION(op, LoadClosure, 1, value)
-#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 2, name, traceSlot)
-#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 2, index, traceSlot)
-#define INSTR_LoadQmlContextPropertyLookup(op) INSTRUCTION(op, LoadQmlContextPropertyLookup, 2, index, traceSlot)
+#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 1, name)
+#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 1, index)
+#define INSTR_LoadQmlContextPropertyLookup(op) INSTRUCTION(op, LoadQmlContextPropertyLookup, 1, index)
#define INSTR_StoreNameSloppy(op) INSTRUCTION(op, StoreNameSloppy, 1, name)
#define INSTR_StoreNameStrict(op) INSTRUCTION(op, StoreNameStrict, 1, name)
-#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 2, name, traceSlot)
-#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, traceSlot)
+#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 1, name)
+#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 1, index)
#define INSTR_LoadIdObject(op) INSTRUCTION(op, LoadIdObject, 2, index, base)
#define INSTR_Yield(op) INSTRUCTION(op, Yield, 0)
#define INSTR_YieldStar(op) INSTRUCTION(op, YieldStar, 0)
@@ -100,18 +99,18 @@ QT_BEGIN_NAMESPACE
#define INSTR_SetLookup(op) INSTRUCTION(op, SetLookup, 2, index, base)
#define INSTR_LoadSuperProperty(op) INSTRUCTION(op, LoadSuperProperty, 1, property)
#define INSTR_StoreSuperProperty(op) INSTRUCTION(op, StoreSuperProperty, 1, property)
-#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 2, base, traceSlot)
-#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 3, base, index, traceSlot)
-#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 4, name, argc, argv, traceSlot)
-#define INSTR_CallWithReceiver(op) INSTRUCTION(op, CallWithReceiver, 5, name, thisObject, argc, argv, traceSlot)
-#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 5, name, base, argc, argv, traceSlot)
-#define INSTR_CallPropertyLookup(op) INSTRUCTION(op, CallPropertyLookup, 5, lookupIndex, base, argc, argv, traceSlot)
-#define INSTR_CallElement(op) INSTRUCTION(op, CallElement, 5, base, index, argc, argv, traceSlot)
-#define INSTR_CallName(op) INSTRUCTION(op, CallName, 4, name, argc, argv, traceSlot)
-#define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 3, argc, argv, traceSlot)
-#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 4, index, argc, argv, traceSlot)
-#define INSTR_CallQmlContextPropertyLookup(op) INSTRUCTION(op, CallQmlContextPropertyLookup, 4, index, argc, argv, traceSlot)
-#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 5, func, thisObject, argc, argv, traceSlot)
+#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 1, base)
+#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 2, base, index)
+#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 3, name, argc, argv)
+#define INSTR_CallWithReceiver(op) INSTRUCTION(op, CallWithReceiver, 4, name, thisObject, argc, argv)
+#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 4, name, base, argc, argv)
+#define INSTR_CallPropertyLookup(op) INSTRUCTION(op, CallPropertyLookup, 4, lookupIndex, base, argc, argv)
+#define INSTR_CallElement(op) INSTRUCTION(op, CallElement, 4, base, index, argc, argv)
+#define INSTR_CallName(op) INSTRUCTION(op, CallName, 3, name, argc, argv)
+#define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 2, argc, argv)
+#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 3, index, argc, argv)
+#define INSTR_CallQmlContextPropertyLookup(op) INSTRUCTION(op, CallQmlContextPropertyLookup, 3, index, argc, argv)
+#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 4, func, thisObject, argc, argv)
#define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv)
#define INSTR_ConstructWithSpread(op) INSTRUCTION(op, ConstructWithSpread, 3, func, argc, argv)
#define INSTR_SetUnwindHandler(op) INSTRUCTION(op, SetUnwindHandler, 1, offset)
@@ -148,10 +147,11 @@ QT_BEGIN_NAMESPACE
#define INSTR_LoadSuperConstructor(op) INSTRUCTION(op, LoadSuperConstructor, 0)
#define INSTR_ToObject(op) INSTRUCTION(op, ToObject, 0)
#define INSTR_Jump(op) INSTRUCTION(op, Jump, 1, offset)
-#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 2, traceSlot, offset)
-#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 2, traceSlot, offset)
+#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 1, offset)
+#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 1, offset)
#define INSTR_JumpNotUndefined(op) INSTRUCTION(op, JumpNotUndefined, 1, offset)
#define INSTR_JumpNoException(op) INSTRUCTION(op, JumpNoException, 1, offset)
+#define INSTR_CheckException(op) INSTRUCTION(op, CheckException, 0)
#define INSTR_CmpEqNull(op) INSTRUCTION(op, CmpEqNull, 0)
#define INSTR_CmpNeNull(op) INSTRUCTION(op, CmpNeNull, 0)
#define INSTR_CmpEqInt(op) INSTRUCTION(op, CmpEqInt, 1, lhs)
@@ -168,11 +168,11 @@ QT_BEGIN_NAMESPACE
#define INSTR_CmpInstanceOf(op) INSTRUCTION(op, CmpInstanceOf, 1, lhs)
#define INSTR_UNot(op) INSTRUCTION(op, UNot, 0)
#define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 0)
-#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 1, traceSlot)
+#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 0)
#define INSTR_UCompl(op) INSTRUCTION(op, UCompl, 0)
-#define INSTR_Increment(op) INSTRUCTION(op, Increment, 1, traceSlot)
-#define INSTR_Decrement(op) INSTRUCTION(op, Decrement, 1, traceSlot)
-#define INSTR_Add(op) INSTRUCTION(op, Add, 2, lhs, traceSlot)
+#define INSTR_Increment(op) INSTRUCTION(op, Increment, 0)
+#define INSTR_Decrement(op) INSTRUCTION(op, Decrement, 0)
+#define INSTR_Add(op) INSTRUCTION(op, Add, 1, lhs)
#define INSTR_BitAnd(op) INSTRUCTION(op, BitAnd, 1, lhs)
#define INSTR_BitOr(op) INSTRUCTION(op, BitOr, 1, lhs)
#define INSTR_BitXor(op) INSTRUCTION(op, BitXor, 1, lhs)
@@ -186,10 +186,10 @@ QT_BEGIN_NAMESPACE
#define INSTR_ShrConst(op) INSTRUCTION(op, ShrConst, 1, rhs)
#define INSTR_ShlConst(op) INSTRUCTION(op, ShlConst, 1, rhs)
#define INSTR_Exp(op) INSTRUCTION(op, Exp, 1, lhs)
-#define INSTR_Mul(op) INSTRUCTION(op, Mul, 2, lhs, traceSlot)
+#define INSTR_Mul(op) INSTRUCTION(op, Mul, 1, lhs)
#define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs)
-#define INSTR_Mod(op) INSTRUCTION(op, Mod, 2, lhs, traceSlot)
-#define INSTR_Sub(op) INSTRUCTION(op, Sub, 2, lhs, traceSlot)
+#define INSTR_Mod(op) INSTRUCTION(op, Mod, 1, lhs)
+#define INSTR_Sub(op) INSTRUCTION(op, Sub, 1, lhs)
#define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result)
#define INSTR_InitializeBlockDeadTemporalZone(op) INSTRUCTION(op, InitializeBlockDeadTemporalZone, 2, firstReg, count)
#define INSTR_ThrowOnNullOrUndefined(op) INSTRUCTION(op, ThrowOnNullOrUndefined, 0)
@@ -241,6 +241,7 @@ QT_BEGIN_NAMESPACE
F(JumpFalse) \
F(JumpNoException) \
F(JumpNotUndefined) \
+ F(CheckException) \
F(CmpEqNull) \
F(CmpNeNull) \
F(CmpEqInt) \
@@ -532,7 +533,6 @@ inline bool operator!=(const StackSlot &l, const StackSlot &r) { return l.stackS
// When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h
-void dumpConstantTable(const Value *constants, uint count);
void dumpBytecode(const char *bytecode, int len, int nLocals, int nFormals, int startLine = 1,
const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping = QVector<CompiledData::CodeOffsetToLine>());
inline void dumpBytecode(const QByteArray &bytecode, int nLocals, int nFormals, int startLine = 1,
diff --git a/src/qml/jsruntime/qv4util_p.h b/src/qml/compiler/qv4util_p.h
index 073832937d..bd9758c1fb 100644
--- a/src/qml/jsruntime/qv4util_p.h
+++ b/src/qml/compiler/qv4util_p.h
@@ -50,7 +50,6 @@
// We mean it.
//
-#include "qv4global_p.h"
#include <QtCore/QBitArray>
#include <algorithm>
#include <vector>
diff --git a/src/qml/configure.json b/src/qml/configure.json
index c35f5be06b..3fc1fd528b 100644
--- a/src/qml/configure.json
+++ b/src/qml/configure.json
@@ -8,7 +8,6 @@
"commandline": {
"options": {
"qml-network": "boolean",
- "qml-tracing": "boolean",
"qml-debug": "boolean"
}
},
@@ -24,6 +23,52 @@
],
"qmake": "CONFIG += c++11"
}
+ },
+ "pointer_32bit": {
+ "label": "32bit pointers",
+ "type": "compile",
+ "test": {
+ "main": "static_assert(sizeof(void *) == 4, \"fail\");"
+ }
+ },
+ "pointer_64bit": {
+ "label": "64bit pointers",
+ "type": "compile",
+ "test": {
+ "main": "static_assert(sizeof(void *) == 8, \"fail\");"
+ }
+ },
+ "arm_thumb": {
+ "label": "THUMB mode on ARM",
+ "type": "compile",
+ "test": {
+ "main": [
+ "#if defined(thumb2) || defined(__thumb2__)",
+ "# define THUMB_OK",
+ "#elif (defined(__thumb) || defined(__thumb__)) && __TARGET_ARCH_THUMB-0 == 4",
+ "# define THUMB_OK",
+ "#elif defined(__ARM_ARCH_ISA_THUMB) && __ARM_ARCH_ISA_THUMB == 2",
+ "// clang 3.5 and later will set this if the core supports the Thumb-2 ISA.",
+ "# define THUMB_OK",
+ "#else",
+ "# error \"fail\"",
+ "#endif"
+ ]
+ }
+ },
+ "arm_fp": {
+ "label": "Sufficiently recent FPU on ARM",
+ "type": "compile",
+ "test": {
+ "main": [
+ "// if !defined(__ARM_FP) we might be on MSVC or we might have a device",
+ "// without an FPU.",
+ "// TODO: The latter case is not supported, but the test still succeeds.",
+ "#if defined(__ARM_FP) && (__ARM_FP <= 0x04)",
+ "# error \"fail\"",
+ "#endif"
+ ]
+ }
}
},
@@ -40,12 +85,26 @@
"condition": "features.network",
"output": [ "publicFeature" ]
},
- "qml-tracing": {
- "label": "QML tracing JIT support",
- "purpose": "Provides a JIT that uses trace information generated by the interpreter.",
+ "qml-jit": {
+ "label": "QML just-in-time compiler",
+ "purpose": "Provides a JIT for QML and JavaScript",
"section": "QML",
+ "condition": [
+ " (arch.i386 && tests.pointer_32bit && features.sse2)
+ || (arch.x86_64 && tests.pointer_64bit && features.sse2)
+ || (arch.arm && tests.pointer_32bit && tests.arm_fp && tests.arm_thumb
+ && (config.linux || config.ios || config.tvos || config.qnx))
+ || (arch.arm64 && tests.pointer_64bit && tests.arm_fp
+ && (config.linux || config.ios || config.tvos || config.qnx || config.integrity))"
+ ],
"output": [ "privateFeature" ],
- "autoDetect": false
+ "autoDetect": "!config.ios && !config.tvos",
+ "comment": "On arm and arm64 we need a specialization of cacheFlush() for each OS to be
+ enabeled. Therefore the config white list.
+ Also Mind that e.g. x86_32 has arch.x86_64 but 32bit pointers. Therefore
+ the checks for architecture and pointer size.
+ Finally, ios and tvos can technically use the JIT but Apple does not allow
+ it. Therefore, it's disabled by default."
},
"qml-debug": {
"label": "QML debugging and profiling support",
@@ -90,12 +149,6 @@
"section": "QML",
"output": [ "privateFeature" ]
},
- "qml-list-model": {
- "label": "QML list model",
- "purpose": "Provides the ListModel QML type.",
- "section": "QML",
- "output": [ "privateFeature" ]
- },
"qml-xml-http-request": {
"label": "QML XML http request",
"purpose": "Provides support for sending XML http requests.",
@@ -119,18 +172,19 @@
"condition": "features.animation",
"output": [ "privateFeature" ]
},
- "qml-delegate-model": {
- "label": "QML delegate model",
- "purpose": "Provides the DelegateModel QML type.",
- "section": "QML",
- "output": [ "privateFeature" ]
- },
"qml-worker-script": {
"label": "QML WorkerScript",
"purpose": "Enables the use of threads in QML.",
"section": "QML",
"condition": "features.thread",
"output": [ "privateFeature" ]
+ },
+ "qml-itemmodel": {
+ "label": "QML Item Model",
+ "purpose": "Provides the item model for item views in QML",
+ "section": "QML",
+ "condition": "features.itemmodel",
+ "output": [ "privateFeature" ]
}
},
@@ -140,12 +194,10 @@
"entries": [
"qml-network",
"qml-debug",
- "qml-tracing",
+ "qml-jit",
"qml-sequence-object",
- "qml-list-model",
"qml-xml-http-request",
- "qml-locale",
- "qml-delegate-model"
+ "qml-locale"
]
}
]
diff --git a/src/qml/debugger/debugger.pri b/src/qml/debugger/debugger.pri
index 202a46e550..1281886816 100644
--- a/src/qml/debugger/debugger.pri
+++ b/src/qml/debugger/debugger.pri
@@ -14,7 +14,6 @@ qtConfig(qml-debug) {
$$PWD/qqmldebugconnector.cpp \
$$PWD/qqmldebugservice.cpp \
$$PWD/qqmlabstractprofileradapter.cpp \
- $$PWD/qqmlmemoryprofiler.cpp \
$$PWD/qqmlprofiler.cpp \
$$PWD/qqmldebugserviceinterfaces.cpp
}
@@ -24,7 +23,6 @@ HEADERS += \
$$PWD/qqmldebugserviceinterfaces_p.h \
$$PWD/qqmldebugstatesdelegate_p.h \
$$PWD/qqmldebug.h \
- $$PWD/qqmlmemoryprofiler_p.h \
$$PWD/qqmlprofiler_p.h
INCLUDEPATH += $$PWD
diff --git a/src/qml/debugger/qqmlconfigurabledebugservice_p.h b/src/qml/debugger/qqmlconfigurabledebugservice_p.h
index 96ec46f475..38f41047c0 100644
--- a/src/qml/debugger/qqmlconfigurabledebugservice_p.h
+++ b/src/qml/debugger/qqmlconfigurabledebugservice_p.h
@@ -64,7 +64,7 @@ class QQmlConfigurableDebugService : public Base
{
protected:
QQmlConfigurableDebugService(float version, QObject *parent = nullptr) :
- Base(version, parent), m_configMutex(QMutex::Recursive)
+ Base(version, parent)
{
init();
}
@@ -103,7 +103,7 @@ protected:
emit Base::attachedToEngine(engine);
}
- QMutex m_configMutex;
+ QRecursiveMutex m_configMutex;
QList<QJSEngine *> m_waitingEngines;
bool m_waitingForConfiguration;
};
diff --git a/src/qml/debugger/qqmlmemoryprofiler.cpp b/src/qml/debugger/qqmlmemoryprofiler.cpp
deleted file mode 100644
index b89dbfd02d..0000000000
--- a/src/qml/debugger/qqmlmemoryprofiler.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qqmlmemoryprofiler_p.h"
-
-QT_BEGIN_NAMESPACE
-
-
-QQmlMemoryScope::LibraryState QQmlMemoryScope::state = QQmlMemoryScope::Unloaded;
-
-typedef void (qmlmemprofile_stats)(int *allocCount, int *bytesAllocated);
-typedef void (qmlmemprofile_clear)();
-typedef void (qmlmemprofile_enable)();
-typedef void (qmlmemprofile_disable)();
-typedef void (qmlmemprofile_push_location)(const char *filename, int lineNumber);
-typedef void (qmlmemprofile_pop_location)();
-typedef void (qmlmemprofile_save)(const char *filename);
-typedef int (qmlmemprofile_is_enabled)();
-
-static qmlmemprofile_stats *memprofile_stats;
-static qmlmemprofile_clear *memprofile_clear;
-static qmlmemprofile_enable *memprofile_enable;
-static qmlmemprofile_disable *memprofile_disable;
-static qmlmemprofile_push_location *memprofile_push_location;
-static qmlmemprofile_pop_location *memprofile_pop_location;
-static qmlmemprofile_save *memprofile_save;
-static qmlmemprofile_is_enabled *memprofile_is_enabled;
-
-#if QT_CONFIG(library)
-extern QFunctionPointer qt_linux_find_symbol_sys(const char *symbol);
-#endif
-
-bool QQmlMemoryScope::doOpenLibrary()
-{
-#if defined(Q_OS_LINUX) && QT_CONFIG(library)
- if (state == Unloaded) {
- memprofile_stats = (qmlmemprofile_stats *) qt_linux_find_symbol_sys("qmlmemprofile_stats");
- memprofile_clear = (qmlmemprofile_clear *) qt_linux_find_symbol_sys("qmlmemprofile_clear");
- memprofile_enable = (qmlmemprofile_enable *) qt_linux_find_symbol_sys("qmlmemprofile_enable");
- memprofile_disable = (qmlmemprofile_disable *) qt_linux_find_symbol_sys("qmlmemprofile_disable");
- memprofile_push_location = (qmlmemprofile_push_location *) qt_linux_find_symbol_sys("qmlmemprofile_push_location");
- memprofile_pop_location = (qmlmemprofile_pop_location *) qt_linux_find_symbol_sys("qmlmemprofile_pop_location");
- memprofile_save = (qmlmemprofile_save *) qt_linux_find_symbol_sys("qmlmemprofile_save");
- memprofile_is_enabled = (qmlmemprofile_is_enabled *) qt_linux_find_symbol_sys("qmlmemprofile_is_enabled");
-
- if (memprofile_stats && memprofile_clear && memprofile_enable && memprofile_disable &&
- memprofile_push_location && memprofile_pop_location && memprofile_save && memprofile_is_enabled)
- state = Loaded;
- else
- state = Failed;
- }
-#endif // Q_OS_LINUX
-
- return state == Loaded;
-}
-
-void QQmlMemoryScope::init(const char *string)
-{
- if (memprofile_is_enabled()) {
- memprofile_push_location(string, 0);
- pushed = true;
- }
-}
-
-void QQmlMemoryScope::done()
-{
- memprofile_pop_location();
-}
-
-bool QQmlMemoryProfiler::isEnabled()
-{
- if (QQmlMemoryScope::openLibrary())
- return memprofile_is_enabled();
-
- return false;
-}
-
-void QQmlMemoryProfiler::enable()
-{
- if (QQmlMemoryScope::openLibrary())
- memprofile_enable();
-}
-
-void QQmlMemoryProfiler::disable()
-{
- if (QQmlMemoryScope::openLibrary())
- memprofile_disable();
-}
-
-void QQmlMemoryProfiler::clear()
-{
- if (QQmlMemoryScope::openLibrary())
- memprofile_clear();
-}
-
-void QQmlMemoryProfiler::stats(int *allocCount, int *bytesAllocated)
-{
- if (QQmlMemoryScope::openLibrary())
- memprofile_stats(allocCount, bytesAllocated);
-}
-
-void QQmlMemoryProfiler::save(const char *filename)
-{
- if (QQmlMemoryScope::openLibrary())
- memprofile_save(filename);
-}
-
-QT_END_NAMESPACE
diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h
index d01e2bc429..d3eedab1c6 100644
--- a/src/qml/debugger/qqmlprofiler_p.h
+++ b/src/qml/debugger/qqmlprofiler_p.h
@@ -171,10 +171,11 @@ public:
: Location(ref->sourceLocation()), locationType(Binding), sent(false)
{
function = ref;
- function->compilationUnit->addref();
+ function->executableCompilationUnit()->addref();
}
- RefLocation(QV4::CompiledData::CompilationUnit *ref, const QUrl &url, const QV4::CompiledData::Object *obj, const QString &type)
+ RefLocation(QV4::ExecutableCompilationUnit *ref, const QUrl &url,
+ const QV4::CompiledData::Object *obj, const QString &type)
: Location(QQmlSourceLocation(type, obj->location.line, obj->location.column), url),
locationType(Creating), sent(false)
{
@@ -230,7 +231,7 @@ public:
switch (locationType) {
case Binding:
- function->compilationUnit->addref();
+ function->executableCompilationUnit()->addref();
break;
case Creating:
unit->addref();
@@ -254,7 +255,7 @@ public:
switch (locationType) {
case Binding:
- function->compilationUnit->release();
+ function->executableCompilationUnit()->release();
break;
case Creating:
unit->release();
@@ -284,7 +285,7 @@ public:
RangeType locationType;
union {
QV4::Function *function;
- QV4::CompiledData::CompilationUnit *unit;
+ QV4::ExecutableCompilationUnit *unit;
QQmlBoundSignalExpression *boundSignal;
QQmlDataBlob *blob;
void *something;
@@ -356,7 +357,7 @@ public:
}
void updateCreating(const QV4::CompiledData::Object *obj,
- QV4::CompiledData::CompilationUnit *ref,
+ QV4::ExecutableCompilationUnit *ref,
const QUrl &url, const QString &type)
{
quintptr locationId(id(obj));
@@ -492,7 +493,7 @@ public:
Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileCreating, profiler, endRange<QQmlProfilerDefinitions::Creating>());
}
- void update(QV4::CompiledData::CompilationUnit *ref, const QV4::CompiledData::Object *obj,
+ void update(QV4::ExecutableCompilationUnit *ref, const QV4::CompiledData::Object *obj,
const QString &typeName, const QUrl &url)
{
profiler->updateCreating(obj, ref, url, typeName);
diff --git a/src/qml/doc/images/cpp-qml-integration-flowchart.odg b/src/qml/doc/images/cpp-qml-integration-flowchart.odg
index f24021635e..026f89fd73 100644
--- a/src/qml/doc/images/cpp-qml-integration-flowchart.odg
+++ b/src/qml/doc/images/cpp-qml-integration-flowchart.odg
Binary files differ
diff --git a/src/qml/doc/images/cpp-qml-integration-flowchart.png b/src/qml/doc/images/cpp-qml-integration-flowchart.png
index 3649ff9e41..97f075f51e 100644
--- a/src/qml/doc/images/cpp-qml-integration-flowchart.png
+++ b/src/qml/doc/images/cpp-qml-integration-flowchart.png
Binary files differ
diff --git a/src/qml/doc/qtqml.qdocconf b/src/qml/doc/qtqml.qdocconf
index cb9fb575b2..74d0a3b27c 100644
--- a/src/qml/doc/qtqml.qdocconf
+++ b/src/qml/doc/qtqml.qdocconf
@@ -37,11 +37,17 @@ tagfile = ../../../doc/qtqml/qtqml.tags
depends += qtcore qtgui qtquick qtdoc qtlinguist qmake qtscript qtwidgets qtxmlpatterns qtquickcontrols
headerdirs += .. \
- ../../imports/models
+ ../../imports/models \
+ ../../qmlmodels \
+ ../../qml \
+ ../../qmlworkerscript
sourcedirs += .. \
../../imports/models \
- ../../imports/statemachine
+ ../../imports/statemachine \
+ ../../qmlmodels \
+ ../../qml \
+ ../../qmlworkerscript
exampledirs += ../../../examples/qml \
../ \
diff --git a/src/qml/doc/snippets/qml/events.qml b/src/qml/doc/snippets/qml/events.qml
index 90bf5d7b3d..f437e32890 100644
--- a/src/qml/doc/snippets/qml/events.qml
+++ b/src/qml/doc/snippets/qml/events.qml
@@ -59,8 +59,8 @@ Rectangle {
//! [signal declaration]
signal trigger
- signal send (string notice)
- signal perform (string task, variant object)
+ signal send(notice: string)
+ signal perform(task: string, object: variant)
//! [signal declaration]
//! [signal handler declaration]
@@ -88,7 +88,7 @@ Rectangle {
Rectangle {
id: messenger
- signal send( string person, string notice)
+ signal send(person: string, notice: string)
onSend: {
console.log("For " + person + ", the notice is: " + notice)
@@ -102,7 +102,7 @@ Rectangle {
Rectangle {
id: relay
- signal send( string person, string notice)
+ signal send(person: string, notice: string)
onSend: console.log("Send signal to: " + person + ", " + notice)
Component.onCompleted: {
diff --git a/src/qml/doc/snippets/qml/qtbinding/functions-qml/MyItem.qml b/src/qml/doc/snippets/qml/qtbinding/functions-qml/MyItem.qml
index 073a5dc361..59907c38e7 100644
--- a/src/qml/doc/snippets/qml/qtbinding/functions-qml/MyItem.qml
+++ b/src/qml/doc/snippets/qml/qtbinding/functions-qml/MyItem.qml
@@ -52,7 +52,7 @@
import QtQuick 2.0
Item {
- function myQmlFunction(msg) {
+ function myQmlFunction(msg: string) : string {
console.log("Got message:", msg)
return "some return value"
}
diff --git a/src/qml/doc/snippets/qml/qtbinding/functions-qml/main.cpp b/src/qml/doc/snippets/qml/qtbinding/functions-qml/main.cpp
index c82f71f749..a562eae2b4 100644
--- a/src/qml/doc/snippets/qml/qtbinding/functions-qml/main.cpp
+++ b/src/qml/doc/snippets/qml/qtbinding/functions-qml/main.cpp
@@ -60,13 +60,13 @@ QQmlEngine engine;
QQmlComponent component(&engine, "MyItem.qml");
QObject *object = component.create();
-QVariant returnedValue;
-QVariant msg = "Hello from C++";
+QString returnedValue;
+QString msg = "Hello from C++";
QMetaObject::invokeMethod(object, "myQmlFunction",
- Q_RETURN_ARG(QVariant, returnedValue),
- Q_ARG(QVariant, msg));
+ Q_RETURN_ARG(QString, returnedValue),
+ Q_ARG(QString, msg));
-qDebug() << "QML function returned:" << returnedValue.toString();
+qDebug() << "QML function returned:" << returnedValue;
delete object;
//![0]
}
diff --git a/src/qml/doc/snippets/qml/qtbinding/signals-qml/MyItem.qml b/src/qml/doc/snippets/qml/qtbinding/signals-qml/MyItem.qml
index eebf2db832..aadc89b72c 100644
--- a/src/qml/doc/snippets/qml/qtbinding/signals-qml/MyItem.qml
+++ b/src/qml/doc/snippets/qml/qtbinding/signals-qml/MyItem.qml
@@ -55,7 +55,7 @@ Item {
id: item
width: 100; height: 100
- signal qmlSignal(string msg)
+ signal qmlSignal(msg: string)
MouseArea {
anchors.fill: parent
diff --git a/src/qml/doc/snippets/qml/statemachine/guardcondition.qml b/src/qml/doc/snippets/qml/statemachine/guardcondition.qml
index 8388b96c21..f1ec89b6ba 100644
--- a/src/qml/doc/snippets/qml/statemachine/guardcondition.qml
+++ b/src/qml/doc/snippets/qml/statemachine/guardcondition.qml
@@ -70,7 +70,7 @@ Rectangle {
}
}
// define the signal the SignalTransition is connected with
- signal mysignal(string mystr)
+ signal mysignal(mystr: string)
// on clicking the button emit the signal with a single string argument
onClicked: button.mysignal("test")
}
diff --git a/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc b/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc
index 9c33979f40..0a824bb5b5 100644
--- a/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc
+++ b/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc
@@ -166,9 +166,12 @@ updated, and any \c onButtonTextChanged handlers would not be called.
\section2 Invoking QML Methods
-All QML methods are exposed to the meta-object system and can be called from C++
-using QMetaObject::invokeMethod(). Method parameters and return values passed
-from QML are always translated into QVariant values in C++.
+All QML methods are exposed to the meta-object system and can be called from
+C++ using QMetaObject::invokeMethod(). You can specify types for the parameters
+and the return value after the colon character, as shown in the code snippet
+below. This can be useful, for example, when you want to connect a signal in
+C++ with a certain signature to a QML-defined method. If you omit the types,
+the C++ signature will use QVariant.
Here is a C++ application that calls a QML method using
QMetaObject::invokeMethod():
@@ -182,9 +185,12 @@ QMetaObject::invokeMethod():
\li \snippet qml/qtbinding/functions-qml/main.cpp 0
\endtable
-Notice the Q_RETURN_ARG() and Q_ARG() arguments for QMetaObject::invokeMethod()
-must be specified as QVariant types, as this is the generic data type used for
-QML method parameters and return values.
+Notice the parameter and return type specified after the colon. You can use \l
+{QML Basic Types}{basic 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.
\section2 Connecting to QML Signals
@@ -210,9 +216,8 @@ QObject::connect(), so that the \c cppSlot() method is called whenever the
\snippet qml/qtbinding/signals-qml/main.cpp 0
\endtable
-When a QML object type is used as a signal parameter, the parameter should
-use \l var as the type, and the value should be received in C++ using the
-QVariant type:
+A QML object type in a signal parameter is translated to a pointer to the class
+in C++:
\table
\row
@@ -226,7 +231,7 @@ QVariant type:
id: item
width: 100; height: 100
- signal qmlSignal(var anObject)
+ signal qmlSignal(anObject: Item)
MouseArea {
anchors.fill: parent
@@ -241,18 +246,16 @@ QVariant type:
{
Q_OBJECT
public slots:
- void cppSlot(const QVariant &v) {
- qDebug() << "Called the C++ slot with value:" << v;
+ void cppSlot(QQuickItem *item) {
+ qDebug() << "Called the C++ slot with item:" << item;
- QQuickItem *item =
- qobject_cast<QQuickItem*>(v.value<QObject*>());
qDebug() << "Item dimensions:" << item->width()
<< item->height();
}
};
int main(int argc, char *argv[]) {
- QApplication app(argc, argv);
+ QGuiApplication app(argc, argv);
QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
QObject *item = view.rootObject();
diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc
index 969dd51433..b2d322465d 100644
--- a/src/qml/doc/src/qmlfunctions.qdoc
+++ b/src/qml/doc/src/qmlfunctions.qdoc
@@ -274,17 +274,16 @@
*/
/*!
- \fn int qmlRegisterType()
- \relates QQmlEngine
- \overload
+ \fn int qmlRegisterAnonymousType(const char *uri, int versionMajor)
- This template function registers the C++ type in the QML
- system. Instances of this type cannot be created from the QML
- system.
+ This template function registers the C++ type in the QML system as an anonymous type. The
+ resulting QML type does not have a name. Therefore, instances of this type cannot be created from
+ the QML system. You can, however, access instances of the type when they are exposed as properties
+ of other types.
- This function should be used when the type will not be referenced by name.
- Specifically, it has to be used for C++ types that are used as the left-hand
- side of a property binding.
+ Use this function when the type will not be referenced by name, specifically for C++ types that
+ are used on the left-hand side of a property binding. To indicate to which module the type belongs
+ use \a uri and \a versionMajor.
For example, consider the following two classes:
@@ -344,18 +343,29 @@
\code
qmlRegisterType<Foo>("App", 1, 0, "Foo");
- qmlRegisterType<Bar>();
+ qmlRegisterAnonymousType<Bar>("App", 1);
\endcode
As the \c Foo type is instantiated in QML, it must be registered
- with the version of \l qmlRegisterType() that takes an import URI.
+ with the version of \l qmlRegisterType() that takes an element name.
Returns the QML type id.
+ \since 5.14
\sa {Choosing the Correct Integration Method Between C++ and QML}
*/
/*!
+ \fn int qmlRegisterType()
+ \relates QQmlEngine
+ \overload
+ \deprecated
+
+ Do not use this function. For anonymous type registrations, use \l qmlRegisterAnonymousType(),
+ and make sure to provide a URI and a major version.
+*/
+
+/*!
\fn int qmlRegisterInterface(const char *typeName)
\relates QQmlEngine
@@ -448,7 +458,7 @@
*/
/*!
- \fn template<typename T> QObject *qmlAttachedPropertiesObject(const QObject *attachee, bool create = true)
+ \fn template<typename T> QObject *qmlAttachedPropertiesObject(const QObject *attachee, bool create)
\relates QQmlEngine
The form of this template function is:
@@ -487,8 +497,7 @@
A QObject singleton type may be referenced via the type name with which it was registered, and this
typename may be used as the target in a \l Connections type or otherwise used as any other type id would.
- One exception to this is that a QObject singleton type property may not be aliased (because the
- singleton type name does not identify an object within the same component as any other item).
+ One exception to this is that a QObject singleton type property may not be aliased.
\b{NOTE:} A QObject singleton type instance returned from a singleton type provider is owned by
the QML engine unless the object has explicit QQmlEngine::CppOwnership flag set.
@@ -538,7 +547,7 @@
Alternatively, you can use a C++11 lambda:
\code
- qmlRegisterSingletonType<SingletonTypeExample>("Qt.example.qjsvalueApi", 1, 0, "MyApi", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * {
+ qmlRegisterSingletonType<SingletonTypeExample>("Qt.example.qobjectSingleton", 1, 0, "MyApi", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * {
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
@@ -564,6 +573,14 @@
\sa {Choosing the Correct Integration Method Between C++ and QML}
*/
+
+/*!
+ \fn int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, std::function<QObject*(QQmlEngine *, QJSEngine *)> callback)
+ \relates QQmlEngine
+
+ \overload qmlRegisterSingletonType
+*/
+
/*!
\fn int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)
\relates QQmlEngine
@@ -611,6 +628,99 @@
*/
/*!
+ \fn int qmlRegisterSingletonInstance(const char *uri, int versionMajor, int versionMinor, const char *typeName, QObject* cppObject)
+ \relates QQmlEngine
+ \since 5.14
+
+ This function is used to register a singleton object \a cppObject, with a
+ particular \a uri and \a typeName. Its version is a combination of \a
+ versionMajor and \a versionMinor.
+
+ Installing a singleton type into a URI allows you to provide arbitrary
+ functionality (methods and properties) to QML code without requiring
+ individual instances of the type to be instantiated by the client.
+
+ Use this function to register an object of the given type T as a singleton
+ type.
+
+ A QObject singleton type may be referenced via the type name with which it
+ was registered; in turn this type name may be used as the target in a \l
+ Connections type, or like any other type ID. However, there's one
+ exception: a QObject singleton type property can't be aliased because the
+ singleton type name does not identify an object within the same component
+ as any other item.
+
+ \note \a cppObject must outlive the QML engine in which it is used.
+ Moreover, \cppObject must have the same thread affinity as the engine. If
+ you want separate singleton instances for multiple engines, you need to use
+ \l {qmlRegisterSingletonType}. See \l{Threads and QObjects} for more
+ information about thread safety.
+
+ Usage:
+ \code
+ // First, define your QObject which provides the functionality.
+ class SingletonTypeExample : public QObject
+ {
+ Q_OBJECT
+ Q_PROPERTY(int someProperty READ someProperty WRITE setSomeProperty NOTIFY somePropertyChanged)
+
+ public:
+ explicit SingletonTypeExample(QObject* parent = nullptr) : QObject(parent) {}
+
+ Q_INVOKABLE int doSomething()
+ {
+ setSomeProperty(5);
+ return m_someProperty;
+ }
+
+ int someProperty() const { return m_someProperty; }
+ void setSomeProperty(int val) {
+ if (m_someProperty != val) {
+ m_someProperty = val;
+ emit somePropertyChanged(val);
+ }
+ }
+
+ signals:
+ void somePropertyChanged(int newValue);
+
+ private:
+ int m_someProperty = 0;
+ };
+ \endcode
+
+ \code
+ // Second, create an instance of the object
+
+ // allocate example before the engine to ensure that it outlives it
+ QScopedPointer<SingletonTypeExample> example(new SingletonTypeExample);
+ QQmlEngine engine;
+
+ // Third, register the singleton type provider with QML by calling this
+ // function in an initialization function.
+ qmlRegisterSingletonInstance("Qt.example.qobjectSingleton", 1, 0, "MyApi", example.get());
+ \endcode
+
+
+ In order to use the registered singleton type in QML, you must import the
+ URI with the corresponding version.
+ \qml
+ import QtQuick 2.0
+ import Qt.example.qobjectSingleton 1.0
+ Item {
+ id: root
+ property int someValue: MyApi.someProperty
+
+ Component.onCompleted: {
+ console.log(MyApi.doSomething())
+ }
+ }
+ \endqml
+
+ \sa qmlRegisterSingletonType
+ */
+
+/*!
\fn int qmlRegisterType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName);
\relates QQmlEngine
diff --git a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc
index c4ecaf367c..401e099ebf 100644
--- a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc
+++ b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc
@@ -464,22 +464,24 @@ Unlike an ordinary property, an alias has the following restrictions:
must be provided when the alias is first declared.
\li It cannot refer to \l {Attached Properties and Attached Signal Handlers}
{attached properties}.
-\li It cannot refer to grouped properties; the following code will not work:
+\li It cannot refer to properties inside a hierarchy with depth 3 or greater. The
+ following code will not work:
\code
- property alias color: rectangle.border.color
+ property alias color: myItem.myRect.border.color
- Rectangle {
- id: rectangle
+ Item {
+ id: myItem
+ property Rectangle myRect
}
\endcode
- However, aliases to \l {QML Basic Types}{value type} properties do work:
+ However, aliases to properties that are up to two levels deep will work.
+
\code
- property alias rectX: object.rectProperty.x
+ property alias color: rectangle.border.color
- Item {
- id: object
- property rect rectProperty
+ Rectangle {
+ id: rectangle
}
\endcode
\endlist
diff --git a/src/qml/jit/jit.pri b/src/qml/jit/jit.pri
index 2c664af188..503ce0ebcd 100644
--- a/src/qml/jit/jit.pri
+++ b/src/qml/jit/jit.pri
@@ -2,13 +2,11 @@ INCLUDEPATH += $$PWD
INCLUDEPATH += $$OUT_PWD
SOURCES += \
- $$PWD/qv4jithelpers.cpp \
$$PWD/qv4baselinejit.cpp \
$$PWD/qv4baselineassembler.cpp \
$$PWD/qv4assemblercommon.cpp
HEADERS += \
- $$PWD/qv4jithelpers_p.h \
$$PWD/qv4baselinejit_p.h \
$$PWD/qv4baselineassembler_p.h \
$$PWD/qv4assemblercommon_p.h
diff --git a/src/qml/jit/qv4assemblercommon.cpp b/src/qml/jit/qv4assemblercommon.cpp
index dd810d9d70..800ee22cd7 100644
--- a/src/qml/jit/qv4assemblercommon.cpp
+++ b/src/qml/jit/qv4assemblercommon.cpp
@@ -53,8 +53,6 @@
#undef ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES
-#ifdef V4_ENABLE_JIT
-
QT_BEGIN_NAMESPACE
namespace QV4 {
namespace JIT {
@@ -366,5 +364,3 @@ void PlatformAssemblerCommon::storeInt32AsValue(int srcInt, Address destAddr)
} // QV4 namepsace
QT_END_NAMESPACE
-
-#endif // V4_ENABLE_JIT
diff --git a/src/qml/jit/qv4assemblercommon_p.h b/src/qml/jit/qv4assemblercommon_p.h
index 67c7c9cd82..b18d082be6 100644
--- a/src/qml/jit/qv4assemblercommon_p.h
+++ b/src/qml/jit/qv4assemblercommon_p.h
@@ -58,7 +58,7 @@
#include <wtf/Vector.h>
#include <assembler/MacroAssembler.h>
-#ifdef V4_ENABLE_JIT
+QT_REQUIRE_CONFIG(qml_jit);
QT_BEGIN_NAMESPACE
@@ -449,7 +449,7 @@ public:
// r6 is used by MacroAssemblerARMv7
static const RegisterID JSStackFrameRegister = JSC::ARMRegisters::r8;
static const RegisterID CppStackFrameRegister = JSC::ARMRegisters::r10;
-#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP)
+#if CPU(ARM_THUMB2)
static const RegisterID FramePointerRegister = JSC::ARMRegisters::r7;
static const RegisterID EngineRegister = JSC::ARMRegisters::r11;
#else // Thumbs down
@@ -619,6 +619,9 @@ public:
for (Jump j : catchyJumps)
j.link(this);
+ // We don't need to check for isInterrupted here because if that is set,
+ // then the first checkException() in any exception handler will find another "exception"
+ // and jump out of the exception handler.
loadPtr(exceptionHandlerAddress(), ScratchRegister);
Jump exitFunction = branchPtr(Equal, ScratchRegister, TrustedImmPtr(0));
loadUndefined();
@@ -633,6 +636,8 @@ public:
void checkException()
{
+ // This actually reads 4 bytes, starting at hasException.
+ // Therefore, it also reads the isInterrupted flag, and triggers an exception on that.
addCatchyJump(
branch32(NotEqual,
Address(EngineRegister, offsetof(EngineBase, hasException)),
@@ -735,6 +740,4 @@ private:
QT_END_NAMESPACE
-#endif // V4_ENABLE_JIT
-
#endif // QV4PLATFORMASSEMBLER_P_H
diff --git a/src/qml/jit/qv4baselineassembler.cpp b/src/qml/jit/qv4baselineassembler.cpp
index 0751704a29..59de86a85d 100644
--- a/src/qml/jit/qv4baselineassembler.cpp
+++ b/src/qml/jit/qv4baselineassembler.cpp
@@ -55,8 +55,6 @@
#undef ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES
-#ifdef V4_ENABLE_JIT
-
QT_BEGIN_NAMESPACE
namespace QV4 {
namespace JIT {
@@ -943,7 +941,7 @@ void BaselineAssembler::uminus()
saveAccumulatorInFrame();
pasm()->prepareCallWithArgCount(1);
pasm()->passAccumulatorAsArg(0);
- ASM_GENERATE_RUNTIME_CALL(Runtime::method_uMinus, CallResultDestination::InAccumulator);
+ ASM_GENERATE_RUNTIME_CALL(UMinus, CallResultDestination::InAccumulator);
checkException();
}
@@ -1044,7 +1042,7 @@ void BaselineAssembler::add(int lhs)
pasm()->passAccumulatorAsArg(2);
pasm()->passJSSlotAsArg(lhs, 1);
pasm()->passEngineAsArg(0);
- ASM_GENERATE_RUNTIME_CALL(Runtime::method_add, CallResultDestination::InAccumulator);
+ ASM_GENERATE_RUNTIME_CALL(Add, CallResultDestination::InAccumulator);
checkException();
// done.
@@ -1196,7 +1194,7 @@ void BaselineAssembler::mul(int lhs)
pasm()->prepareCallWithArgCount(2);
pasm()->passAccumulatorAsArg(1);
pasm()->passJSSlotAsArg(lhs, 0);
- ASM_GENERATE_RUNTIME_CALL(Runtime::method_mul, CallResultDestination::InAccumulator);
+ ASM_GENERATE_RUNTIME_CALL(Mul, CallResultDestination::InAccumulator);
checkException();
// done.
@@ -1209,7 +1207,7 @@ void BaselineAssembler::div(int lhs)
pasm()->prepareCallWithArgCount(2);
pasm()->passAccumulatorAsArg(1);
pasm()->passJSSlotAsArg(lhs, 0);
- ASM_GENERATE_RUNTIME_CALL(Runtime::method_div, CallResultDestination::InAccumulator);
+ ASM_GENERATE_RUNTIME_CALL(Div, CallResultDestination::InAccumulator);
checkException();
}
@@ -1219,7 +1217,7 @@ void BaselineAssembler::mod(int lhs)
pasm()->prepareCallWithArgCount(2);
pasm()->passAccumulatorAsArg(1);
pasm()->passJSSlotAsArg(lhs, 0);
- ASM_GENERATE_RUNTIME_CALL(Runtime::method_mod, CallResultDestination::InAccumulator);
+ ASM_GENERATE_RUNTIME_CALL(Mod, CallResultDestination::InAccumulator);
checkException();
}
@@ -1239,7 +1237,7 @@ void BaselineAssembler::sub(int lhs)
pasm()->prepareCallWithArgCount(2);
pasm()->passAccumulatorAsArg(1);
pasm()->passJSSlotAsArg(lhs, 0);
- ASM_GENERATE_RUNTIME_CALL(Runtime::method_sub, CallResultDestination::InAccumulator);
+ ASM_GENERATE_RUNTIME_CALL(Sub, CallResultDestination::InAccumulator);
checkException();
// done.
@@ -1269,7 +1267,7 @@ void BaselineAssembler::cmpeqInt(int lhs)
else
pasm()->move(PlatformAssembler::StackPointerRegister, pasm()->registerForArg(1));
pasm()->pushAccumulatorAsArg(0);
- pasm()->callRuntimeUnchecked("Runtime::method_equal", (void*)Runtime::method_equal);
+ pasm()->callRuntimeUnchecked("Equal", (void*)Runtime::Equal::call);
pasm()->saveReturnValueInAccumulator();
if (PlatformAssembler::ArgInRegCount < 2)
pasm()->addPtr(TrustedImm32(2 * PlatformAssembler::PointerSize), PlatformAssembler::StackPointerRegister);
@@ -1293,7 +1291,7 @@ void BaselineAssembler::cmpneInt(int lhs)
else
pasm()->move(PlatformAssembler::StackPointerRegister, pasm()->registerForArg(1));
pasm()->pushAccumulatorAsArg(0);
- pasm()->callRuntimeUnchecked("Runtime::method_notEqual", (void*)Runtime::method_notEqual);
+ pasm()->callRuntimeUnchecked("NotEqual", (void*)Runtime::NotEqual::call);
pasm()->saveReturnValueInAccumulator();
if (PlatformAssembler::ArgInRegCount < 2)
pasm()->addPtr(TrustedImm32(2 * PlatformAssembler::PointerSize), PlatformAssembler::StackPointerRegister);
@@ -1314,7 +1312,6 @@ void BaselineAssembler::cmp(int cond, CmpFunc function, const char *functionName
pasm()->compare32(c, PlatformAssembler::ScratchRegister,
PlatformAssembler::AccumulatorRegisterValue,
PlatformAssembler::AccumulatorRegisterValue);
- pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
return PlatformAssembler::Jump();
});
@@ -1326,60 +1323,58 @@ void BaselineAssembler::cmp(int cond, CmpFunc function, const char *functionName
callRuntime(functionName, reinterpret_cast<void*>(function), CallResultDestination::InAccumulator);
checkException();
- pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
// done.
done.link(pasm());
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
}
void BaselineAssembler::cmpeq(int lhs)
{
- cmp(PlatformAssembler::Equal, &Runtime::method_compareEqual,
- "Runtime::method_compareEqual", lhs);
+ cmp(PlatformAssembler::Equal, &Runtime::CompareEqual::call,
+ "CompareEqual", lhs);
}
void BaselineAssembler::cmpne(int lhs)
{
- cmp(PlatformAssembler::NotEqual, &Runtime::method_compareNotEqual,
- "Runtime::method_compareNotEqual", lhs);
+ cmp(PlatformAssembler::NotEqual, &Runtime::CompareNotEqual::call,
+ "CompareNotEqual", lhs);
}
void BaselineAssembler::cmpgt(int lhs)
{
- cmp(PlatformAssembler::GreaterThan, &Runtime::method_compareGreaterThan,
- "Runtime::method_compareGreaterThan", lhs);
+ cmp(PlatformAssembler::GreaterThan, &Runtime::CompareGreaterThan::call,
+ "CompareGreaterThan", lhs);
}
void BaselineAssembler::cmpge(int lhs)
{
- cmp(PlatformAssembler::GreaterThanOrEqual, &Runtime::method_compareGreaterEqual,
- "Runtime::method_compareGreaterEqual", lhs);
+ cmp(PlatformAssembler::GreaterThanOrEqual, &Runtime::CompareGreaterEqual::call,
+ "CompareGreaterEqual", lhs);
}
void BaselineAssembler::cmplt(int lhs)
{
- cmp(PlatformAssembler::LessThan, &Runtime::method_compareLessThan,
- "Runtime::method_compareLessThan", lhs);
+ cmp(PlatformAssembler::LessThan, &Runtime::CompareLessThan::call,
+ "CompareLessThan", lhs);
}
void BaselineAssembler::cmple(int lhs)
{
- cmp(PlatformAssembler::LessThanOrEqual, &Runtime::method_compareLessEqual,
- "Runtime::method_compareLessEqual", lhs);
+ cmp(PlatformAssembler::LessThanOrEqual, &Runtime::CompareLessEqual::call,
+ "CompareLessEqual", lhs);
}
void BaselineAssembler::cmpStrictEqual(int lhs)
{
- cmp(PlatformAssembler::Equal, &RuntimeHelpers::strictEqual,
+ cmp(PlatformAssembler::Equal, &Runtime::CompareStrictEqual::call,
"RuntimeHelpers::strictEqual", lhs);
}
void BaselineAssembler::cmpStrictNotEqual(int lhs)
{
- cmp(PlatformAssembler::Equal, &RuntimeHelpers::strictEqual,
- "RuntimeHelpers::strictEqual", lhs);
- pasm()->xor32(TrustedImm32(1), PlatformAssembler::AccumulatorRegisterValue);
- pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
+ cmp(PlatformAssembler::NotEqual, &Runtime::CompareStrictNotEqual::call,
+ "RuntimeHelpers::strictNotEqual", lhs);
}
int BaselineAssembler::jump(int offset)
@@ -1487,7 +1482,7 @@ void BaselineAssembler::loadAccumulatorFromFrame()
static ReturnedValue TheJitIs__Tail_Calling__ToTheRuntimeSoTheJitFrameIsMissing(CppStackFrame *frame, ExecutionEngine *engine)
{
- return Runtime::method_tailCall(frame, engine);
+ return Runtime::TailCall::call(frame, engine);
}
void BaselineAssembler::jsTailCall(int func, int thisObject, int argc, int argv)
@@ -1594,9 +1589,8 @@ void BaselineAssembler::pushCatchContext(int index, int name)
pasm()->prepareCallWithArgCount(3);
pasm()->passInt32AsArg(name, 2);
pasm()->passInt32AsArg(index, 1);
- pasm()->passJSSlotAsArg(CallData::Context, 0);
- ASM_GENERATE_RUNTIME_CALL(Runtime::method_createCatchContext, CallResultDestination::InAccumulator);
- pasm()->storeAccumulator(pasm()->contextAddress());
+ pasm()->passEngineAsArg(0);
+ ASM_GENERATE_RUNTIME_CALL(PushCatchContext, CallResultDestination::Ignore);
}
void BaselineAssembler::popContext()
@@ -1615,7 +1609,7 @@ void BaselineAssembler::deadTemporalZoneCheck(int offsetForSavedIP, int variable
prepareCallWithArgCount(2);
passInt32AsArg(variableName, 1);
passEngineAsArg(0);
- ASM_GENERATE_RUNTIME_CALL(Runtime::method_throwReferenceError, CallResultDestination::Ignore);
+ ASM_GENERATE_RUNTIME_CALL(ThrowReferenceError, CallResultDestination::Ignore);
gotoCatchException();
valueIsAliveJump.link(pasm());
}
@@ -1629,5 +1623,3 @@ void BaselineAssembler::ret()
} // QV4 namepsace
QT_END_NAMESPACE
-
-#endif // V4_ENABLE_JIT
diff --git a/src/qml/jit/qv4baselineassembler_p.h b/src/qml/jit/qv4baselineassembler_p.h
index 97cb522244..33fd288ac3 100644
--- a/src/qml/jit/qv4baselineassembler_p.h
+++ b/src/qml/jit/qv4baselineassembler_p.h
@@ -55,6 +55,8 @@
#include <private/qv4function_p.h>
#include <QHash>
+QT_REQUIRE_CONFIG(qml_jit);
+
QT_BEGIN_NAMESPACE
namespace QV4 {
@@ -65,7 +67,7 @@ namespace JIT {
#define GENERATE_RUNTIME_CALL(function, destination) \
callRuntime(JIT_STRINGIFY(function), \
- reinterpret_cast<void *>(&function), \
+ reinterpret_cast<void *>(&Runtime::function::call), \
destination)
#define GENERATE_TAIL_CALL(function) \
tailCallRuntime(JIT_STRINGIFY(function), \
diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp
index d8d4e36f35..fcaa87290e 100644
--- a/src/qml/jit/qv4baselinejit.cpp
+++ b/src/qml/jit/qv4baselinejit.cpp
@@ -38,13 +38,10 @@
****************************************************************************/
#include "qv4baselinejit_p.h"
-#include "qv4jithelpers_p.h"
#include "qv4baselineassembler_p.h"
#include <private/qv4lookup_p.h>
#include <private/qv4generatorobject_p.h>
-#ifdef V4_ENABLE_JIT
-
QT_USE_NAMESPACE
using namespace QV4;
using namespace QV4::JIT;
@@ -52,7 +49,7 @@ using namespace QV4::Moth;
BaselineJIT::BaselineJIT(Function *function)
: function(function)
- , as(new BaselineAssembler(function->compilationUnit->constants))
+ , as(new BaselineAssembler(&(function->compilationUnit->constants->asValue<Value>())))
{}
BaselineJIT::~BaselineJIT()
@@ -78,10 +75,11 @@ void BaselineJIT::generate()
#define STORE_IP() as->storeInstructionPointer(nextInstructionOffset())
#define STORE_ACC() as->saveAccumulatorInFrame()
#define LOAD_ACC() as->loadAccumulatorFromFrame()
-#define BASELINEJIT_GENERATE_RUNTIME_CALL(function, destination) \
- as->GENERATE_RUNTIME_CALL(function, destination)
-#define BASELINEJIT_GENERATE_TAIL_CALL(function) \
- as->GENERATE_TAIL_CALL(function)
+#define BASELINEJIT_GENERATE_RUNTIME_CALL(function, destination) { \
+ as->GENERATE_RUNTIME_CALL(function, destination); \
+ if (Runtime::function::throws) \
+ as->checkException(); \
+ else {} } // this else prevents else statements after the macro from attaching to the if above
void BaselineJIT::generate_Ret()
{
@@ -152,7 +150,7 @@ void BaselineJIT::generate_LoadImport(int index)
as->loadImport(index);
}
-void BaselineJIT::generate_LoadLocal(int index, int /*traceSlot*/)
+void BaselineJIT::generate_LoadLocal(int index)
{
as->loadLocal(index);
}
@@ -163,7 +161,7 @@ void BaselineJIT::generate_StoreLocal(int index)
as->storeLocal(index);
}
-void BaselineJIT::generate_LoadScopedLocal(int scope, int index, int /*traceSlot*/)
+void BaselineJIT::generate_LoadScopedLocal(int scope, int index)
{
as->loadLocal(index, scope);
}
@@ -184,7 +182,7 @@ void BaselineJIT::generate_MoveRegExp(int regExpId, int destReg)
as->prepareCallWithArgCount(2);
as->passInt32AsArg(regExpId, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_regexpLiteral, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(RegexpLiteral, CallResultDestination::InAccumulator);
as->storeReg(destReg);
}
@@ -193,37 +191,33 @@ void BaselineJIT::generate_LoadClosure(int value)
as->prepareCallWithArgCount(2);
as->passInt32AsArg(value, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_closure, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Closure, CallResultDestination::InAccumulator);
}
-void BaselineJIT::generate_LoadName(int name, int /*traceSlot*/)
+void BaselineJIT::generate_LoadName(int name)
{
STORE_IP();
as->prepareCallWithArgCount(2);
as->passInt32AsArg(name, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadName, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(LoadName, CallResultDestination::InAccumulator);
}
-void BaselineJIT::generate_LoadGlobalLookup(int index, int /*traceSlot*/)
+void BaselineJIT::generate_LoadGlobalLookup(int index)
{
as->prepareCallWithArgCount(3);
as->passInt32AsArg(index, 2);
- as->passEngineAsArg(1);
- as->passFunctionAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::loadGlobalLookup, CallResultDestination::InAccumulator);
- as->checkException();
+ as->passFunctionAsArg(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(LoadGlobalLookup, CallResultDestination::InAccumulator);
}
-void BaselineJIT::generate_LoadQmlContextPropertyLookup(int index, int /*traceSlot*/)
+void BaselineJIT::generate_LoadQmlContextPropertyLookup(int index)
{
- as->prepareCallWithArgCount(3);
- as->passInt32AsArg(index, 2);
- as->passEngineAsArg(1);
- as->passFunctionAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::loadQmlContextPropertyLookup, CallResultDestination::InAccumulator);
- as->checkException();
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(index, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlContextPropertyLookup, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_StoreNameSloppy(int name)
@@ -234,8 +228,7 @@ void BaselineJIT::generate_StoreNameSloppy(int name)
as->passAccumulatorAsArg(2);
as->passInt32AsArg(name, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeNameSloppy, CallResultDestination::Ignore);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(StoreNameSloppy, CallResultDestination::Ignore);
LOAD_ACC();
}
@@ -247,12 +240,11 @@ void BaselineJIT::generate_StoreNameStrict(int name)
as->passAccumulatorAsArg(2);
as->passInt32AsArg(name, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeNameStrict, CallResultDestination::Ignore);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(StoreNameStrict, CallResultDestination::Ignore);
LOAD_ACC();
}
-void BaselineJIT::generate_LoadElement(int base, int /*traceSlot*/)
+void BaselineJIT::generate_LoadElement(int base)
{
STORE_IP();
STORE_ACC();
@@ -260,11 +252,10 @@ void BaselineJIT::generate_LoadElement(int base, int /*traceSlot*/)
as->passAccumulatorAsArg(2);
as->passJSSlotAsArg(base, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadElement, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(LoadElement, CallResultDestination::InAccumulator);
}
-void BaselineJIT::generate_StoreElement(int base, int index, int /*traceSlot*/)
+void BaselineJIT::generate_StoreElement(int base, int index)
{
STORE_IP();
STORE_ACC();
@@ -273,12 +264,11 @@ void BaselineJIT::generate_StoreElement(int base, int index, int /*traceSlot*/)
as->passJSSlotAsArg(index, 2);
as->passJSSlotAsArg(base, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeElement, CallResultDestination::Ignore);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(StoreElement, CallResultDestination::Ignore);
LOAD_ACC();
}
-void BaselineJIT::generate_LoadProperty(int name, int /*traceSlot*/)
+void BaselineJIT::generate_LoadProperty(int name)
{
STORE_IP();
STORE_ACC();
@@ -286,21 +276,19 @@ void BaselineJIT::generate_LoadProperty(int name, int /*traceSlot*/)
as->passInt32AsArg(name, 2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadProperty, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(LoadProperty, CallResultDestination::InAccumulator);
}
-void BaselineJIT::generate_GetLookup(int index, int /*traceSlot*/)
+void BaselineJIT::generate_GetLookup(int index)
{
STORE_IP();
STORE_ACC();
as->prepareCallWithArgCount(4);
as->passInt32AsArg(index, 3);
as->passAccumulatorAsArg(2);
- as->passEngineAsArg(1);
- as->passFunctionAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::getLookup, CallResultDestination::InAccumulator);
- as->checkException();
+ as->passFunctionAsArg(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(GetLookup, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_StoreProperty(int name, int base)
@@ -312,8 +300,7 @@ void BaselineJIT::generate_StoreProperty(int name, int base)
as->passInt32AsArg(name, 2);
as->passJSSlotAsArg(base, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeProperty, CallResultDestination::Ignore);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(StoreProperty, CallResultDestination::Ignore);
LOAD_ACC();
}
@@ -323,12 +310,13 @@ void BaselineJIT::generate_SetLookup(int index, int base)
STORE_ACC();
as->prepareCallWithArgCount(4);
as->passAccumulatorAsArg(3);
- as->passJSSlotAsArg(base, 2);
- as->passInt32AsArg(index, 1);
+ as->passInt32AsArg(index, 2);
+ as->passJSSlotAsArg(base, 1);
as->passFunctionAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL((function->isStrict() ? Helpers::setLookupStrict : Helpers::setLookupSloppy),
- CallResultDestination::InAccumulator);
- as->checkException();
+ if (function->isStrict())
+ BASELINEJIT_GENERATE_RUNTIME_CALL(SetLookupStrict, CallResultDestination::InAccumulator)
+ else
+ BASELINEJIT_GENERATE_RUNTIME_CALL(SetLookupSloppy, CallResultDestination::InAccumulator)
}
void BaselineJIT::generate_LoadSuperProperty(int property)
@@ -337,8 +325,7 @@ void BaselineJIT::generate_LoadSuperProperty(int property)
as->prepareCallWithArgCount(2);
as->passJSSlotAsArg(property, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadSuperProperty, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(LoadSuperProperty, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_StoreSuperProperty(int property)
@@ -349,8 +336,7 @@ void BaselineJIT::generate_StoreSuperProperty(int property)
as->passAccumulatorAsArg(2);
as->passJSSlotAsArg(property, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeSuperProperty, CallResultDestination::Ignore);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(StoreSuperProperty, CallResultDestination::Ignore);
LOAD_ACC();
}
@@ -372,7 +358,7 @@ void BaselineJIT::generate_Resume(int)
Q_UNREACHABLE();
}
-void BaselineJIT::generate_CallValue(int name, int argc, int argv, int /*traceSlot*/)
+void BaselineJIT::generate_CallValue(int name, int argc, int argv)
{
STORE_IP();
as->prepareCallWithArgCount(4);
@@ -380,11 +366,10 @@ void BaselineJIT::generate_CallValue(int name, int argc, int argv, int /*traceSl
as->passJSSlotAsArg(argv, 2);
as->passJSSlotAsArg(name, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callValue, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallValue, CallResultDestination::InAccumulator);
}
-void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, int argv, int /*traceSlot*/)
+void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, int argv)
{
STORE_IP();
as->prepareCallWithArgCount(5);
@@ -393,11 +378,10 @@ void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc,
as->passJSSlotAsArg(thisObject, 2);
as->passJSSlotAsArg(name, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callWithReceiver, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallWithReceiver, CallResultDestination::InAccumulator);
}
-void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv, int /*traceSlot*/)
+void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv)
{
STORE_IP();
as->prepareCallWithArgCount(5);
@@ -406,11 +390,10 @@ void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv,
as->passInt32AsArg(name, 2);
as->passJSSlotAsArg(base, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callProperty, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallProperty, CallResultDestination::InAccumulator);
}
-void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv, int /*traceSlot*/)
+void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv)
{
STORE_IP();
as->prepareCallWithArgCount(5);
@@ -419,11 +402,10 @@ void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int arg
as->passInt32AsArg(lookupIndex, 2);
as->passJSSlotAsArg(base, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callPropertyLookup, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallPropertyLookup, CallResultDestination::InAccumulator);
}
-void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv, int /*traceSlot*/)
+void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv)
{
STORE_IP();
as->prepareCallWithArgCount(5);
@@ -432,11 +414,10 @@ void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv,
as->passJSSlotAsArg(index, 2);
as->passJSSlotAsArg(base, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callElement, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallElement, CallResultDestination::InAccumulator);
}
-void BaselineJIT::generate_CallName(int name, int argc, int argv, int /*traceSlot*/)
+void BaselineJIT::generate_CallName(int name, int argc, int argv)
{
STORE_IP();
as->prepareCallWithArgCount(4);
@@ -444,22 +425,20 @@ void BaselineJIT::generate_CallName(int name, int argc, int argv, int /*traceSlo
as->passJSSlotAsArg(argv, 2);
as->passInt32AsArg(name, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callName, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallName, CallResultDestination::InAccumulator);
}
-void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv, int /*traceSlot*/)
+void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv)
{
STORE_IP();
as->prepareCallWithArgCount(3);
as->passInt32AsArg(argc, 2);
as->passJSSlotAsArg(argv, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callPossiblyDirectEval, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallPossiblyDirectEval, CallResultDestination::InAccumulator);
}
-void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv, int /*traceSlot*/)
+void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv)
{
STORE_IP();
as->prepareCallWithArgCount(4);
@@ -467,12 +446,10 @@ void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv, int /
as->passJSSlotAsArg(argv, 2);
as->passInt32AsArg(index, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callGlobalLookup, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallGlobalLookup, CallResultDestination::InAccumulator);
}
-void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int argv,
- int /*traceSlot*/)
+void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int argv)
{
STORE_IP();
as->prepareCallWithArgCount(4);
@@ -480,11 +457,10 @@ void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int
as->passJSSlotAsArg(argv, 2);
as->passInt32AsArg(index, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callQmlContextPropertyLookup, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallQmlContextPropertyLookup, CallResultDestination::InAccumulator);
}
-void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv, int /*traceSlot*/)
+void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv)
{
STORE_IP();
as->prepareCallWithArgCount(5);
@@ -493,8 +469,7 @@ void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, in
as->passJSSlotAsArg(thisObject, 2);
as->passJSSlotAsArg(func, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callWithSpread, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallWithSpread, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_TailCall(int func, int thisObject, int argc, int argv)
@@ -513,8 +488,7 @@ void BaselineJIT::generate_Construct(int func, int argc, int argv)
as->passAccumulatorAsArg(2);
as->passJSSlotAsArg(func, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_construct, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Construct, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_ConstructWithSpread(int func, int argc, int argv)
@@ -527,8 +501,7 @@ void BaselineJIT::generate_ConstructWithSpread(int func, int argc, int argv)
as->passAccumulatorAsArg(2);
as->passJSSlotAsArg(func, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_constructWithSpread, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(ConstructWithSpread, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_SetUnwindHandler(int offset)
@@ -561,7 +534,7 @@ void BaselineJIT::generate_ThrowException()
as->prepareCallWithArgCount(2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_throwException, CallResultDestination::Ignore);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(ThrowException, CallResultDestination::Ignore);
as->gotoCatchException();
}
@@ -572,8 +545,7 @@ void BaselineJIT::generate_CreateCallContext()
{
as->prepareCallWithArgCount(1);
as->passCppFrameAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(ExecutionContext::newCallContext, CallResultDestination::Ignore); // keeps result in return value register
- as->storeHeapObject(CallData::Context);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(PushCallContext, CallResultDestination::Ignore);
}
void BaselineJIT::generate_PushCatchContext(int index, int name) { as->pushCatchContext(index, name); }
@@ -583,11 +555,9 @@ void BaselineJIT::generate_PushWithContext()
STORE_IP();
as->saveAccumulatorInFrame();
as->prepareCallWithArgCount(2);
- as->passJSSlotAsArg(0, 1);
+ as->passJSSlotAsArg(CallData::Accumulator, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createWithContext, CallResultDestination::Ignore); // keeps result in return value register
- as->checkException();
- as->storeHeapObject(CallData::Context);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(PushWithContext, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_PushBlockContext(int index)
@@ -595,8 +565,8 @@ void BaselineJIT::generate_PushBlockContext(int index)
as->saveAccumulatorInFrame();
as->prepareCallWithArgCount(2);
as->passInt32AsArg(index, 1);
- as->passJSSlotAsArg(0, 0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::pushBlockContext, CallResultDestination::Ignore);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(PushBlockContext, CallResultDestination::Ignore);
as->loadAccumulatorFromFrame();
}
@@ -604,29 +574,27 @@ void BaselineJIT::generate_CloneBlockContext()
{
as->saveAccumulatorInFrame();
as->prepareCallWithArgCount(1);
- as->passJSSlotAsArg(CallData::Context, 0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::cloneBlockContext, CallResultDestination::Ignore);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CloneBlockContext, CallResultDestination::Ignore);
as->loadAccumulatorFromFrame();
}
void BaselineJIT::generate_PushScriptContext(int index)
{
as->saveAccumulatorInFrame();
- as->prepareCallWithArgCount(3);
- as->passInt32AsArg(index, 2);
- as->passEngineAsArg(1);
- as->passJSSlotAsArg(0, 0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::pushScriptContext, CallResultDestination::Ignore);
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(index, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(PushScriptContext, CallResultDestination::Ignore);
as->loadAccumulatorFromFrame();
}
void BaselineJIT::generate_PopScriptContext()
{
as->saveAccumulatorInFrame();
- as->prepareCallWithArgCount(2);
- as->passEngineAsArg(1);
- as->passJSSlotAsArg(0, 0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::popScriptContext, CallResultDestination::Ignore);
+ as->prepareCallWithArgCount(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(PopScriptContext, CallResultDestination::Ignore);
as->loadAccumulatorFromFrame();
}
@@ -639,8 +607,7 @@ void BaselineJIT::generate_GetIterator(int iterator)
as->passInt32AsArg(iterator, 2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_getIterator, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(GetIterator, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_IteratorNext(int value, int done)
@@ -650,9 +617,8 @@ void BaselineJIT::generate_IteratorNext(int value, int done)
as->passJSSlotAsArg(value, 2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_iteratorNext, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(IteratorNext, CallResultDestination::InAccumulator);
as->storeReg(done);
- as->checkException();
}
void BaselineJIT::generate_IteratorNextForYieldStar(int iterator, int object)
@@ -663,8 +629,7 @@ void BaselineJIT::generate_IteratorNextForYieldStar(int iterator, int object)
as->passJSSlotAsArg(iterator, 2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_iteratorNextForYieldStar, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(IteratorNextForYieldStar, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_IteratorClose(int done)
@@ -674,8 +639,7 @@ void BaselineJIT::generate_IteratorClose(int done)
as->passJSSlotAsArg(done, 2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_iteratorClose, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(IteratorClose, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_DestructureRestElement()
@@ -684,29 +648,28 @@ void BaselineJIT::generate_DestructureRestElement()
as->prepareCallWithArgCount(2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_destructureRestElement, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(DestructureRestElement, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_DeleteProperty(int base, int index)
{
STORE_IP();
- as->prepareCallWithArgCount(3);
- as->passJSSlotAsArg(index, 2);
- as->passJSSlotAsArg(base, 1);
- as->passFunctionAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::deleteProperty, CallResultDestination::InAccumulator);
- as->checkException();
+ as->prepareCallWithArgCount(4);
+ as->passJSSlotAsArg(index, 3);
+ as->passJSSlotAsArg(base, 2);
+ as->passFunctionAsArg(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(DeleteProperty, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_DeleteName(int name)
{
STORE_IP();
- as->prepareCallWithArgCount(2);
- as->passInt32AsArg(name, 1);
- as->passFunctionAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::deleteName, CallResultDestination::InAccumulator);
- as->checkException();
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(name, 2);
+ as->passFunctionAsArg(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(DeleteName, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_TypeofName(int name)
@@ -714,7 +677,7 @@ void BaselineJIT::generate_TypeofName(int name)
as->prepareCallWithArgCount(2);
as->passInt32AsArg(name, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_typeofName, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(TypeofName, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_TypeofValue()
@@ -723,7 +686,7 @@ void BaselineJIT::generate_TypeofValue()
as->prepareCallWithArgCount(2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_typeofValue, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(TypeofValue, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_DeclareVar(int varName, int isDeletable)
@@ -733,7 +696,7 @@ void BaselineJIT::generate_DeclareVar(int varName, int isDeletable)
as->passInt32AsArg(varName, 2);
as->passInt32AsArg(isDeletable, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_declareVar, CallResultDestination::Ignore);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(DeclareVar, CallResultDestination::Ignore);
LOAD_ACC();
}
@@ -743,7 +706,7 @@ void BaselineJIT::generate_DefineArray(int argc, int args)
as->passInt32AsArg(argc, 2);
as->passJSSlotAsArg(args, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_arrayLiteral, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(ArrayLiteral, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
@@ -753,7 +716,7 @@ void BaselineJIT::generate_DefineObjectLiteral(int internalClassId, int argc, in
as->passJSSlotAsArg(args, 2);
as->passInt32AsArg(internalClassId, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_objectLiteral, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(ObjectLiteral, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_CreateClass(int classIndex, int heritage, int computedNames)
@@ -763,14 +726,14 @@ void BaselineJIT::generate_CreateClass(int classIndex, int heritage, int compute
as->passJSSlotAsArg(heritage, 2);
as->passInt32AsArg(classIndex, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createClass, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CreateClass, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_CreateMappedArgumentsObject()
{
as->prepareCallWithArgCount(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createMappedArgumentsObject,
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CreateMappedArgumentsObject,
CallResultDestination::InAccumulator);
}
@@ -778,7 +741,7 @@ void BaselineJIT::generate_CreateUnmappedArgumentsObject()
{
as->prepareCallWithArgCount(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createUnmappedArgumentsObject,
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CreateUnmappedArgumentsObject,
CallResultDestination::InAccumulator);
}
@@ -787,7 +750,7 @@ void BaselineJIT::generate_CreateRestParameter(int argIndex)
as->prepareCallWithArgCount(2);
as->passInt32AsArg(argIndex, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createRestParameter, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CreateRestParameter, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_ConvertThisToObject()
@@ -796,8 +759,8 @@ void BaselineJIT::generate_ConvertThisToObject()
as->prepareCallWithArgCount(2);
as->passJSSlotAsArg(CallData::This, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::convertThisToObject, CallResultDestination::Ignore);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(ConvertThisToObject, CallResultDestination::InAccumulator);
+ as->storeReg(CallData::This);
LOAD_ACC();
}
@@ -806,8 +769,7 @@ void BaselineJIT::generate_LoadSuperConstructor()
as->prepareCallWithArgCount(2);
as->passJSSlotAsArg(CallData::Function, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadSuperConstructor, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(LoadSuperConstructor, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_ToObject()
@@ -816,8 +778,7 @@ void BaselineJIT::generate_ToObject()
as->prepareCallWithArgCount(2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::toObject, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(ToObject, CallResultDestination::InAccumulator);
}
@@ -826,12 +787,12 @@ void BaselineJIT::generate_Jump(int offset)
labels.insert(as->jump(absoluteOffset(offset)));
}
-void BaselineJIT::generate_JumpTrue(int /*traceSlot*/, int offset)
+void BaselineJIT::generate_JumpTrue(int offset)
{
labels.insert(as->jumpTrue(absoluteOffset(offset)));
}
-void BaselineJIT::generate_JumpFalse(int /*traceSlot*/, int offset)
+void BaselineJIT::generate_JumpFalse(int offset)
{
labels.insert(as->jumpFalse(absoluteOffset(offset)));
}
@@ -846,6 +807,11 @@ void BaselineJIT::generate_JumpNotUndefined(int offset)
labels.insert(as->jumpNotUndefined(absoluteOffset(offset)));
}
+void BaselineJIT::generate_CheckException()
+{
+ as->checkException();
+}
+
void BaselineJIT::generate_CmpEqNull() { as->cmpeqNull(); }
void BaselineJIT::generate_CmpNeNull() { as->cmpneNull(); }
void BaselineJIT::generate_CmpEqInt(int lhs) { as->cmpeqInt(lhs); }
@@ -866,8 +832,7 @@ void BaselineJIT::generate_CmpIn(int lhs)
as->passAccumulatorAsArg(2);
as->passJSSlotAsArg(lhs, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_in, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(In, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_CmpInstanceOf(int lhs)
@@ -877,17 +842,16 @@ void BaselineJIT::generate_CmpInstanceOf(int lhs)
as->passAccumulatorAsArg(2);
as->passJSSlotAsArg(lhs, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_instanceof, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Instanceof, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_UNot() { as->unot(); }
void BaselineJIT::generate_UPlus() { as->toNumber(); }
-void BaselineJIT::generate_UMinus(int /*traceSlot*/) { as->uminus(); }
+void BaselineJIT::generate_UMinus() { as->uminus(); }
void BaselineJIT::generate_UCompl() { as->ucompl(); }
-void BaselineJIT::generate_Increment(int /*traceSlot*/) { as->inc(); }
-void BaselineJIT::generate_Decrement(int /*traceSlot*/) { as->dec(); }
-void BaselineJIT::generate_Add(int lhs, int /*traceSlot*/) { as->add(lhs); }
+void BaselineJIT::generate_Increment() { as->inc(); }
+void BaselineJIT::generate_Decrement() { as->dec(); }
+void BaselineJIT::generate_Add(int lhs) { as->add(lhs); }
void BaselineJIT::generate_BitAnd(int lhs) { as->bitAnd(lhs); }
void BaselineJIT::generate_BitOr(int lhs) { as->bitOr(lhs); }
@@ -909,13 +873,12 @@ void BaselineJIT::generate_Exp(int lhs) {
as->prepareCallWithArgCount(2);
as->passAccumulatorAsArg(1);
as->passJSSlotAsArg(lhs, 0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::exp, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Exp, CallResultDestination::InAccumulator);
}
-void BaselineJIT::generate_Mul(int lhs, int /*traceSlot*/) { as->mul(lhs); }
+void BaselineJIT::generate_Mul(int lhs) { as->mul(lhs); }
void BaselineJIT::generate_Div(int lhs) { as->div(lhs); }
-void BaselineJIT::generate_Mod(int lhs, int /*traceSlot*/) { as->mod(lhs); }
-void BaselineJIT::generate_Sub(int lhs, int /*traceSlot*/) { as->sub(lhs); }
+void BaselineJIT::generate_Mod(int lhs) { as->mod(lhs); }
+void BaselineJIT::generate_Sub(int lhs) { as->sub(lhs); }
//void BaselineJIT::generate_BinopContext(int alu, int lhs)
//{
@@ -942,8 +905,7 @@ void BaselineJIT::generate_ThrowOnNullOrUndefined()
as->prepareCallWithArgCount(2);
as->passAccumulatorAsArg(1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::throwOnNullOrUndefined, CallResultDestination::Ignore);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(ThrowOnNullOrUndefined, CallResultDestination::Ignore);
LOAD_ACC();
}
@@ -952,19 +914,17 @@ void BaselineJIT::generate_GetTemplateObject(int index)
as->prepareCallWithArgCount(2);
as->passInt32AsArg(index, 1);
as->passFunctionAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(RuntimeHelpers::getTemplateObject, CallResultDestination::InAccumulator);
- as->checkException();
+ BASELINEJIT_GENERATE_RUNTIME_CALL(GetTemplateObject, CallResultDestination::InAccumulator);
}
-void BaselineJIT::startInstruction(Instr::Type /*instr*/)
+ByteCodeHandler::Verdict BaselineJIT::startInstruction(Instr::Type /*instr*/)
{
if (labels.contains(currentInstructionOffset()))
as->addLabel(currentInstructionOffset());
+ return ProcessInstruction;
}
void BaselineJIT::endInstruction(Instr::Type instr)
{
Q_UNUSED(instr);
}
-
-#endif // V4_ENABLE_JIT
diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h
index 10c89bc74b..284faf0ff0 100644
--- a/src/qml/jit/qv4baselinejit_p.h
+++ b/src/qml/jit/qv4baselinejit_p.h
@@ -56,7 +56,7 @@
#include <private/qv4instr_moth_p.h>
#include <private/qv4bytecodehandler_p.h>
-//QT_REQUIRE_CONFIG(qml_jit);
+QT_REQUIRE_CONFIG(qml_jit);
QT_BEGIN_NAMESPACE
@@ -65,7 +65,6 @@ namespace JIT {
class BaselineAssembler;
-#ifdef V4_ENABLE_JIT
class BaselineJIT final: public Moth::ByteCodeHandler
{
public:
@@ -88,22 +87,22 @@ public:
void generate_StoreReg(int reg) override;
void generate_MoveReg(int srcReg, int destReg) override;
void generate_LoadImport(int index) override;
- void generate_LoadLocal(int index, int traceSlot) override;
+ void generate_LoadLocal(int index) override;
void generate_StoreLocal(int index) override;
- void generate_LoadScopedLocal(int scope, int index, int traceSlot) override;
+ void generate_LoadScopedLocal(int scope, int index) override;
void generate_StoreScopedLocal(int scope, int index) override;
void generate_LoadRuntimeString(int stringId) override;
void generate_MoveRegExp(int regExpId, int destReg) override;
void generate_LoadClosure(int value) override;
- void generate_LoadName(int name, int traceSlot) override;
- void generate_LoadGlobalLookup(int index, int traceSlot) override;
- void generate_LoadQmlContextPropertyLookup(int index, int traceSlot) override;
+ void generate_LoadName(int name) override;
+ void generate_LoadGlobalLookup(int index) override;
+ void generate_LoadQmlContextPropertyLookup(int index) override;
void generate_StoreNameSloppy(int name) override;
void generate_StoreNameStrict(int name) override;
- void generate_LoadElement(int base, int traceSlot) override;
- void generate_StoreElement(int base, int index, int traceSlot) override;
- void generate_LoadProperty(int name, int traceSlot) override;
- void generate_GetLookup(int index, int traceSlot) override;
+ void generate_LoadElement(int base) override;
+ void generate_StoreElement(int base, int index) override;
+ void generate_LoadProperty(int name) override;
+ void generate_GetLookup(int index) override;
void generate_StoreProperty(int name, int base) override;
void generate_SetLookup(int index, int base) override;
void generate_LoadSuperProperty(int property) override;
@@ -112,16 +111,16 @@ public:
void generate_YieldStar() override;
void generate_Resume(int) override;
- void generate_CallValue(int name, int argc, int argv, int traceSlot) override;
- void generate_CallWithReceiver(int name, int thisObject, int argc, int argv, int traceSlot) override;
- void generate_CallProperty(int name, int base, int argc, int argv, int traceSlot) override;
- void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv, int traceSlot) override;
- void generate_CallElement(int base, int index, int argc, int argv, int traceSlot) override;
- void generate_CallName(int name, int argc, int argv, int traceSlot) override;
- void generate_CallPossiblyDirectEval(int argc, int argv, int traceSlot) override;
- void generate_CallGlobalLookup(int index, int argc, int argv, int traceSlot) override;
- void generate_CallQmlContextPropertyLookup(int index, int argc, int argv, int traceSlot) override;
- void generate_CallWithSpread(int func, int thisObject, int argc, int argv, int traceSlot) override;
+ void generate_CallValue(int name, int argc, int argv) override;
+ void generate_CallWithReceiver(int name, int thisObject, int argc, int argv) override;
+ void generate_CallProperty(int name, int base, int argc, int argv) override;
+ void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) override;
+ void generate_CallElement(int base, int index, int argc, int argv) override;
+ void generate_CallName(int name, int argc, int argv) override;
+ void generate_CallPossiblyDirectEval(int argc, int argv) override;
+ void generate_CallGlobalLookup(int index, int argc, int argv) override;
+ void generate_CallQmlContextPropertyLookup(int index, int argc, int argv) override;
+ void generate_CallWithSpread(int func, int thisObject, int argc, int argv) override;
void generate_TailCall(int func, int thisObject, int argc, int argv) override;
void generate_Construct(int func, int argc, int argv) override;
void generate_ConstructWithSpread(int func, int argc, int argv) override;
@@ -160,10 +159,11 @@ public:
void generate_LoadSuperConstructor() override;
void generate_ToObject() override;
void generate_Jump(int offset) override;
- void generate_JumpTrue(int traceSlot, int offset) override;
- void generate_JumpFalse(int traceSlot, 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_CheckException() override;
void generate_CmpEqNull() override;
void generate_CmpNeNull() override;
void generate_CmpEqInt(int lhs) override;
@@ -180,11 +180,11 @@ public:
void generate_CmpInstanceOf(int lhs) override;
void generate_UNot() override;
void generate_UPlus() override;
- void generate_UMinus(int traceSlot) override;
+ void generate_UMinus() override;
void generate_UCompl() override;
- void generate_Increment(int traceSlot) override;
- void generate_Decrement(int traceSlot) override;
- void generate_Add(int lhs, int traceSlot) override;
+ void generate_Increment() override;
+ void generate_Decrement() override;
+ void generate_Add(int lhs) override;
void generate_BitAnd(int lhs) override;
void generate_BitOr(int lhs) override;
void generate_BitXor(int lhs) override;
@@ -198,15 +198,15 @@ public:
void generate_ShrConst(int rhs) override;
void generate_ShlConst(int rhs) override;
void generate_Exp(int lhs) override;
- void generate_Mul(int lhs, int traceSlot) override;
+ void generate_Mul(int lhs) override;
void generate_Div(int lhs) override;
- void generate_Mod(int lhs, int traceSlot) override;
- void generate_Sub(int lhs, int traceSlot) override;
+ void generate_Mod(int lhs) override;
+ void generate_Sub(int lhs) override;
void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override;
void generate_ThrowOnNullOrUndefined() override;
void generate_GetTemplateObject(int index) override;
- void startInstruction(Moth::Instr::Type instr) override;
+ Verdict startInstruction(Moth::Instr::Type instr) override;
void endInstruction(Moth::Instr::Type instr) override;
private:
@@ -214,7 +214,6 @@ private:
QScopedPointer<BaselineAssembler> as;
QSet<int> labels;
};
-#endif // V4_ENABLE_JIT
} // namespace JIT
} // namespace QV4
diff --git a/src/qml/jit/qv4jithelpers.cpp b/src/qml/jit/qv4jithelpers.cpp
deleted file mode 100644
index 674fd8c8c8..0000000000
--- a/src/qml/jit/qv4jithelpers.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qv4jithelpers_p.h"
-#include "qv4engine_p.h"
-#include "qv4function_p.h"
-#include "qv4value_p.h"
-#include "qv4object_p.h"
-#include "qv4functionobject_p.h"
-#include "qv4lookup_p.h"
-#include <QtCore/private/qnumeric_p.h>
-
-#ifdef V4_ENABLE_JIT
-
-QT_BEGIN_NAMESPACE
-namespace QV4 {
-namespace JIT {
-namespace Helpers {
-
-void convertThisToObject(ExecutionEngine *engine, Value *t)
-{
- if (!t->isObject()) {
- if (t->isNullOrUndefined()) {
- *t = engine->globalObject->asReturnedValue();
- } else {
- *t = t->toObject(engine)->asReturnedValue();
- }
- }
-}
-
-ReturnedValue loadGlobalLookup(Function *f, ExecutionEngine *engine, int index)
-{
- Lookup *l = f->compilationUnit->runtimeLookups + index;
- return l->globalGetter(l, engine);
-}
-
-ReturnedValue loadQmlContextPropertyLookup(Function *f, ExecutionEngine *engine, int index)
-{
- Lookup *l = f->compilationUnit->runtimeLookups + index;
- return l->qmlContextPropertyGetter(l, engine, nullptr);
-}
-
-ReturnedValue toObject(ExecutionEngine *engine, const Value &obj)
-{
- if (obj.isObject())
- return obj.asReturnedValue();
-
- return obj.toObject(engine)->asReturnedValue();
-}
-
-ReturnedValue exp(const Value &base, const Value &exp)
-{
- double b = base.toNumber();
- double e = exp.toNumber();
- if (qt_is_inf(e) && (b == 1 || b == -1))
- return Encode(qt_snan());
- return Encode(pow(b,e));
-}
-
-ReturnedValue getLookup(Function *f, ExecutionEngine *engine, const Value &base, int index)
-{
- Lookup *l = f->compilationUnit->runtimeLookups + index;
- return l->getter(l, engine, base);
-}
-
-void setLookupSloppy(Function *f, int index, Value &base, const Value &value)
-{
- ExecutionEngine *engine = f->internalClass->engine;
- QV4::Lookup *l = f->compilationUnit->runtimeLookups + index;
- l->setter(l, engine, base, value);
-}
-
-void setLookupStrict(Function *f, int index, Value &base, const Value &value)
-{
- ExecutionEngine *engine = f->internalClass->engine;
- QV4::Lookup *l = f->compilationUnit->runtimeLookups + index;
- if (!l->setter(l, engine, base, value))
- engine->throwTypeError();
-}
-
-
-void pushBlockContext(Value *stack, int index)
-{
- ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
- stack[CallData::Context] = Runtime::method_createBlockContext(c, index);
-}
-
-void cloneBlockContext(Value *contextSlot)
-{
- *contextSlot = Runtime::method_cloneBlockContext(static_cast<QV4::ExecutionContext *>(contextSlot));
-}
-
-void pushScriptContext(Value *stack, ExecutionEngine *engine, int index)
-{
- stack[CallData::Context] = Runtime::method_createScriptContext(engine, index);
-}
-
-void popScriptContext(Value *stack, ExecutionEngine *engine)
-{
- stack[CallData::Context] = Runtime::method_popScriptContext(engine);
-}
-
-ReturnedValue deleteProperty(QV4::Function *function, const QV4::Value &base, const QV4::Value &index)
-{
- auto engine = function->internalClass->engine;
- if (!Runtime::method_deleteProperty(engine, base, index)) {
- if (function->isStrict())
- engine->throwTypeError();
- return Encode(false);
- } else {
- return Encode(true);
- }
-}
-
-ReturnedValue deleteName(Function *function, int name)
-{
- auto engine = function->internalClass->engine;
- if (!Runtime::method_deleteName(engine, name)) {
- if (function->isStrict())
- engine->throwTypeError();
- return Encode(false);
- } else {
- return Encode(true);
- }
-}
-
-void throwOnNullOrUndefined(ExecutionEngine *engine, const Value &v)
-{
- if (v.isNullOrUndefined())
- engine->throwTypeError();
-}
-
-} // Helpers namespace
-} // JIT namespace
-} // QV4 namespace
-QT_END_NAMESPACE
-
-#endif // V4_ENABLE_JIT
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp
index c41738c811..065fbc1c0a 100644
--- a/src/qml/jsapi/qjsengine.cpp
+++ b/src/qml/jsapi/qjsengine.cpp
@@ -41,7 +41,6 @@
#include "qjsengine_p.h"
#include "qjsvalue.h"
#include "qjsvalue_p.h"
-#include "private/qv8engine_p.h"
#include "private/qv4engine_p.h"
#include "private/qv4mm_p.h"
@@ -348,7 +347,6 @@ QJSEngine::QJSEngine(QObject *parent)
: QObject(*new QJSEnginePrivate, parent)
, m_v4Engine(new QV4::ExecutionEngine(this))
{
- m_v4Engine->v8Engine = new QV8Engine(m_v4Engine);
checkForApplicationInstance();
QJSEnginePrivate::addToDebugServer(this);
@@ -361,7 +359,6 @@ QJSEngine::QJSEngine(QJSEnginePrivate &dd, QObject *parent)
: QObject(dd, parent)
, m_v4Engine(new QV4::ExecutionEngine(this))
{
- m_v4Engine->v8Engine = new QV8Engine(m_v4Engine);
checkForApplicationInstance();
}
@@ -375,7 +372,6 @@ QJSEngine::QJSEngine(QJSEnginePrivate &dd, QObject *parent)
QJSEngine::~QJSEngine()
{
QJSEnginePrivate::removeFromDebugServer(this);
- delete m_v4Engine->v8Engine;
delete m_v4Engine;
}
@@ -470,6 +466,33 @@ void QJSEngine::installExtensions(QJSEngine::Extensions extensions, const QJSVal
QV4::GlobalExtensions::init(obj, extensions);
}
+/*!
+ \since 5.14
+ Interrupts or re-enables JavaScript execution.
+
+ If \a interrupted is \c true, any JavaScript executed by this engine
+ immediately aborts and returns an error object until this function is
+ called again with a value of \c false for \a interrupted.
+
+ This function is thread safe. You may call it from a different thread
+ in order to interrupt, for example, an infinite loop in JavaScript.
+*/
+void QJSEngine::setInterrupted(bool interrupted)
+{
+ m_v4Engine->isInterrupted = interrupted;
+}
+
+/*!
+ \since 5.14
+ Returns whether JavaScript execution is currently interrupted.
+
+ \sa setInterrupted()
+*/
+bool QJSEngine::isInterrupted() const
+{
+ return m_v4Engine->isInterrupted.loadAcquire();
+}
+
static QUrl urlForFileName(const QString &fileName)
{
if (!fileName.startsWith(QLatin1Char(':')))
@@ -527,6 +550,8 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in
result = script.run();
if (scope.engine->hasException)
result = v4->catchException();
+ if (v4->isInterrupted.loadAcquire())
+ result = v4->newErrorObject(QStringLiteral("Interrupted"));
QJSValue retval(v4, result->asReturnedValue());
@@ -565,7 +590,12 @@ QJSValue QJSEngine::importModule(const QString &fileName)
if (m_v4Engine->hasException)
return QJSValue(m_v4Engine, m_v4Engine->catchException());
moduleUnit->evaluate();
- return QJSValue(m_v4Engine, moduleNamespace->asReturnedValue());
+ if (!m_v4Engine->isInterrupted.loadAcquire())
+ return QJSValue(m_v4Engine, moduleNamespace->asReturnedValue());
+
+ return QJSValue(
+ m_v4Engine,
+ m_v4Engine->newErrorObject(QStringLiteral("Interrupted"))->asReturnedValue());
}
/*!
diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h
index 6300842341..31a4d68baa 100644
--- a/src/qml/jsapi/qjsengine.h
+++ b/src/qml/jsapi/qjsengine.h
@@ -113,6 +113,9 @@ public:
void installExtensions(Extensions extensions, const QJSValue &object = QJSValue());
+ void setInterrupted(bool interrupted);
+ bool isInterrupted() const;
+
QV4::ExecutionEngine *handle() const { return m_v4Engine; }
void throwError(const QString &message);
diff --git a/src/qml/jsapi/qjsengine_p.h b/src/qml/jsapi/qjsengine_p.h
index a77d710cff..164a70d000 100644
--- a/src/qml/jsapi/qjsengine_p.h
+++ b/src/qml/jsapi/qjsengine_p.h
@@ -74,7 +74,7 @@ public:
static const QJSEnginePrivate* get(const QJSEngine*e) { return e->d_func(); }
static QJSEnginePrivate* get(QV4::ExecutionEngine *e);
- QJSEnginePrivate() : mutex(QMutex::Recursive) {}
+ QJSEnginePrivate() = default;
~QJSEnginePrivate() override;
static void addToDebugServer(QJSEngine *q);
@@ -105,7 +105,7 @@ public:
};
// Shared by QQmlEngine
- mutable QMutex mutex;
+ mutable QRecursiveMutex mutex;
// These methods may be called from the QML loader thread
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp
index 1ab6e8c767..c2957dd294 100644
--- a/src/qml/jsapi/qjsvalue.cpp
+++ b/src/qml/jsapi/qjsvalue.cpp
@@ -51,7 +51,6 @@
#include "qv4variantobject_p.h"
#include "qv4regexpobject_p.h"
#include "qv4errorobject_p.h"
-#include "private/qv8engine_p.h"
#include <private/qv4mm_p.h>
#include <private/qv4jscall_p.h>
#include <private/qv4qobjectwrapper_p.h>
@@ -770,6 +769,8 @@ QJSValue QJSValue::call(const QJSValueList &args)
ScopedValue result(scope, f->call(jsCallData));
if (engine->hasException)
result = engine->catchException();
+ if (engine->isInterrupted.loadAcquire())
+ result = engine->newErrorObject(QStringLiteral("Interrupted"));
return QJSValue(engine, result->asReturnedValue());
}
@@ -826,6 +827,8 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList
ScopedValue result(scope, f->call(jsCallData));
if (engine->hasException)
result = engine->catchException();
+ if (engine->isInterrupted.loadAcquire())
+ result = engine->newErrorObject(QStringLiteral("Interrupted"));
return QJSValue(engine, result->asReturnedValue());
}
@@ -874,6 +877,8 @@ QJSValue QJSValue::callAsConstructor(const QJSValueList &args)
ScopedValue result(scope, f->callAsConstructor(jsCallData));
if (engine->hasException)
result = engine->catchException();
+ if (engine->isInterrupted.loadAcquire())
+ result = engine->newErrorObject(QStringLiteral("Interrupted"));
return QJSValue(engine, result->asReturnedValue());
}
@@ -1042,7 +1047,7 @@ bool QJSValue::equals(const QJSValue& other) const
if (!ov)
return other.equals(*this);
- return Runtime::method_compareEqual(*v, *ov);
+ return Runtime::CompareEqual::call(*v, *ov);
}
/*!
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri
index a24ee0a188..32acc6affc 100644
--- a/src/qml/jsruntime/jsruntime.pri
+++ b/src/qml/jsruntime/jsruntime.pri
@@ -1,8 +1,8 @@
INCLUDEPATH += $$PWD
INCLUDEPATH += $$OUT_PWD
-!qmldevtools_build {
SOURCES += \
+ $$PWD/qv4engine.cpp \
$$PWD/qv4context.cpp \
$$PWD/qv4persistent.cpp \
$$PWD/qv4lookup.cpp \
@@ -36,13 +36,13 @@ SOURCES += \
$$PWD/qv4reflect.cpp \
$$PWD/qv4regexpobject.cpp \
$$PWD/qv4stackframe.cpp \
+ $$PWD/qv4string.cpp \
$$PWD/qv4stringiterator.cpp \
$$PWD/qv4stringobject.cpp \
$$PWD/qv4variantobject.cpp \
$$PWD/qv4objectiterator.cpp \
$$PWD/qv4regexp.cpp \
$$PWD/qv4runtimecodegen.cpp \
- $$PWD/qv4serialize.cpp \
$$PWD/qv4script.cpp \
$$PWD/qv4symbol.cpp \
$$PWD/qv4setobject.cpp \
@@ -57,13 +57,17 @@ SOURCES += \
$$PWD/qv4mapiterator.cpp \
$$PWD/qv4estable.cpp \
$$PWD/qv4module.cpp \
- $$PWD/qv4promiseobject.cpp
+ $$PWD/qv4promiseobject.cpp \
+ $$PWD/qv4runtime.cpp \
+ $$PWD/qv4value.cpp \
+ $$PWD/qv4compilationunitmapper.cpp \
+ $$PWD/qv4executablecompilationunit.cpp \
+ $$PWD/qv4executableallocator.cpp
qtConfig(qml-debug): SOURCES += $$PWD/qv4profiling.cpp
HEADERS += \
$$PWD/qv4global_p.h \
- $$PWD/qv4alloca_p.h \
$$PWD/qv4engine_p.h \
$$PWD/qv4enginebase_p.h \
$$PWD/qv4context_p.h \
@@ -103,13 +107,13 @@ HEADERS += \
$$PWD/qv4regexpobject_p.h \
$$PWD/qv4runtimecodegen_p.h \
$$PWD/qv4stackframe_p.h \
+ $$PWD/qv4string_p.h \
$$PWD/qv4stringiterator_p.h \
$$PWD/qv4stringobject_p.h \
$$PWD/qv4variantobject_p.h \
$$PWD/qv4property_p.h \
$$PWD/qv4objectiterator_p.h \
$$PWD/qv4regexp_p.h \
- $$PWD/qv4serialize_p.h \
$$PWD/qv4script_p.h \
$$PWD/qv4symbol_p.h \
$$PWD/qv4setobject_p.h \
@@ -128,7 +132,13 @@ HEADERS += \
$$PWD/qv4estable_p.h \
$$PWD/qv4vtable_p.h \
$$PWD/qv4module_p.h \
- $$PWD/qv4promiseobject_p.h
+ $$PWD/qv4promiseobject_p.h \
+ $$PWD/qv4runtime_p.h \
+ $$PWD/qv4value_p.h \
+ $$PWD/qv4compilationunitmapper_p.h \
+ $$PWD/qv4executablecompilationunit_p.h \
+ $$PWD/qv4functiontable_p.h \
+ $$PWD/qv4runtimeapi_p.h
qtConfig(qml-sequence-object) {
HEADERS += \
@@ -138,29 +148,10 @@ qtConfig(qml-sequence-object) {
$$PWD/qv4sequenceobject.cpp
}
-}
-
-
-HEADERS += \
- $$PWD/qv4runtime_p.h \
- $$PWD/qv4runtimeapi_p.h \
- $$PWD/qv4value_p.h \
- $$PWD/qv4string_p.h \
- $$PWD/qv4util_p.h \
- $$PWD/qv4value_p.h \
- $$PWD/qv4functiontable_p.h
-
-SOURCES += \
- $$PWD/qv4engine.cpp \
- $$PWD/qv4runtime.cpp \
- $$PWD/qv4string.cpp \
- $$PWD/qv4value.cpp \
- $$PWD/qv4executableallocator.cpp
+unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp
+else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp
-qmldevtools_build {
- SOURCES += \
- $$PWD/qv4functiontable_noop.cpp
-} else:win32 {
+win32 {
!winrt:equals(QT_ARCH, x86_64) {
SOURCES += \
$$PWD/qv4functiontable_win64.cpp
diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp
index 4a21f62cf2..206e2b9aa4 100644
--- a/src/qml/jsruntime/qv4argumentsobject.cpp
+++ b/src/qml/jsruntime/qv4argumentsobject.cpp
@@ -38,13 +38,14 @@
****************************************************************************/
#include <qv4argumentsobject_p.h>
#include <qv4arrayobject_p.h>
-#include <qv4alloca_p.h>
#include <qv4scopedvalue_p.h>
#include <qv4string_p.h>
#include <qv4function_p.h>
#include <qv4jscall_p.h>
#include <qv4symbol_p.h>
+#include <private/qv4alloca_p.h>
+
using namespace QV4;
DEFINE_OBJECT_VTABLE(ArgumentsObject);
@@ -116,6 +117,9 @@ bool ArgumentsObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const
{
ArgumentsObject *args = static_cast<ArgumentsObject *>(m);
args->fullyCreate();
+ if (!id.isArrayIndex())
+ return Object::virtualDefineOwnProperty(m, id, desc, attrs);
+
uint index = id.asArrayIndex();
if (!args->isMapped(index))
@@ -148,36 +152,42 @@ bool ArgumentsObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const
ReturnedValue ArgumentsObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
- const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m);
- uint index = id.asArrayIndex();
- if (index < args->d()->argCount && !args->d()->fullyCreated) {
- if (hasProperty)
- *hasProperty = true;
- return args->context()->args()[index].asReturnedValue();
+ if (id.isArrayIndex()) {
+ const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m);
+ uint index = id.asArrayIndex();
+ if (index < args->d()->argCount && !args->d()->fullyCreated) {
+ if (hasProperty)
+ *hasProperty = true;
+ return args->context()->args()[index].asReturnedValue();
+ }
+
+ if (args->isMapped(index)) {
+ Q_ASSERT(index < static_cast<uint>(args->context()->function->formalParameterCount()));
+ if (hasProperty)
+ *hasProperty = true;
+ return args->context()->args()[index].asReturnedValue();
+ }
}
- if (!args->isMapped(index))
- return Object::virtualGet(m, id, receiver, hasProperty);
- Q_ASSERT(index < static_cast<uint>(args->context()->function->formalParameterCount()));
- if (hasProperty)
- *hasProperty = true;
- return args->context()->args()[index].asReturnedValue();
+ return Object::virtualGet(m, id, receiver, hasProperty);
}
bool ArgumentsObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
- ArgumentsObject *args = static_cast<ArgumentsObject *>(m);
- uint index = id.asArrayIndex();
-
- if (args == receiver && index < args->d()->argCount && !args->d()->fullyCreated) {
- args->context()->setArg(index, value);
- return true;
+ if (id.isArrayIndex()) {
+ ArgumentsObject *args = static_cast<ArgumentsObject *>(m);
+ uint index = id.asArrayIndex();
+
+ if (args == receiver && index < args->d()->argCount && !args->d()->fullyCreated) {
+ args->context()->setArg(index, value);
+ return true;
+ }
+
+ bool isMapped = (args == receiver && args->isMapped(index));
+ if (isMapped)
+ args->context()->setArg(index, value);
}
- bool isMapped = (args == receiver && args->isMapped(index));
- if (isMapped)
- args->context()->setArg(index, value);
-
return Object::virtualPut(m, id, value, receiver);
}
@@ -186,13 +196,16 @@ bool ArgumentsObject::virtualDeleteProperty(Managed *m, PropertyKey id)
ArgumentsObject *args = static_cast<ArgumentsObject *>(m);
args->fullyCreate();
bool result = Object::virtualDeleteProperty(m, id);
- if (result)
+ if (result && id.isArrayIndex())
args->removeMapping(id.asArrayIndex());
return result;
}
PropertyAttributes ArgumentsObject::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
{
+ if (!id.isArrayIndex())
+ return Object::virtualGetOwnProperty(m, id, p);
+
const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m);
uint index = id.asArrayIndex();
if (index < args->d()->argCount && !args->d()->fullyCreated) {
diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp
index b5b421fa39..af1a2d1de0 100644
--- a/src/qml/jsruntime/qv4arrayobject.cpp
+++ b/src/qml/jsruntime/qv4arrayobject.cpp
@@ -175,7 +175,8 @@ static ScopedObject createObjectFromCtorOrArray(Scope &scope, ScopedFunctionObje
// this isn't completely kosher. for instance:
// Array.from.call(Object, []).constructor == Object
// is expected by the tests, but naturally, we get Number.
- ScopedValue argument(scope, useLen ? QV4::Encode(len) : Value::undefinedValue());
+ ScopedValue argument(scope, useLen ? Value::fromReturnedValue(QV4::Encode(len))
+ : Value::undefinedValue());
a = ctor->callAsConstructor(argument, useLen ? 1 : 0);
} else {
a = scope.engine->newArrayObject(len);
@@ -219,7 +220,7 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V
// Item iteration supported, so let's go ahead and try use that.
ScopedObject a(createObjectFromCtorOrArray(scope, thatCtor, false, 0));
CHECK_EXCEPTION();
- ScopedObject iterator(scope, Runtime::method_getIterator(scope.engine, itemsObject, true));
+ ScopedObject iterator(scope, Runtime::GetIterator::call(scope.engine, itemsObject, true));
CHECK_EXCEPTION(); // symbol_iterator threw; whoops.
if (!iterator) {
return scope.engine->throwTypeError(); // symbol_iterator wasn't an object.
@@ -236,11 +237,11 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V
if (k > (static_cast<qint64>(1) << 53) - 1) {
ScopedValue falsey(scope, Encode(false));
ScopedValue error(scope, scope.engine->throwTypeError());
- return Runtime::method_iteratorClose(scope.engine, iterator, falsey);
+ return Runtime::IteratorClose::call(scope.engine, iterator, falsey);
}
// Retrieve the next value. If the iteration ends, we're done here.
- done = Value::fromReturnedValue(Runtime::method_iteratorNext(scope.engine, iterator, nextValue));
+ done = Value::fromReturnedValue(Runtime::IteratorNext::call(scope.engine, iterator, nextValue));
CHECK_EXCEPTION();
if (done->toBoolean()) {
if (ArrayObject *ao = a->as<ArrayObject>()) {
@@ -257,7 +258,7 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V
mapArguments[1] = Value::fromDouble(k);
mappedValue = mapfn->call(thisArg, mapArguments, 2);
if (scope.engine->hasException)
- return Runtime::method_iteratorClose(scope.engine, iterator, Value::fromBoolean(false));
+ return Runtime::IteratorClose::call(scope.engine, iterator, Value::fromBoolean(false));
} else {
mappedValue = *nextValue;
}
@@ -271,7 +272,7 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V
if (scope.engine->hasException) {
ScopedValue falsey(scope, Encode(false));
- return Runtime::method_iteratorClose(scope.engine, iterator, falsey);
+ return Runtime::IteratorClose::call(scope.engine, iterator, falsey);
}
k++;
@@ -387,7 +388,7 @@ ReturnedValue ArrayPrototype::method_toLocaleString(const FunctionObject *b, con
v = instance->get(k);
if (v->isNullOrUndefined())
continue;
- v = Runtime::method_callElement(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0);
+ v = Runtime::CallElement::call(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0);
s = v->toString(scope.engine);
if (scope.hasException())
return Encode::undefined();
diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp
index 3e5f51c302..a9b4ecb607 100644
--- a/src/qml/jsruntime/qv4booleanobject.cpp
+++ b/src/qml/jsruntime/qv4booleanobject.cpp
@@ -103,7 +103,7 @@ ReturnedValue BooleanPrototype::method_toString(const FunctionObject *b, const V
if (exception)
return v4->throwTypeError();
- return Encode(result ? v4->id_true() : v4->id_false());
+ return (result ? v4->id_true() : v4->id_false())->asReturnedValue();
}
ReturnedValue BooleanPrototype::method_valueOf(const FunctionObject *b, const Value *thisObject, const Value *, int)
diff --git a/src/qml/compiler/qv4compilationunitmapper.cpp b/src/qml/jsruntime/qv4compilationunitmapper.cpp
index 350f6f9485..74f34a284d 100644
--- a/src/qml/compiler/qv4compilationunitmapper.cpp
+++ b/src/qml/jsruntime/qv4compilationunitmapper.cpp
@@ -39,7 +39,7 @@
#include "qv4compilationunitmapper_p.h"
-#include "qv4compileddata_p.h"
+#include <private/qv4compileddata_p.h>
#include <QFileInfo>
#include <QDateTime>
#include <QCoreApplication>
diff --git a/src/qml/compiler/qv4compilationunitmapper_p.h b/src/qml/jsruntime/qv4compilationunitmapper_p.h
index 80f914c141..80f914c141 100644
--- a/src/qml/compiler/qv4compilationunitmapper_p.h
+++ b/src/qml/jsruntime/qv4compilationunitmapper_p.h
diff --git a/src/qml/compiler/qv4compilationunitmapper_unix.cpp b/src/qml/jsruntime/qv4compilationunitmapper_unix.cpp
index 6768bc9596..a9ab2f5ccb 100644
--- a/src/qml/compiler/qv4compilationunitmapper_unix.cpp
+++ b/src/qml/jsruntime/qv4compilationunitmapper_unix.cpp
@@ -45,7 +45,7 @@
#include <QScopeGuard>
#include <QDateTime>
-#include "qv4compileddata_p.h"
+#include "qv4executablecompilationunit_p.h"
QT_BEGIN_NAMESPACE
@@ -73,7 +73,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
return nullptr;
}
- if (!header.verifyHeader(sourceTimeStamp, errorString))
+ if (!ExecutableCompilationUnit::verifyHeader(&header, sourceTimeStamp, errorString))
return nullptr;
// Data structure and qt version matched, so now we can access the rest of the file safely.
diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/jsruntime/qv4compilationunitmapper_win.cpp
index 779c1288fe..9e8babc5e6 100644
--- a/src/qml/compiler/qv4compilationunitmapper_win.cpp
+++ b/src/qml/jsruntime/qv4compilationunitmapper_win.cpp
@@ -39,7 +39,7 @@
#include "qv4compilationunitmapper_p.h"
-#include "qv4compileddata_p.h"
+#include "qv4executablecompilationunit_p.h"
#include <QScopeGuard>
#include <QFileInfo>
#include <QDateTime>
@@ -87,7 +87,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
return nullptr;
}
- if (!header.verifyHeader(sourceTimeStamp, errorString))
+ if (!ExecutableCompilationUnit::verifyHeader(&header, sourceTimeStamp, errorString))
return nullptr;
// Data structure and qt version matched, so now we can access the rest of the file safely.
diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp
index 94b1a9fb73..018571e325 100644
--- a/src/qml/jsruntime/qv4context.cpp
+++ b/src/qml/jsruntime/qv4context.cpp
@@ -60,7 +60,7 @@ Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int b
{
Function *function = frame->v4Function;
- Heap::InternalClass *ic = function->compilationUnit->runtimeBlocks.at(blockIndex);
+ Heap::InternalClass *ic = function->executableCompilationUnit()->runtimeBlocks.at(blockIndex);
uint nLocals = ic->size;
size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals;
@@ -71,27 +71,28 @@ Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int b
Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m());
c->outer.set(v4, outer);
- c->function.set(v4, static_cast<Heap::FunctionObject *>(frame->jsFrame->function.m()));
+ c->function.set(v4, static_cast<Heap::FunctionObject *>(
+ Value::fromStaticValue(frame->jsFrame->function).m()));
c->locals.size = nLocals;
c->locals.alloc = nLocals;
- c->setupLocalTemporalDeadZone(function->compilationUnit->unitData()->blockAt(blockIndex));
+ c->setupLocalTemporalDeadZone(function->executableCompilationUnit()->unitData()->blockAt(blockIndex));
return c;
}
-Heap::CallContext *ExecutionContext::cloneBlockContext(Heap::CallContext *context)
+Heap::CallContext *ExecutionContext::cloneBlockContext(ExecutionEngine *engine,
+ Heap::CallContext *callContext)
{
- uint nLocals = context->locals.alloc;
+ uint nLocals = callContext->locals.alloc;
size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals;
- ExecutionEngine *v4 = context->internalClass->engine;
- Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, context->internalClass);
- memcpy(c, context, requiredMemory);
+ Heap::CallContext *c = engine->memoryManager->allocManaged<CallContext>(
+ requiredMemory, callContext->internalClass);
+ memcpy(c, callContext, requiredMemory);
return c;
-
}
Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame)
@@ -108,7 +109,8 @@ Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame)
c->init();
c->outer.set(v4, outer);
- c->function.set(v4, static_cast<Heap::FunctionObject *>(frame->jsFrame->function.m()));
+ c->function.set(v4, static_cast<Heap::FunctionObject *>(
+ Value::fromStaticValue(frame->jsFrame->function).m()));
const CompiledData::Function *compiledFunction = function->compiledFunction;
uint nLocals = compiledFunction->nLocals;
@@ -128,7 +130,7 @@ Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame)
return c;
}
-Heap::ExecutionContext *ExecutionContext::newWithContext(Heap::Object *with)
+Heap::ExecutionContext *ExecutionContext::newWithContext(Heap::Object *with) const
{
Heap::ExecutionContext *c = engine()->memoryManager->alloc<ExecutionContext>(Heap::ExecutionContext::Type_WithContext);
c->outer.set(engine(), d());
diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h
index 5cd2f9ddf0..75fa2d08e6 100644
--- a/src/qml/jsruntime/qv4context_p.h
+++ b/src/qml/jsruntime/qv4context_p.h
@@ -150,9 +150,10 @@ struct Q_QML_EXPORT ExecutionContext : public Managed
V4_INTERNALCLASS(ExecutionContext)
static Heap::CallContext *newBlockContext(QV4::CppStackFrame *frame, int blockIndex);
- static Heap::CallContext *cloneBlockContext(Heap::CallContext *context);
+ static Heap::CallContext *cloneBlockContext(ExecutionEngine *engine,
+ Heap::CallContext *callContext);
static Heap::CallContext *newCallContext(QV4::CppStackFrame *frame);
- Heap::ExecutionContext *newWithContext(Heap::Object *with);
+ Heap::ExecutionContext *newWithContext(Heap::Object *with) const;
static Heap::ExecutionContext *newCatchContext(CppStackFrame *frame, int blockIndex, Heap::String *exceptionVarName);
void createMutableBinding(String *name, bool deletable);
diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp
index e313ebe300..cc89947cec 100644
--- a/src/qml/jsruntime/qv4dateobject.cpp
+++ b/src/qml/jsruntime/qv4dateobject.cpp
@@ -52,11 +52,9 @@
#include <time.h>
-#include <private/qqmljsengine_p.h>
-
#include <wtf/MathExtras.h>
-#if defined(Q_OS_LINUX) && QT_CONFIG(timezone)
+#if defined(Q_OS_LINUX) && QT_CONFIG(timezone) && !defined(Q_OS_ANDROID)
/*
See QTBUG-56899. Although we don't (yet) have a proper way to reset the
system zone, the code below, that uses QTimeZone::systemTimeZone(), works
@@ -618,8 +616,7 @@ static inline double ParseString(const QString &s, double localTZA)
QStringLiteral("d MMMM, yyyy hh:mm:ss"),
};
- for (uint i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) {
- const QString &format(formats[i]);
+ for (const QString &format : formats) {
dt = format.indexOf(QLatin1String("hh:mm")) < 0
? QDateTime(QDate::fromString(s, format),
QTime(0, 0, 0), Qt::UTC)
@@ -746,14 +743,16 @@ void Heap::DateObject::init(const QTime &time)
* time from it, which shall (via toQDateTime(), below) discard the date
* part. We need a date for which time-zone data is likely to be sane (so
* MakeDay(0, 0, 0) was a bad choice; 2 BC, December 31st is before
- * time-zones were standardized), with no transition nearby in date. We
- * ignore DST transitions before 1970, but even then zone transitions did
- * happen. Some do happen at new year, others on DST transitions in spring
- * and autumn; so pick the three hundredth anniversary of the birth of
- * Giovanni Domenico Cassini (1625-06-08), whose work first let us
- * synchronize clocks tolerably accurately at distant locations.
+ * time-zones were standardized), with no transition nearby in date.
+ * QDateTime ignores DST transitions before 1970, but even then zone
+ * transitions did happen; and DaylightSavingTA() will include DST, at odds
+ * with QDateTime. So pick a date since 1970 and prefer one when no zone
+ * was in DST. One such interval (according to the Olson database, at
+ * least) was 1971 March 15th to April 17th. Since converting a time to a
+ * date-time without specifying a date is foolish, let's use April Fools'
+ * day.
*/
- static const double d = MakeDay(1925, 5, 8);
+ static const double d = MakeDay(1971, 3, 1);
double t = MakeTime(time.hour(), time.minute(), time.second(), time.msec());
date = TimeClip(UTC(MakeDate(d, t), internalClass->engine->localTZA));
}
diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h
index 5b9934282c..a87eb92caf 100644
--- a/src/qml/jsruntime/qv4dateobject_p.h
+++ b/src/qml/jsruntime/qv4dateobject_p.h
@@ -96,7 +96,7 @@ struct DateObject: Object {
double date() const { return d()->date; }
void setDate(double date) { d()->date = date; }
- QDateTime toQDateTime() const;
+ Q_QML_PRIVATE_EXPORT QDateTime toQDateTime() const;
};
template<>
diff --git a/src/qml/jsruntime/qv4debugging_p.h b/src/qml/jsruntime/qv4debugging_p.h
index 9b41bb6e7a..52263105fa 100644
--- a/src/qml/jsruntime/qv4debugging_p.h
+++ b/src/qml/jsruntime/qv4debugging_p.h
@@ -52,6 +52,7 @@
//
#include "qv4global_p.h"
+#include <private/qv4staticvalue_p.h>
#include <QtCore/qobject.h>
QT_BEGIN_NAMESPACE
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 3002945945..3d3f452073 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -38,21 +38,18 @@
****************************************************************************/
#include <qv4engine_p.h>
-#include <private/qqmljslexer_p.h>
-#include <private/qqmljsparser_p.h>
-#include <private/qqmljsast_p.h>
#include <private/qv4compileddata_p.h>
-#include <private/qv4compiler_p.h>
-#include <private/qv4compilercontext_p.h>
#include <private/qv4codegen_p.h>
+#include <private/qqmljsdiagnosticmessage_p.h>
#include <QtCore/QTextStream>
#include <QDateTime>
#include <QDir>
#include <QFileInfo>
#include <QLoggingCategory>
-
-#ifndef V4_BOOTSTRAP
+#if QT_CONFIG(regularexpression)
+#include <QRegularExpression>
+#endif
#include <qv4qmlcontext_p.h>
#include <qv4value_p.h>
@@ -104,7 +101,6 @@
#include "qv4dataview_p.h"
#include "qv4promiseobject_p.h"
#include "qv4typedarray_p.h"
-#include <private/qv8engine_p.h>
#include <private/qjsvalue_p.h>
#include <private/qqmltypewrapper_p.h>
#include <private/qqmlvaluetypewrapper_p.h>
@@ -112,9 +108,15 @@
#include <private/qqmllistwrapper_p.h>
#include <private/qqmllist_p.h>
#include <private/qqmltypeloader_p.h>
+#include <private/qqmlbuiltinfunctions_p.h>
#if QT_CONFIG(qml_locale)
#include <private/qqmllocale_p.h>
#endif
+#if QT_CONFIG(qml_xml_http_request)
+#include <private/qv4domerrors_p.h>
+#include <private/qqmlxmlhttprequest_p.h>
+#endif
+#include <private/qv4sqlerrors_p.h>
#include <qqmlfile.h>
#if USE(PTHREADS)
@@ -131,16 +133,12 @@
#include <valgrind/memcheck.h>
#endif
-#endif // #ifndef V4_BOOTSTRAP
+Q_DECLARE_METATYPE(QList<int>)
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(lcTracingAll, "qt.v4.tracing.all")
-
using namespace QV4;
-#ifndef V4_BOOTSTRAP
-
static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1);
ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
@@ -150,6 +148,43 @@ ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const
qint32 ExecutionEngine::maxCallDepth = -1;
+template <typename ReturnType>
+ReturnType convertJSValueToVariantType(const QJSValue &value)
+{
+ return value.toVariant().value<ReturnType>();
+}
+
+static void saveJSValue(QDataStream &stream, const void *data)
+{
+ const QJSValue *jsv = reinterpret_cast<const QJSValue *>(data);
+ quint32 isNullOrUndefined = 0;
+ if (jsv->isNull())
+ isNullOrUndefined |= 0x1;
+ if (jsv->isUndefined())
+ isNullOrUndefined |= 0x2;
+ stream << isNullOrUndefined;
+ if (!isNullOrUndefined)
+ reinterpret_cast<const QJSValue*>(data)->toVariant().save(stream);
+}
+
+static void restoreJSValue(QDataStream &stream, void *data)
+{
+ QJSValue *jsv = reinterpret_cast<QJSValue*>(data);
+
+ quint32 isNullOrUndefined;
+ stream >> isNullOrUndefined;
+
+ if (isNullOrUndefined & 0x1) {
+ *jsv = QJSValue(QJSValue::NullValue);
+ } else if (isNullOrUndefined & 0x2) {
+ *jsv = QJSValue();
+ } else {
+ QVariant v;
+ v.load(stream);
+ QJSValuePrivate::setVariant(jsv, v);
+ }
+}
+
ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
: executableAllocator(new QV4::ExecutableAllocator)
, regExpAllocator(new QV4::ExecutableAllocator)
@@ -157,19 +192,31 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
, jsStack(new WTF::PageAllocation)
, gcStack(new WTF::PageAllocation)
, globalCode(nullptr)
- , v8Engine(nullptr)
, publicEngine(jsEngine)
, m_engineId(engineSerial.fetchAndAddOrdered(1))
, regExpCache(nullptr)
, m_multiplyWrappedQObjects(nullptr)
-#if defined(V4_ENABLE_JIT) && !defined(V4_BOOTSTRAP)
+#if QT_CONFIG(qml_jit)
, m_canAllocateExecutableMemory(OSAllocator::canAllocateExecutableMemory())
#endif
+#if QT_CONFIG(qml_xml_http_request)
+ , m_xmlHttpRequestData(nullptr)
+#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) {
- bool ok = false;
+ ok = false;
maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok);
if (!ok || maxCallDepth <= 0) {
#if defined(QT_NO_DEBUG) && !defined(__SANITIZE_ADDRESS__) && !QT_HAS_FEATURE(address_sanitizer)
@@ -183,24 +230,24 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
Q_ASSERT(maxCallDepth > 0);
// reserve space for the JS stack
- // we allow it to grow to a bit more than JSStackLimit, as we can overshoot due to ScopedValues
+ // 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(JSStackLimit + 256*1024, WTF::OSAllocator::JSVMStackPages,
+ *jsStack = WTF::PageAllocation::allocate(m_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, JSStackLimit + 256*1024);
+ VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, m_maxJSStackSize + 256*1024);
#endif
jsStackTop = jsStackBase;
- *gcStack = WTF::PageAllocation::allocate(GCStackLimit, WTF::OSAllocator::JSVMStackPages,
+ *gcStack = WTF::PageAllocation::allocate(m_maxGCStackSize, WTF::OSAllocator::JSVMStackPages,
/* writable */ true, /* executable */ false,
/* includesGuardPages */ true);
{
- bool ok = false;
+ ok = false;
jitCallCountThreshold = qEnvironmentVariableIntValue("QV4_JIT_CALL_THRESHOLD", &ok);
if (!ok)
jitCallCountThreshold = 3;
@@ -218,7 +265,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
jsSymbols = jsAlloca(NJSSymbols);
// set up stack limits
- jsStackLimit = jsStackBase + JSStackLimit/sizeof(Value);
+ jsStackLimit = jsStackBase + m_maxJSStackSize/sizeof(Value);
identifierTable = new IdentifierTable(this);
@@ -643,7 +690,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
ScopedFunctionObject t(scope, memoryManager->allocate<FunctionObject>(rootContext(), nullptr, ::throwTypeError));
t->defineReadonlyProperty(id_length(), Value::fromInt32(0));
- t->setInternalClass(t->internalClass()->frozen());
+ t->setInternalClass(t->internalClass()->cryopreserved());
jsObjects[ThrowerObject] = t;
ScopedProperty pd(scope);
@@ -651,18 +698,27 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
pd->set = thrower();
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>);
+ QMetaType::registerStreamOperators(qMetaTypeId<QJSValue>(), saveJSValue, restoreJSValue);
+
+ QV4::QObjectWrapper::initializeBindings(this);
+
+ m_delayedCallQueue.init(this);
}
ExecutionEngine::~ExecutionEngine()
{
- if (Q_UNLIKELY(lcTracingAll().isDebugEnabled())) {
- for (auto cu : compilationUnits) {
- for (auto f : qAsConst(cu->runtimeFunctions))
- qCDebug(lcTracingAll).noquote().nospace() << f->traceInfoToString();
- }
- }
-
modules.clear();
+ qDeleteAll(m_extensionData);
delete m_multiplyWrappedQObjects;
m_multiplyWrappedQObjects = nullptr;
delete identifierTable;
@@ -679,6 +735,11 @@ ExecutionEngine::~ExecutionEngine()
delete jsStack;
gcStack->deallocate();
delete gcStack;
+
+#if QT_CONFIG(qml_xml_http_request)
+ qt_rem_qmlxmlhttprequest(this, m_xmlHttpRequestData);
+ m_xmlHttpRequestData = nullptr;
+#endif
}
ExecutionContext *ExecutionEngine::currentContext() const
@@ -860,6 +921,13 @@ Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re)
return memoryManager->allocate<RegExpObject>(re);
}
+#if QT_CONFIG(regularexpression)
+Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegularExpression &re)
+{
+ return memoryManager->allocate<RegExpObject>(re);
+}
+#endif
+
Heap::Object *ExecutionEngine::newErrorObject(const Value &value)
{
return ErrorObject::create<ErrorObject>(this, value, errorCtor());
@@ -1072,6 +1140,12 @@ extern "C" Q_QML_EXPORT char *qt_v4StackTrace(void *executionContext)
return v4StackTrace(reinterpret_cast<const ExecutionContext *>(executionContext));
}
+extern "C" Q_QML_EXPORT char *qt_v4StackTraceForEngine(void *executionEngine)
+{
+ auto engine = (reinterpret_cast<const ExecutionEngine *>(executionEngine));
+ return v4StackTrace(engine->currentContext());
+}
+
QUrl ExecutionEngine::resolvedUrl(const QString &file)
{
QUrl src(file);
@@ -1269,6 +1343,7 @@ static bool convertToNativeQObject(QV4::ExecutionEngine *e, const QV4::Value &va
const QByteArray &targetType,
void **result);
static QV4::ReturnedValue variantListToJS(QV4::ExecutionEngine *v4, const QVariantList &lst);
+static QV4::ReturnedValue sequentialIterableToJS(QV4::ExecutionEngine *v4, const QSequentialIterable &lst);
static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVariantMap &vmap);
static QV4::ReturnedValue variantToJS(QV4::ExecutionEngine *v4, const QVariant &value)
{
@@ -1305,7 +1380,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
&& !value.as<ArrayObject>() && !value.as<FunctionObject>()) {
return QVariant::fromValue(QV4::JsonObject::toJsonObject(object));
} else if (QV4::QObjectWrapper *wrapper = object->as<QV4::QObjectWrapper>()) {
- return qVariantFromValue<QObject *>(wrapper->object());
+ return QVariant::fromValue<QObject *>(wrapper->object());
} else if (object->as<QV4::QQmlContextWrapper>()) {
return QVariant();
} else if (QV4::QQmlTypeWrapper *w = object->as<QV4::QQmlTypeWrapper>()) {
@@ -1336,7 +1411,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
}
}
- return qVariantFromValue<QList<QObject*> >(list);
+ return QVariant::fromValue<QList<QObject*> >(list);
} else if (typeHint == QMetaType::QJsonArray) {
return QVariant::fromValue(QV4::JsonObject::toJsonArray(a));
}
@@ -1379,8 +1454,13 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
QV4::ScopedObject o(scope, value);
Q_ASSERT(o);
- if (QV4::RegExpObject *re = o->as<QV4::RegExpObject>())
+ if (QV4::RegExpObject *re = o->as<QV4::RegExpObject>()) {
+#if QT_CONFIG(regularexpression)
+ if (typeHint != QMetaType::QRegExp)
+ return re->toQRegularExpression();
+#endif
return re->toQRegExp();
+ }
if (createJSValueForObjects)
return QVariant::fromValue(QJSValue(scope.engine, o->asReturnedValue()));
@@ -1442,35 +1522,6 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V
return result;
}
-static QV4::ReturnedValue arrayFromVariantList(QV4::ExecutionEngine *e, const QVariantList &list)
-{
- QV4::Scope scope(e);
- QV4::ScopedArrayObject a(scope, e->newArrayObject());
- int len = list.count();
- a->arrayReserve(len);
- QV4::ScopedValue v(scope);
- for (int ii = 0; ii < len; ++ii)
- a->arrayPut(ii, (v = scope.engine->fromVariant(list.at(ii))));
-
- a->setArrayLengthUnchecked(len);
- return a.asReturnedValue();
-}
-
-static QV4::ReturnedValue objectFromVariantMap(QV4::ExecutionEngine *e, const QVariantMap &map)
-{
- QV4::Scope scope(e);
- QV4::ScopedObject o(scope, e->newObject());
- QV4::ScopedString s(scope);
- QV4::ScopedValue v(scope);
- for (QVariantMap::const_iterator iter = map.begin(), cend = map.end(); iter != cend; ++iter) {
- s = e->newString(iter.key());
- o->put(s, (v = e->fromVariant(iter.value())));
- }
- return o.asReturnedValue();
-}
-
-Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax);
-
QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
{
int type = variant.userType();
@@ -1520,6 +1571,10 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
return QV4::Encode(newDateObjectFromTime(*reinterpret_cast<const QTime *>(ptr)));
case QMetaType::QRegExp:
return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(ptr)));
+#if QT_CONFIG(regularexpression)
+ case QMetaType::QRegularExpression:
+ return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegularExpression *>(ptr)));
+#endif
case QMetaType::QObjectStar:
return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr));
#if QT_CONFIG(qml_sequence_object)
@@ -1534,9 +1589,9 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
}
#endif
case QMetaType::QVariantList:
- return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr));
+ return variantListToJS(this, *reinterpret_cast<const QVariantList *>(ptr));
case QMetaType::QVariantMap:
- return objectFromVariantMap(this, *reinterpret_cast<const QVariantMap *>(ptr));
+ return variantMapToJS(this, *reinterpret_cast<const QVariantMap *>(ptr));
case QMetaType::QJsonValue:
return QV4::JsonObject::fromJsonValue(this, *reinterpret_cast<const QJsonValue *>(ptr));
case QMetaType::QJsonObject:
@@ -1597,6 +1652,11 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
return retn->asReturnedValue();
#endif
+ if (QMetaType::hasRegisteredConverterFunction(type, qMetaTypeId<QtMetaTypePrivate::QSequentialIterableImpl>())) {
+ QSequentialIterable lst = variant.value<QSequentialIterable>();
+ return sequentialIterableToJS(this, lst);
+ }
+
if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(type))
return QV4::QQmlValueTypeWrapper::create(this, variant, vtmo, type);
}
@@ -1661,9 +1721,8 @@ static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVarian
s = v4->newIdentifier(it.key());
key = s->propertyKey();
v = variantToJS(v4, it.value());
- uint idx = key->asArrayIndex();
- if (idx < UINT_MAX)
- o->arraySet(idx, v);
+ if (key->isArrayIndex())
+ o->arraySet(key->asArrayIndex(), v);
else
o->insertMember(s, v);
}
@@ -1676,99 +1735,23 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data)
{
Q_ASSERT(data != nullptr);
- // check if it's one of the types we know
- switch (QMetaType::Type(type)) {
- case QMetaType::UnknownType:
- case QMetaType::Void:
- return QV4::Encode::undefined();
- case QMetaType::Nullptr:
- case QMetaType::VoidStar:
- return QV4::Encode::null();
- case QMetaType::Bool:
- return QV4::Encode(*reinterpret_cast<const bool*>(data));
- case QMetaType::Int:
- return QV4::Encode(*reinterpret_cast<const int*>(data));
- case QMetaType::UInt:
- return QV4::Encode(*reinterpret_cast<const uint*>(data));
- case QMetaType::LongLong:
- return QV4::Encode(double(*reinterpret_cast<const qlonglong*>(data)));
- case QMetaType::ULongLong:
-#if defined(Q_OS_WIN) && defined(_MSC_FULL_VER) && _MSC_FULL_VER <= 12008804
-#pragma message("** NOTE: You need the Visual Studio Processor Pack to compile support for 64bit unsigned integers.")
- return QV4::Encode(double((qlonglong)*reinterpret_cast<const qulonglong*>(data)));
-#elif defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET)
- return QV4::Encode(double((qlonglong)*reinterpret_cast<const qulonglong*>(data)));
-#else
- return QV4::Encode(double(*reinterpret_cast<const qulonglong*>(data)));
-#endif
- case QMetaType::Double:
- return QV4::Encode(*reinterpret_cast<const double*>(data));
- case QMetaType::QString:
- return newString(*reinterpret_cast<const QString*>(data))->asReturnedValue();
- case QMetaType::QByteArray:
- return newArrayBuffer(*reinterpret_cast<const QByteArray*>(data))->asReturnedValue();
- case QMetaType::Float:
- return QV4::Encode(*reinterpret_cast<const float*>(data));
- case QMetaType::Short:
- return QV4::Encode((int)*reinterpret_cast<const short*>(data));
- case QMetaType::UShort:
- return QV4::Encode((int)*reinterpret_cast<const unsigned short*>(data));
- case QMetaType::Char:
- return QV4::Encode((int)*reinterpret_cast<const char*>(data));
- case QMetaType::UChar:
- return QV4::Encode((int)*reinterpret_cast<const unsigned char*>(data));
- case QMetaType::QChar:
- return QV4::Encode((int)(*reinterpret_cast<const QChar*>(data)).unicode());
- case QMetaType::QStringList:
- return QV4::Encode(newArrayObject(*reinterpret_cast<const QStringList *>(data)));
- case QMetaType::QVariantList:
- return variantListToJS(this, *reinterpret_cast<const QVariantList *>(data));
- case QMetaType::QVariantMap:
- return variantMapToJS(this, *reinterpret_cast<const QVariantMap *>(data));
- case QMetaType::QDateTime:
- return QV4::Encode(newDateObject(*reinterpret_cast<const QDateTime *>(data)));
- case QMetaType::QDate:
- return QV4::Encode(newDateObject(QDateTime(*reinterpret_cast<const QDate *>(data))));
- case QMetaType::QRegExp:
- return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(data)));
- case QMetaType::QObjectStar:
- return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(data));
- case QMetaType::QVariant:
+ QVariant variant(type, data);
+ if (QMetaType::Type(variant.type()) == QMetaType::QVariant) {
+ // unwrap it: this is tested in QJSEngine, and makes the most sense for
+ // end-user code too.
return variantToJS(this, *reinterpret_cast<const QVariant*>(data));
- case QMetaType::QJsonValue:
- return QV4::JsonObject::fromJsonValue(this, *reinterpret_cast<const QJsonValue *>(data));
- case QMetaType::QJsonObject:
- return QV4::JsonObject::fromJsonObject(this, *reinterpret_cast<const QJsonObject *>(data));
- case QMetaType::QJsonArray:
- return QV4::JsonObject::fromJsonArray(this, *reinterpret_cast<const QJsonArray *>(data));
- default:
- if (type == qMetaTypeId<QJSValue>()) {
- return QJSValuePrivate::convertedToValue(this, *reinterpret_cast<const QJSValue*>(data));
- } else {
- QByteArray typeName = QMetaType::typeName(type);
- if (typeName.endsWith('*') && !*reinterpret_cast<void* const *>(data)) {
- return QV4::Encode::null();
- }
- QMetaType mt(type);
- if (auto metaObject = mt.metaObject()) {
- auto flags = mt.flags();
- if (flags & QMetaType::IsGadget) {
- return QV4::QQmlValueTypeWrapper::create(this, QVariant(type, data), metaObject, type);
- } else if (flags & QMetaType::PointerToQObject) {
- return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(data));
- }
- }
- if (QMetaType::hasRegisteredConverterFunction(type, qMetaTypeId<QtMetaTypePrivate::QSequentialIterableImpl>())) {
- auto v = QVariant(type, data);
- QSequentialIterable lst = v.value<QSequentialIterable>();
- return sequentialIterableToJS(this, lst);
- }
- // Fall back to wrapping in a QVariant.
- return QV4::Encode(newVariantObject(QVariant(type, data)));
- }
}
- Q_UNREACHABLE();
- return 0;
+ return fromVariant(variant);
+}
+
+int ExecutionEngine::maxJSStackSize() const
+{
+ return m_maxJSStackSize;
+}
+
+int ExecutionEngine::maxGCStackSize() const
+{
+ return m_maxGCStackSize;
}
ReturnedValue ExecutionEngine::global()
@@ -1776,13 +1759,12 @@ ReturnedValue ExecutionEngine::global()
return globalObject->asReturnedValue();
}
-QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::compileModule(const QUrl &url)
+QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::compileModule(const QUrl &url)
{
QQmlMetaType::CachedUnitLookupError cacheError = QQmlMetaType::CachedUnitLookupError::NoError;
if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(url, &cacheError)) {
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> jsUnit;
- jsUnit.adopt(new QV4::CompiledData::CompilationUnit(cachedUnit, url.fileName(), url.toString()));
- return jsUnit;
+ return ExecutableCompilationUnit::create(
+ QV4::CompiledData::CompilationUnit(cachedUnit, url.fileName(), url.toString()));
}
QFile f(QQmlFile::urlToLocalFileOrQrc(url));
@@ -1800,69 +1782,26 @@ QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::compileModule(con
}
-QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::compileModule(const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp)
+QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::compileModule(
+ const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp)
{
QList<QQmlJS::DiagnosticMessage> diagnostics;
- auto unit = compileModule(/*debugMode*/debugger() != nullptr, url.toString(), sourceCode, sourceTimeStamp, &diagnostics);
+ auto unit = Compiler::Codegen::compileModule(/*debugMode*/debugger() != nullptr, url.toString(),
+ sourceCode, sourceTimeStamp, &diagnostics);
for (const QQmlJS::DiagnosticMessage &m : diagnostics) {
if (m.isError()) {
- throwSyntaxError(m.message, url.toString(), m.loc.startLine, m.loc.startColumn);
+ throwSyntaxError(m.message, url.toString(), m.line, m.column);
return nullptr;
} else {
- qWarning() << url << ':' << m.loc.startLine << ':' << m.loc.startColumn
+ qWarning() << url << ':' << m.line << ':' << m.column
<< ": warning: " << m.message;
}
}
- return unit;
-}
-
-#endif // ifndef V4_BOOTSTRAP
-
-QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::compileModule(bool debugMode, const QString &url, const QString &sourceCode,
- const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics)
-{
- QQmlJS::Engine ee;
- QQmlJS::Lexer lexer(&ee);
- lexer.setCode(sourceCode, /*line*/1, /*qml mode*/false);
- QQmlJS::Parser parser(&ee);
-
- const bool parsed = parser.parseModule();
-
- if (diagnostics)
- *diagnostics = parser.diagnosticMessages();
-
- if (!parsed)
- return nullptr;
-
- QQmlJS::AST::ESModule *moduleNode = QQmlJS::AST::cast<QQmlJS::AST::ESModule*>(parser.rootNode());
- if (!moduleNode) {
- // if parsing was successful, and we have no module, then
- // the file was empty.
- if (diagnostics)
- diagnostics->clear();
- return nullptr;
- }
-
- using namespace QV4::Compiler;
- Compiler::Module compilerModule(debugMode);
- compilerModule.unitFlags |= CompiledData::Unit::IsESModule;
- compilerModule.sourceTimeStamp = sourceTimeStamp;
- JSUnitGenerator jsGenerator(&compilerModule);
- Codegen cg(&jsGenerator, /*strictMode*/true);
- cg.generateFromModule(url, url, sourceCode, moduleNode, &compilerModule);
- auto errors = cg.errors();
- if (diagnostics)
- *diagnostics << errors;
-
- if (!errors.isEmpty())
- return nullptr;
- return cg.generateCompilationUnit();
+ return ExecutableCompilationUnit::create(std::move(unit));
}
-#ifndef V4_BOOTSTRAP
-
-void ExecutionEngine::injectModule(const QQmlRefPointer<CompiledData::CompilationUnit> &moduleUnit)
+void ExecutionEngine::injectModule(const QQmlRefPointer<ExecutableCompilationUnit> &moduleUnit)
{
// Injection can happen from the QML type loader thread for example, but instantiation and
// evaluation must be limited to the ExecutionEngine's thread.
@@ -1870,7 +1809,7 @@ void ExecutionEngine::injectModule(const QQmlRefPointer<CompiledData::Compilatio
modules.insert(moduleUnit->finalUrl(), moduleUnit);
}
-QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::moduleForUrl(const QUrl &_url, const CompiledData::CompilationUnit *referrer) const
+QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::moduleForUrl(const QUrl &_url, const ExecutableCompilationUnit *referrer) const
{
QUrl url = QQmlTypeLoader::normalize(_url);
if (referrer)
@@ -1883,7 +1822,7 @@ QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::moduleForUrl(cons
return *existingModule;
}
-QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::loadModule(const QUrl &_url, const CompiledData::CompilationUnit *referrer)
+QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::loadModule(const QUrl &_url, const ExecutableCompilationUnit *referrer)
{
QUrl url = QQmlTypeLoader::normalize(_url);
if (referrer)
@@ -1905,6 +1844,131 @@ QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::loadModule(const
return newModule;
}
+void ExecutionEngine::initQmlGlobalObject()
+{
+ initializeGlobal();
+ freezeObject(*globalObject);
+}
+
+void ExecutionEngine::initializeGlobal()
+{
+ QV4::Scope scope(this);
+ QV4::GlobalExtensions::init(globalObject, QJSEngine::AllExtensions);
+
+ QV4::ScopedObject qt(scope, memoryManager->allocate<QV4::QtObject>(qmlEngine()));
+ globalObject->defineDefaultProperty(QStringLiteral("Qt"), qt);
+
+#if QT_CONFIG(qml_locale)
+ QQmlLocale::registerStringLocaleCompare(this);
+ QQmlDateExtension::registerExtension(this);
+ QQmlNumberExtension::registerExtension(this);
+#endif
+
+#if QT_CONFIG(qml_xml_http_request)
+ qt_add_domexceptions(this);
+ m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this);
+#endif
+
+ qt_add_sqlexceptions(this);
+
+ {
+ for (uint i = 0; i < globalObject->internalClass()->size; ++i) {
+ if (globalObject->internalClass()->nameMap.at(i).isString()) {
+ QV4::PropertyKey id = globalObject->internalClass()->nameMap.at(i);
+ m_illegalNames.insert(id.toQString());
+ }
+ }
+ }
+}
+
+const QSet<QString> &ExecutionEngine::illegalNames() const
+{
+ return m_illegalNames;
+}
+
+void ExecutionEngine::setQmlEngine(QQmlEngine *engine)
+{
+ m_qmlEngine = engine;
+ initQmlGlobalObject();
+}
+
+static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object)
+{
+ if (object->as<QV4::QObjectWrapper>() || object->internalClass()->isFrozen)
+ return;
+
+ QV4::Scope scope(v4);
+
+ bool instanceOfObject = false;
+ QV4::ScopedObject p(scope, object->getPrototypeOf());
+ while (p) {
+ if (p->d() == v4->objectPrototype()->d()) {
+ instanceOfObject = true;
+ break;
+ }
+ p = p->getPrototypeOf();
+ }
+ if (!instanceOfObject)
+ return;
+
+ Heap::InternalClass *frozen = object->internalClass()->frozen();
+ object->setInternalClass(frozen); // Immediately assign frozen to prevent it from getting GC'd
+
+ QV4::ScopedObject o(scope);
+ for (uint i = 0; i < frozen->size; ++i) {
+ if (!frozen->nameMap.at(i).isStringOrSymbol())
+ continue;
+ o = *object->propertyData(i);
+ if (o)
+ freeze_recursive(v4, o);
+ }
+}
+
+void ExecutionEngine::freezeObject(const QV4::Value &value)
+{
+ QV4::Scope scope(this);
+ QV4::ScopedObject o(scope, value);
+ freeze_recursive(this, o);
+}
+
+void ExecutionEngine::startTimer(const QString &timerName)
+{
+ if (!m_time.isValid())
+ m_time.start();
+ m_startedTimers[timerName] = m_time.elapsed();
+}
+
+qint64 ExecutionEngine::stopTimer(const QString &timerName, bool *wasRunning)
+{
+ if (!m_startedTimers.contains(timerName)) {
+ *wasRunning = false;
+ return 0;
+ }
+ *wasRunning = true;
+ qint64 startedAt = m_startedTimers.take(timerName);
+ return m_time.elapsed() - startedAt;
+}
+
+int ExecutionEngine::consoleCountHelper(const QString &file, quint16 line, quint16 column)
+{
+ const QString key = file + QString::number(line) + QString::number(column);
+ int number = m_consoleCount.value(key, 0);
+ number++;
+ m_consoleCount.insert(key, number);
+ return number;
+}
+
+void ExecutionEngine::setExtensionData(int index, Deletable *data)
+{
+ if (m_extensionData.count() <= index)
+ m_extensionData.resize(index + 1);
+
+ if (m_extensionData.at(index))
+ delete m_extensionData.at(index);
+
+ m_extensionData[index] = data;
+}
+
// Converts a JS value to a meta-type.
// data must point to a place that can store a value of the given type.
// Returns true if conversion succeeded, false otherwise.
@@ -1980,6 +2044,13 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data)
*reinterpret_cast<QRegExp *>(data) = r->toQRegExp();
return true;
} break;
+#if QT_CONFIG(regularexpression)
+ case QMetaType::QRegularExpression:
+ if (const QV4::RegExpObject *r = value->as<QV4::RegExpObject>()) {
+ *reinterpret_cast<QRegularExpression *>(data) = r->toQRegularExpression();
+ return true;
+ } break;
+#endif
case QMetaType::QObjectStar: {
const QV4::QObjectWrapper *qobjectWrapper = value->as<QV4::QObjectWrapper>();
if (qobjectWrapper || value->isNull()) {
@@ -2144,6 +2215,23 @@ static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const QV4::Value &v
return wrapper->object();
}
-#endif // ifndef V4_BOOTSTRAP
+struct QV4EngineRegistrationData
+{
+ QV4EngineRegistrationData() : extensionCount(0) {}
+
+ QMutex mutex;
+ int extensionCount;
+};
+Q_GLOBAL_STATIC(QV4EngineRegistrationData, registrationData);
+
+QMutex *ExecutionEngine::registrationMutex()
+{
+ return &registrationData()->mutex;
+}
+
+int ExecutionEngine::registerExtension()
+{
+ return registrationData()->extensionCount++;
+}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index 86367c0ece..d233347060 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -53,25 +53,82 @@
#include "qv4global_p.h"
#include "qv4managed_p.h"
#include "qv4context_p.h"
+#include "qv4stackframe_p.h"
#include <private/qintrusivelist_p.h>
#include "qv4enginebase_p.h"
#include <private/qqmlrefcount_p.h>
-#include <private/qqmljsengine_p.h>
+#include <private/qqmldelayedcallqueue_p.h>
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qmutex.h>
-#ifndef V4_BOOTSTRAP
-# include "qv4function_p.h"
-# include <private/qv8engine_p.h>
-# include <private/qv4compileddata_p.h>
-#endif
+#include "qv4function_p.h"
+#include <private/qv4compileddata_p.h>
+#include <private/qv4executablecompilationunit_p.h>
namespace WTF {
class BumpPointerAllocator;
class PageAllocation;
}
+#define V4_DEFINE_EXTENSION(dataclass, datafunction) \
+ static inline dataclass *datafunction(QV4::ExecutionEngine *engine) \
+ { \
+ static int extensionId = -1; \
+ if (extensionId == -1) { \
+ QV4::ExecutionEngine::registrationMutex()->lock(); \
+ if (extensionId == -1) \
+ extensionId = QV4::ExecutionEngine::registerExtension(); \
+ QV4::ExecutionEngine::registrationMutex()->unlock(); \
+ } \
+ dataclass *rv = (dataclass *)engine->extensionData(extensionId); \
+ if (!rv) { \
+ rv = new dataclass(engine); \
+ engine->setExtensionData(extensionId, rv); \
+ } \
+ return rv; \
+ } \
+
+
QT_BEGIN_NAMESPACE
-class QV8Engine;
+namespace QV4 { struct QObjectMethod; }
+
+// Used to allow a QObject method take and return raw V4 handles without having to expose
+// 48 in the public API.
+// Use like this:
+// class MyClass : public QObject {
+// Q_OBJECT
+// ...
+// Q_INVOKABLE void myMethod(QQmlV4Function*);
+// };
+// The QQmlV8Function - and consequently the arguments and return value - only remains
+// valid during the call. If the return value isn't set within myMethod(), the will return
+// undefined.
+
+class QQmlV4Function
+{
+public:
+ int length() const { return callData->argc(); }
+ QV4::ReturnedValue operator[](int idx) const { return (idx < callData->argc() ? callData->args[idx].asReturnedValue() : QV4::Encode::undefined()); }
+ void setReturnValue(QV4::ReturnedValue rv) { *retVal = rv; }
+ QV4::ExecutionEngine *v4engine() const { return e; }
+private:
+ friend struct QV4::QObjectMethod;
+ QQmlV4Function();
+ QQmlV4Function(const QQmlV4Function &);
+ QQmlV4Function &operator=(const QQmlV4Function &);
+
+ QQmlV4Function(QV4::CallData *callData, QV4::Value *retVal, QV4::ExecutionEngine *e)
+ : callData(callData), retVal(retVal), e(e)
+ {
+ callData->thisObject = QV4::Encode::undefined();
+ }
+
+ QV4::CallData *callData;
+ QV4::Value *retVal;
+ QV4::ExecutionEngine *e;
+};
+
class QQmlError;
class QJSEngine;
class QQmlEngine;
@@ -112,10 +169,6 @@ public:
WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine.
- enum {
- JSStackLimit = 4*1024*1024,
- GCStackLimit = 2*1024*1024
- };
WTF::PageAllocation *jsStack;
WTF::PageAllocation *gcStack;
@@ -128,14 +181,8 @@ public:
Function *globalCode;
-#ifdef V4_BOOTSTRAP
- QJSEngine *jsEngine() const;
- QQmlEngine *qmlEngine() const;
-#else // !V4_BOOTSTRAP
QJSEngine *jsEngine() const { return publicEngine; }
- QQmlEngine *qmlEngine() const { return v8Engine ? v8Engine->engine() : nullptr; }
-#endif // V4_BOOTSTRAP
- QV8Engine *v8Engine;
+ QQmlEngine *qmlEngine() const { return m_qmlEngine; }
QJSEngine *publicEngine;
enum JSObjects {
@@ -438,9 +485,7 @@ public:
Symbol *symbol_unscopables() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_unscopables); }
Symbol *symbol_revokableProxy() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_revokableProxy); }
-#ifndef V4_BOOTSTRAP
- QIntrusiveList<CompiledData::CompilationUnit, &CompiledData::CompilationUnit::nextCompilationUnit> compilationUnits;
-#endif
+ QIntrusiveList<ExecutableCompilationUnit, &ExecutableCompilationUnit::nextCompilationUnit> compilationUnits;
quint32 m_engineId;
@@ -464,7 +509,7 @@ public:
// but any time a QObject is wrapped a second time in another engine, we have to do
// bookkeeping.
MultiplyWrappedQObjectMap *m_multiplyWrappedQObjects;
-#if defined(V4_ENABLE_JIT) && !defined(V4_BOOTSTRAP)
+#if QT_CONFIG(qml_jit)
const bool m_canAllocateExecutableMemory;
#endif
@@ -520,6 +565,9 @@ public:
Heap::RegExpObject *newRegExpObject(const QString &pattern, int flags);
Heap::RegExpObject *newRegExpObject(RegExp *re);
Heap::RegExpObject *newRegExpObject(const QRegExp &re);
+#if QT_CONFIG(regularexpression)
+ Heap::RegExpObject *newRegExpObject(const QRegularExpression &re);
+#endif
Heap::Object *newErrorObject(const Value &value);
Heap::Object *newErrorObject(const QString &message);
@@ -588,11 +636,14 @@ public:
bool metaTypeFromJS(const Value *value, int type, void *data);
QV4::ReturnedValue metaTypeToJS(int type, const void *data);
+ int maxJSStackSize() const;
+ int maxGCStackSize() const;
+
bool checkStackLimits();
bool canJIT(Function *f = nullptr)
{
-#if defined(V4_ENABLE_JIT) && !defined(V4_BOOTSTRAP)
+#if QT_CONFIG(qml_jit)
if (!m_canAllocateExecutableMemory)
return false;
if (f)
@@ -605,42 +656,86 @@ public:
}
QV4::ReturnedValue global();
+ void initQmlGlobalObject();
+ void initializeGlobal();
+
+ void freezeObject(const QV4::Value &value);
+
+ // Return the list of illegal id names (the names of the properties on the global object)
+ const QSet<QString> &illegalNames() const;
+
+#if QT_CONFIG(qml_xml_http_request)
+ void *xmlHttpRequestData() const { return m_xmlHttpRequestData; }
+#endif
+
+ void setQmlEngine(QQmlEngine *engine);
+
+ QQmlDelayedCallQueue *delayedCallQueue() { return &m_delayedCallQueue; }
+
+ // used for console.time(), console.timeEnd()
+ void startTimer(const QString &timerName);
+ qint64 stopTimer(const QString &timerName, bool *wasRunning);
+
+ // used for console.count()
+ int consoleCountHelper(const QString &file, quint16 line, quint16 column);
+
+ struct Deletable {
+ virtual ~Deletable() {}
+ };
+
+ static QMutex *registrationMutex();
+ static int registerExtension();
+
+ void setExtensionData(int, Deletable *);
+ Deletable *extensionData(int index) const
+ {
+ if (index < m_extensionData.count())
+ return m_extensionData[index];
+ else
+ return nullptr;
+ }
double localTZA = 0.0; // local timezone, initialized at startup
- static QQmlRefPointer<CompiledData::CompilationUnit> compileModule(bool debugMode, const QString &url, const QString &sourceCode, const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics);
-#ifndef V4_BOOTSTRAP
- QQmlRefPointer<CompiledData::CompilationUnit> compileModule(const QUrl &url);
- QQmlRefPointer<CompiledData::CompilationUnit> compileModule(const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp);
+ QQmlRefPointer<ExecutableCompilationUnit> compileModule(const QUrl &url);
+ QQmlRefPointer<ExecutableCompilationUnit> compileModule(
+ const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp);
mutable QMutex moduleMutex;
- QHash<QUrl, QQmlRefPointer<CompiledData::CompilationUnit>> modules;
- void injectModule(const QQmlRefPointer<CompiledData::CompilationUnit> &moduleUnit);
- QQmlRefPointer<CompiledData::CompilationUnit> moduleForUrl(const QUrl &_url, const CompiledData::CompilationUnit *referrer = nullptr) const;
- QQmlRefPointer<CompiledData::CompilationUnit> loadModule(const QUrl &_url, const CompiledData::CompilationUnit *referrer = nullptr);
-#endif
+ QHash<QUrl, QQmlRefPointer<ExecutableCompilationUnit>> modules;
+ void injectModule(const QQmlRefPointer<ExecutableCompilationUnit> &moduleUnit);
+ QQmlRefPointer<ExecutableCompilationUnit> moduleForUrl(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr) const;
+ QQmlRefPointer<ExecutableCompilationUnit> loadModule(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr);
private:
#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;
-};
-// This is a trick to tell the code generators that functions taking a NoThrowContext won't
-// throw exceptions and therefore don't need a check after the call.
-#ifndef V4_BOOTSTRAP
-struct NoThrowEngine : public ExecutionEngine
-{
-};
-#else
-struct NoThrowEngine;
+#if QT_CONFIG(qml_xml_http_request)
+ void *m_xmlHttpRequestData;
#endif
+ QQmlEngine *m_qmlEngine;
+
+ QQmlDelayedCallQueue m_delayedCallQueue;
+
+ QElapsedTimer m_time;
+ QHash<QString, qint64> m_startedTimers;
+
+ 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(); \
ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4);
diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h
index b5cfea8863..788897bdad 100644
--- a/src/qml/jsruntime/qv4enginebase_p.h
+++ b/src/qml/jsruntime/qv4enginebase_p.h
@@ -69,9 +69,20 @@ struct Q_QML_EXPORT EngineBase {
CppStackFrame *currentStackFrame = nullptr;
Value *jsStackTop = nullptr;
+
+ // The JIT expects hasException and isInterrupted to be in the same 32bit word in memory.
quint8 hasException = false;
- quint8 writeBarrierActive = false;
+ // isInterrupted is expected to be set from a different thread
+#if defined(Q_ATOMIC_INT8_IS_SUPPORTED)
+ QAtomicInteger<quint8> isInterrupted = false;
quint16 unused = 0;
+#elif defined(Q_ATOMIC_INT16_IS_SUPPORTED)
+ quint8 unused = 0;
+ QAtomicInteger<quint16> isInterrupted = false;
+#else
+# error V4 needs either 8bit or 16bit atomics.
+#endif
+
quint8 isExecutingInRegExpJIT = false;
quint8 padding[3];
MemoryManager *memoryManager = nullptr;
@@ -136,6 +147,7 @@ Q_STATIC_ASSERT(offsetof(EngineBase, jsStackTop) == offsetof(EngineBase, current
Q_STATIC_ASSERT(offsetof(EngineBase, hasException) == offsetof(EngineBase, jsStackTop) + QT_POINTER_SIZE);
Q_STATIC_ASSERT(offsetof(EngineBase, memoryManager) == offsetof(EngineBase, hasException) + 8);
Q_STATIC_ASSERT(offsetof(EngineBase, runtime) == offsetof(EngineBase, memoryManager) + QT_POINTER_SIZE);
+Q_STATIC_ASSERT(offsetof(EngineBase, isInterrupted) + sizeof(EngineBase::isInterrupted) <= offsetof(EngineBase, hasException) + 4);
}
diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp
index c6d6c77d11..525d3458f4 100644
--- a/src/qml/jsruntime/qv4errorobject.cpp
+++ b/src/qml/jsruntime/qv4errorobject.cpp
@@ -47,7 +47,7 @@
#include "qv4string_p.h"
#include <private/qv4mm_p.h>
-#include <qv4codegen_p.h>
+#include <private/qv4codegen_p.h>
#ifndef Q_OS_WIN
# include <time.h>
diff --git a/src/qml/jsruntime/qv4executableallocator.cpp b/src/qml/jsruntime/qv4executableallocator.cpp
index c836d121e3..7ee6f39aa2 100644
--- a/src/qml/jsruntime/qv4executableallocator.cpp
+++ b/src/qml/jsruntime/qv4executableallocator.cpp
@@ -147,9 +147,7 @@ bool ExecutableAllocator::ChunkOfPages::contains(Allocation *alloc) const
}
ExecutableAllocator::ExecutableAllocator()
- : mutex(QMutex::NonRecursive)
-{
-}
+ = default;
ExecutableAllocator::~ExecutableAllocator()
{
diff --git a/src/qml/jsruntime/qv4executableallocator_p.h b/src/qml/jsruntime/qv4executableallocator_p.h
index 013c6d7120..f98f2c7d33 100644
--- a/src/qml/jsruntime/qv4executableallocator_p.h
+++ b/src/qml/jsruntime/qv4executableallocator_p.h
@@ -130,7 +130,7 @@ public:
private:
QMultiMap<size_t, Allocation*> freeAllocations;
QMap<quintptr, ChunkOfPages*> chunks;
- mutable QMutex mutex;
+ mutable QRecursiveMutex mutex;
};
}
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp
index c0ce125741..79e2ec2a5d 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp
@@ -1,9 +1,9 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
-** This file is part of the QtQml module of the Qt Toolkit.
+** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
@@ -37,48 +37,31 @@
**
****************************************************************************/
-#include "qv4compileddata_p.h"
-#include <private/qv4value_p.h>
-#ifndef V4_BOOTSTRAP
+#include "qv4executablecompilationunit_p.h"
+
#include <private/qv4engine_p.h>
-#include <private/qv4function_p.h>
-#include <private/qv4objectproto_p.h>
-#include <private/qv4lookup_p.h>
-#include <private/qv4regexpobject_p.h>
#include <private/qv4regexp_p.h>
-#include <private/qqmlpropertycache_p.h>
-#include <private/qqmltypeloader_p.h>
+#include <private/qv4lookup_p.h>
+#include <private/qv4qmlcontext_p.h>
+#include <private/qv4identifiertable_p.h>
+#include <private/qv4objectproto_p.h>
#include <private/qqmlengine_p.h>
-#include <private/qv4vme_moth_p.h>
-#include <private/qv4module_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qqmlvaluetypewrapper_p.h>
-#include "qv4compilationunitmapper_p.h"
-#include <QQmlPropertyMap>
-#include <QDateTime>
-#include <QFile>
-#include <QFileInfo>
-#include <QScopedValueRollback>
-#include <QStandardPaths>
-#include <QDir>
-#include <private/qv4identifiertable_p.h>
-#endif
-#include <private/qqmlirbuilder_p.h>
-#include <QCoreApplication>
-#include <QCryptographicHash>
-#include <QSaveFile>
-#include <QScopeGuard>
-
-// generated by qmake:
-#include "qml_compile_hash_p.h"
-
-#include <algorithm>
-
-QT_BEGIN_NAMESPACE
+#include <private/qqmlscriptdata_p.h>
+#include <private/qv4module_p.h>
+#include <private/qv4compilationunitmapper_p.h>
+#include <private/qml_compile_hash_p.h>
+#include <private/qqmltypewrapper_p.h>
-namespace QV4 {
+#include <QtQml/qqmlfile.h>
+#include <QtQml/qqmlpropertymap.h>
-namespace CompiledData {
+#include <QtCore/qdir.h>
+#include <QtCore/qstandardpaths.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qscopeguard.h>
+#include <QtCore/qcryptographichash.h>
#if defined(QML_COMPILE_HASH)
# ifdef Q_OS_LINUX
@@ -87,49 +70,29 @@ namespace CompiledData {
__attribute__((section(".qml_compile_hash")))
# endif
const char qml_compile_hash[48 + 1] = QML_COMPILE_HASH;
-static_assert(sizeof(Unit::libraryVersionHash) >= QML_COMPILE_HASH_LENGTH + 1, "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version");
+static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) >= QML_COMPILE_HASH_LENGTH + 1,
+ "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version");
#else
# error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
#endif
+QT_BEGIN_NAMESPACE
-CompilationUnit::CompilationUnit(const Unit *unitData, const QString &fileName, const QString &finalUrlString)
-{
- setUnitData(unitData, nullptr, fileName, finalUrlString);
-}
-
-CompilationUnit::~CompilationUnit()
-{
-#ifndef V4_BOOTSTRAP
- unlink();
-#endif
+namespace QV4 {
- if (data) {
- if (data->qmlUnit() != qmlData)
- free(const_cast<QmlUnit *>(qmlData));
- qmlData = nullptr;
+ExecutableCompilationUnit::ExecutableCompilationUnit() = default;
-#ifndef V4_BOOTSTRAP
- if (!(data->flags & QV4::CompiledData::Unit::StaticData))
- free(const_cast<Unit *>(data));
-#else
- // Unconditionally free the memory. In the dev tools we create units that have
- // the flag set and will be saved to disk, so intended to persist later.
- free(const_cast<Unit *>(data));
-#endif
- }
- data = nullptr;
-#if Q_BYTE_ORDER == Q_BIG_ENDIAN
- delete [] constants;
- constants = nullptr;
-#endif
+ExecutableCompilationUnit::ExecutableCompilationUnit(
+ CompiledData::CompilationUnit &&compilationUnit)
+ : CompiledData::CompilationUnit(std::move(compilationUnit))
+{}
- delete [] imports;
- imports = nullptr;
+ExecutableCompilationUnit::~ExecutableCompilationUnit()
+{
+ unlink();
}
-#ifndef V4_BOOTSTRAP
-QString CompilationUnit::localCacheFilePath(const QUrl &url)
+QString ExecutableCompilationUnit::localCacheFilePath(const QUrl &url)
{
const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
const QString cacheFileSuffix = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix();
@@ -140,7 +103,32 @@ QString CompilationUnit::localCacheFilePath(const QUrl &url)
return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + cacheFileSuffix;
}
-QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
+static QString toString(QV4::ReturnedValue v)
+{
+ Value val = Value::fromReturnedValue(v);
+ QString result;
+ if (val.isInt32())
+ result = QLatin1String("int ");
+ else if (val.isDouble())
+ result = QLatin1String("double ");
+ if (val.isEmpty())
+ result += QLatin1String("empty");
+ else
+ result += val.toQStringNoThrow();
+ return result;
+}
+
+static void dumpConstantTable(const StaticValue *constants, uint count)
+{
+ QDebug d = qDebug();
+ d.nospace() << right;
+ for (uint i = 0; i < count; ++i) {
+ d << qSetFieldWidth(8) << i << qSetFieldWidth(0) << ": "
+ << toString(constants[i].asReturnedValue()).toUtf8().constData() << "\n";
+ }
+}
+
+QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine)
{
this->engine = engine;
engine->compilationUnits.insert(this);
@@ -154,14 +142,17 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
for (uint i = 0; i < stringCount; ++i)
runtimeStrings[i] = engine->newString(stringAt(i));
- runtimeRegularExpressions = new QV4::Value[data->regexpTableSize];
+ runtimeRegularExpressions
+ = new QV4::Value[data->regexpTableSize];
// memset the regexps to 0 in case a GC run happens while we're within the loop below
- memset(runtimeRegularExpressions, 0, data->regexpTableSize * sizeof(QV4::Value));
+ memset(runtimeRegularExpressions, 0,
+ data->regexpTableSize * sizeof(QV4::Value));
for (uint i = 0; i < data->regexpTableSize; ++i) {
const CompiledData::RegExp *re = data->regexpAt(i);
uint f = re->flags;
const CompiledData::RegExp::Flags flags = static_cast<CompiledData::RegExp::Flags>(f);
- runtimeRegularExpressions[i] = QV4::RegExp::create(engine, stringAt(re->stringIndex), flags);
+ runtimeRegularExpressions[i] = QV4::RegExp::create(
+ engine, stringAt(re->stringIndex), flags);
}
if (data->lookupTableSize) {
@@ -171,7 +162,8 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
for (uint i = 0; i < data->lookupTableSize; ++i) {
QV4::Lookup *l = runtimeLookups + i;
- Lookup::Type type = Lookup::Type(uint(compiledLookups[i].type_and_flags));
+ CompiledData::Lookup::Type type
+ = CompiledData::Lookup::Type(uint(compiledLookups[i].type_and_flags));
if (type == CompiledData::Lookup::Type_Getter)
l->getter = QV4::Lookup::getterGeneric;
else if (type == CompiledData::Lookup::Type_Setter)
@@ -185,15 +177,26 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
}
if (data->jsClassTableSize) {
- runtimeClasses = (QV4::Heap::InternalClass **)malloc(data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *));
+ runtimeClasses
+ = (QV4::Heap::InternalClass **)malloc(data->jsClassTableSize
+ * sizeof(QV4::Heap::InternalClass *));
// memset the regexps to 0 in case a GC run happens while we're within the loop below
- memset(runtimeClasses, 0, data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *));
+ memset(runtimeClasses, 0,
+ data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *));
for (uint i = 0; i < data->jsClassTableSize; ++i) {
int memberCount = 0;
- const CompiledData::JSClassMember *member = data->jsClassAt(i, &memberCount);
- runtimeClasses[i] = engine->internalClasses(QV4::ExecutionEngine::Class_Object);
+ const CompiledData::JSClassMember *member
+ = data->jsClassAt(i, &memberCount);
+ runtimeClasses[i]
+ = engine->internalClasses(QV4::ExecutionEngine::Class_Object);
for (int j = 0; j < memberCount; ++j, ++member)
- runtimeClasses[i] = runtimeClasses[i]->addMember(engine->identifierTable->asPropertyKey(runtimeStrings[member->nameOffset]), member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data);
+ runtimeClasses[i]
+ = runtimeClasses[i]->addMember(
+ engine->identifierTable->asPropertyKey(
+ runtimeStrings[member->nameOffset]),
+ member->isAccessor
+ ? QV4::Attr_Accessor
+ : QV4::Attr_Data);
}
}
@@ -214,21 +217,25 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
// first locals
const quint32_le *localsIndices = compiledBlock->localsTable();
for (quint32 j = 0; j < compiledBlock->nLocals; ++j)
- ic = ic->addMember(engine->identifierTable->asPropertyKey(runtimeStrings[localsIndices[j]]), Attr_NotConfigurable);
+ ic = ic->addMember(
+ engine->identifierTable->asPropertyKey(runtimeStrings[localsIndices[j]]),
+ Attr_NotConfigurable);
runtimeBlocks[i] = ic->d();
}
static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
if (showCode) {
qDebug() << "=== Constant table";
- Moth::dumpConstantTable(constants, data->constantTableSize);
+ dumpConstantTable(constants, data->constantTableSize);
qDebug() << "=== String table";
for (uint i = 0, end = totalStringCount(); i < end; ++i)
qDebug() << " " << i << ":" << runtimeStrings[i]->toQString();
qDebug() << "=== Closure table";
for (uint i = 0; i < data->functionTableSize; ++i)
qDebug() << " " << i << ":" << runtimeFunctions[i]->name()->toQString();
- qDebug() << "root function at index " << (data->indexOfRootFunction != -1 ? data->indexOfRootFunction : 0);
+ qDebug() << "root function at index "
+ << (data->indexOfRootFunction != -1
+ ? data->indexOfRootFunction : 0);
}
if (data->indexOfRootFunction != -1)
@@ -237,7 +244,7 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
return nullptr;
}
-Heap::Object *CompilationUnit::templateObjectAt(int index) const
+Heap::Object *ExecutableCompilationUnit::templateObjectAt(int index) const
{
Q_ASSERT(index < int(data->templateObjectTableSize));
if (!templateObjects.size())
@@ -267,7 +274,7 @@ Heap::Object *CompilationUnit::templateObjectAt(int index) const
return templateObjects.at(index);
}
-void CompilationUnit::unlink()
+void ExecutableCompilationUnit::unlink()
{
if (engine)
nextCompilationUnit.remove();
@@ -285,15 +292,18 @@ void CompilationUnit::unlink()
if (runtimeLookups) {
for (uint i = 0; i < data->lookupTableSize; ++i) {
QV4::Lookup &l = runtimeLookups[i];
- if (l.getter == QV4::QObjectWrapper::lookupGetter) {
+ if (l.getter == QV4::QObjectWrapper::lookupGetter
+ || l.getter == QQmlTypeWrapper::lookupSingletonProperty) {
if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache)
pc->release();
- } else if (l.getter == QQmlValueTypeWrapper::lookupGetter) {
+ } else if (l.getter == QQmlValueTypeWrapper::lookupGetter
+ || l.getter == QQmlTypeWrapper::lookupSingletonProperty) {
if (QQmlPropertyCache *pc = l.qgadgetLookup.propertyCache)
pc->release();
}
- if (l.qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty) {
+ if (l.qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty
+ || l.qmlContextPropertyGetter == QQmlContextWrapper::lookupContextObjectProperty) {
if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache)
pc->release();
}
@@ -309,20 +319,23 @@ void CompilationUnit::unlink()
engine = nullptr;
qmlEngine = nullptr;
- free(runtimeStrings);
- runtimeStrings = nullptr;
+
delete [] runtimeLookups;
runtimeLookups = nullptr;
+
+ for (QV4::Function *f : qAsConst(runtimeFunctions))
+ f->destroy();
+ runtimeFunctions.clear();
+
+ free(runtimeStrings);
+ runtimeStrings = nullptr;
delete [] runtimeRegularExpressions;
runtimeRegularExpressions = nullptr;
free(runtimeClasses);
runtimeClasses = nullptr;
- for (QV4::Function *f : qAsConst(runtimeFunctions))
- f->destroy();
- runtimeFunctions.clear();
}
-void CompilationUnit::markObjects(QV4::MarkStack *markStack)
+void ExecutableCompilationUnit::markObjects(QV4::MarkStack *markStack)
{
if (runtimeStrings) {
for (uint i = 0, end = totalStringCount(); i < end; ++i)
@@ -331,7 +344,7 @@ void CompilationUnit::markObjects(QV4::MarkStack *markStack)
}
if (runtimeRegularExpressions) {
for (uint i = 0; i < data->regexpTableSize; ++i)
- runtimeRegularExpressions[i].mark(markStack);
+ Value::fromStaticValue(runtimeRegularExpressions[i]).mark(markStack);
}
if (runtimeClasses) {
for (uint i = 0; i < data->jsClassTableSize; ++i)
@@ -354,11 +367,11 @@ void CompilationUnit::markObjects(QV4::MarkStack *markStack)
runtimeLookups[i].markObjects(markStack);
}
- if (m_module)
- m_module->mark(markStack);
+ if (auto mod = module())
+ mod->mark(markStack);
}
-IdentifierHash CompilationUnit::createNamedObjectsPerComponent(int componentObjectIndex)
+IdentifierHash ExecutableCompilationUnit::createNamedObjectsPerComponent(int componentObjectIndex)
{
IdentifierHash namedObjectCache(engine);
const CompiledData::Object *component = objectAt(componentObjectIndex);
@@ -370,7 +383,7 @@ IdentifierHash CompilationUnit::createNamedObjectsPerComponent(int componentObje
return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache);
}
-void CompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine)
+void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine)
{
this->qmlEngine = qmlEngine;
@@ -417,7 +430,7 @@ void CompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine)
totalObjectCount = objectCount;
}
-bool CompilationUnit::verifyChecksum(const DependentTypesHasher &dependencyHasher) const
+bool ExecutableCompilationUnit::verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const
{
if (!dependencyHasher) {
for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) {
@@ -426,16 +439,13 @@ bool CompilationUnit::verifyChecksum(const DependentTypesHasher &dependencyHashe
}
return true;
}
- QCryptographicHash hash(QCryptographicHash::Md5);
- if (!dependencyHasher(&hash))
- return false;
- QByteArray checksum = hash.result();
- Q_ASSERT(checksum.size() == sizeof(data->dependencyMD5Checksum));
- return memcmp(data->dependencyMD5Checksum, checksum.constData(),
- sizeof(data->dependencyMD5Checksum)) == 0;
+ const QByteArray checksum = dependencyHasher();
+ return checksum.size() == sizeof(data->dependencyMD5Checksum)
+ && memcmp(data->dependencyMD5Checksum, checksum.constData(),
+ sizeof(data->dependencyMD5Checksum)) == 0;
}
-QStringList CompilationUnit::moduleRequests() const
+QStringList ExecutableCompilationUnit::moduleRequests() const
{
QStringList requests;
requests.reserve(data->moduleRequestTableSize);
@@ -444,10 +454,10 @@ QStringList CompilationUnit::moduleRequests() const
return requests;
}
-Heap::Module *CompilationUnit::instantiate(ExecutionEngine *engine)
+Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine)
{
- if (isESModule() && m_module)
- return m_module;
+ if (isESModule() && module())
+ return module();
if (data->indexOfRootFunction < 0)
return nullptr;
@@ -459,7 +469,7 @@ Heap::Module *CompilationUnit::instantiate(ExecutionEngine *engine)
Scoped<Module> module(scope, engine->memoryManager->allocate<Module>(engine, this));
if (isESModule())
- m_module = module->d();
+ setModule(module->d());
for (const QString &request: moduleRequests()) {
auto dependentModuleUnit = engine->loadModule(QUrl(request), this);
@@ -472,12 +482,12 @@ Heap::Module *CompilationUnit::instantiate(ExecutionEngine *engine)
const uint importCount = data->importEntryTableSize;
if (importCount > 0) {
- imports = new const Value *[importCount];
- memset(imports, 0, importCount * sizeof(Value *));
+ imports = new const StaticValue *[importCount];
+ memset(imports, 0, importCount * sizeof(StaticValue *));
}
for (uint i = 0; i < importCount; ++i) {
const CompiledData::ImportEntry &entry = data->importEntryTable()[i];
- auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this);
+ auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this);
importName = runtimeStrings[entry.importName];
const Value *valuePtr = dependentModuleUnit->resolveExport(importName);
if (!valuePtr) {
@@ -491,7 +501,7 @@ Heap::Module *CompilationUnit::instantiate(ExecutionEngine *engine)
for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) {
const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i];
- auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this);
+ auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this);
if (!dependentModuleUnit)
return nullptr;
@@ -507,26 +517,10 @@ Heap::Module *CompilationUnit::instantiate(ExecutionEngine *engine)
return module->d();
}
-const Value *CompilationUnit::resolveExport(QV4::String *exportName)
-{
- QVector<ResolveSetEntry> resolveSet;
- return resolveExportRecursively(exportName, &resolveSet);
-}
-
-QStringList CompilationUnit::exportedNames() const
+const Value *ExecutableCompilationUnit::resolveExportRecursively(
+ QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet)
{
- QStringList names;
- QVector<const CompiledData::CompilationUnit*> exportNameSet;
- getExportedNamesRecursively(&names, &exportNameSet);
- names.sort();
- auto last = std::unique(names.begin(), names.end());
- names.erase(last, names.end());
- return names;
-}
-
-const Value *CompilationUnit::resolveExportRecursively(QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet)
-{
- if (!m_module)
+ if (!module())
return nullptr;
for (const auto &entry: *resolveSet)
@@ -536,22 +530,24 @@ const Value *CompilationUnit::resolveExportRecursively(QV4::String *exportName,
(*resolveSet) << ResolveSetEntry(this, exportName);
if (exportName->toQString() == QLatin1String("*"))
- return &m_module->self;
+ return &module()->self;
Scope scope(engine);
- if (auto localExport = lookupNameInExportTable(data->localExportEntryTable(), data->localExportEntryTableSize, exportName)) {
+ if (auto localExport = lookupNameInExportTable(
+ data->localExportEntryTable(), data->localExportEntryTableSize, exportName)) {
ScopedString localName(scope, runtimeStrings[localExport->localName]);
- uint index = m_module->scope->internalClass->indexOfValueOrGetter(localName->toPropertyKey());
+ uint index = module()->scope->internalClass->indexOfValueOrGetter(localName->toPropertyKey());
if (index == UINT_MAX)
return nullptr;
- if (index >= m_module->scope->locals.size)
- return imports[index - m_module->scope->locals.size];
- return &m_module->scope->locals[index];
+ if (index >= module()->scope->locals.size)
+ return &(imports[index - module()->scope->locals.size]->asValue<Value>());
+ return &module()->scope->locals[index];
}
- if (auto indirectExport = lookupNameInExportTable(data->indirectExportEntryTable(), data->indirectExportEntryTableSize, exportName)) {
- auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(indirectExport->moduleRequest)), this);
+ if (auto indirectExport = lookupNameInExportTable(
+ data->indirectExportEntryTable(), data->indirectExportEntryTableSize, exportName)) {
+ auto dependentModuleUnit = engine->loadModule(urlAt(indirectExport->moduleRequest), this);
if (!dependentModuleUnit)
return nullptr;
ScopedString importName(scope, runtimeStrings[indirectExport->importName]);
@@ -566,7 +562,7 @@ const Value *CompilationUnit::resolveExportRecursively(QV4::String *exportName,
for (uint i = 0; i < data->starExportEntryTableSize; ++i) {
const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i];
- auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this);
+ auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this);
if (!dependentModuleUnit)
return nullptr;
@@ -585,7 +581,8 @@ const Value *CompilationUnit::resolveExportRecursively(QV4::String *exportName,
return starResolution;
}
-const ExportEntry *CompilationUnit::lookupNameInExportTable(const ExportEntry *firstExportEntry, int tableSize, QV4::String *name) const
+const CompiledData::ExportEntry *ExecutableCompilationUnit::lookupNameInExportTable(
+ const CompiledData::ExportEntry *firstExportEntry, int tableSize, QV4::String *name) const
{
const CompiledData::ExportEntry *lastExportEntry = firstExportEntry + tableSize;
auto matchingExport = std::lower_bound(firstExportEntry, lastExportEntry, name, [this](const CompiledData::ExportEntry &lhs, QV4::String *name) {
@@ -596,7 +593,9 @@ const ExportEntry *CompilationUnit::lookupNameInExportTable(const ExportEntry *f
return matchingExport;
}
-void CompilationUnit::getExportedNamesRecursively(QStringList *names, QVector<const CompilationUnit*> *exportNameSet, bool includeDefaultExport) const
+void ExecutableCompilationUnit::getExportedNamesRecursively(
+ QStringList *names, QVector<const ExecutableCompilationUnit*> *exportNameSet,
+ bool includeDefaultExport) const
{
if (exportNameSet->contains(this))
return;
@@ -620,21 +619,21 @@ void CompilationUnit::getExportedNamesRecursively(QStringList *names, QVector<co
for (uint i = 0; i < data->starExportEntryTableSize; ++i) {
const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i];
- auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this);
+ auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this);
if (!dependentModuleUnit)
return;
dependentModuleUnit->getExportedNamesRecursively(names, exportNameSet, /*includeDefaultExport*/false);
}
}
-void CompilationUnit::evaluate()
+void ExecutableCompilationUnit::evaluate()
{
QV4::Scope scope(engine);
- QV4::Scoped<Module> module(scope, m_module);
- module->evaluate();
+ QV4::Scoped<Module> mod(scope, module());
+ mod->evaluate();
}
-void CompilationUnit::evaluateModuleRequests()
+void ExecutableCompilationUnit::evaluateModuleRequests()
{
for (const QString &request: moduleRequests()) {
auto dependentModuleUnit = engine->loadModule(QUrl(request), this);
@@ -646,7 +645,7 @@ void CompilationUnit::evaluateModuleRequests()
}
}
-bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString)
+bool ExecutableCompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString)
{
if (!QQmlFile::isLocalFile(url)) {
*errorString = QStringLiteral("File has to be a local file.");
@@ -662,20 +661,23 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeS
if (!mappedUnit)
continue;
- const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr;
- const Unit *oldData = data;
+ const CompiledData::Unit * const oldDataPtr
+ = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data
+ : nullptr;
+ const CompiledData::Unit *oldData = data;
auto dataPtrRevert = qScopeGuard([this, oldData](){
setUnitData(oldData);
});
setUnitData(mappedUnit);
- if (data->sourceFileIndex != 0 && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) {
+ if (data->sourceFileIndex != 0
+ && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) {
*errorString = QStringLiteral("QML source file has moved to a different location.");
continue;
}
dataPtrRevert.dismiss();
- free(const_cast<Unit*>(oldDataPtr));
+ free(const_cast<CompiledData::Unit*>(oldDataPtr));
backingFile.reset(cacheFile.take());
return true;
}
@@ -683,17 +685,8 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeS
return false;
}
-#endif // V4_BOOTSTRAP
-
-#if defined(V4_BOOTSTRAP)
-bool CompilationUnit::saveToDisk(const QString &outputFileName, QString *errorString)
-#else
-bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
-#endif
+bool ExecutableCompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
{
- errorString->clear();
-
-#if !defined(V4_BOOTSTRAP)
if (data->sourceTimeStamp == 0) {
*errorString = QStringLiteral("Missing time stamp for source file");
return false;
@@ -703,168 +696,12 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
*errorString = QStringLiteral("File has to be a local file.");
return false;
}
- const QString outputFileName = localCacheFilePath(unitUrl);
-#endif
-
-#if QT_CONFIG(temporaryfile)
- // Foo.qml -> Foo.qmlc
- QSaveFile cacheFile(outputFileName);
- if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
- *errorString = cacheFile.errorString();
- return false;
- }
-
- QByteArray modifiedUnit;
- modifiedUnit.resize(data->unitSize);
- memcpy(modifiedUnit.data(), data, data->unitSize);
- const char *dataPtr = modifiedUnit.data();
- Unit *unitPtr;
- memcpy(&unitPtr, &dataPtr, sizeof(unitPtr));
- unitPtr->flags |= Unit::StaticData;
-
- qint64 headerWritten = cacheFile.write(modifiedUnit);
- if (headerWritten != modifiedUnit.size()) {
- *errorString = cacheFile.errorString();
- return false;
- }
- if (!cacheFile.commit()) {
- *errorString = cacheFile.errorString();
- return false;
- }
-
- return true;
-#else
- Q_UNUSED(outputFileName)
- *errorString = QStringLiteral("features.temporaryfile is disabled.");
- return false;
-#endif // QT_CONFIG(temporaryfile)
-}
-
-void CompilationUnit::setUnitData(const Unit *unitData, const QmlUnit *qmlUnit,
- const QString &fileName, const QString &finalUrlString)
-{
- data = unitData;
- qmlData = nullptr;
-#if Q_BYTE_ORDER == Q_BIG_ENDIAN
- delete [] constants;
-#endif
- constants = nullptr;
- m_fileName.clear();
- m_finalUrlString.clear();
- if (!data)
- return;
-
- qmlData = qmlUnit ? qmlUnit : data->qmlUnit();
-
-#if Q_BYTE_ORDER == Q_BIG_ENDIAN
- Value *bigEndianConstants = new Value[data->constantTableSize];
- const quint64_le *littleEndianConstants = data->constants();
- for (uint i = 0; i < data->constantTableSize; ++i)
- bigEndianConstants[i] = Value::fromReturnedValue(littleEndianConstants[i]);
- constants = bigEndianConstants;
-#else
- constants = reinterpret_cast<const Value*>(data->constants());
-#endif
-
- m_fileName = !fileName.isEmpty() ? fileName : stringAt(data->sourceFileIndex);
- m_finalUrlString = !finalUrlString.isEmpty() ? finalUrlString : stringAt(data->finalUrlIndex);
-}
-
-#ifndef V4_BOOTSTRAP
-QString Binding::valueAsString(const CompilationUnit *unit) const
-{
- switch (type) {
- case Type_Script:
- case Type_String:
- return unit->stringAt(stringIndex);
- case Type_Null:
- return QStringLiteral("null");
- case Type_Boolean:
- return value.b ? QStringLiteral("true") : QStringLiteral("false");
- case Type_Number:
- return QString::number(valueAsNumber(unit->constants));
- case Type_Invalid:
- return QString();
-#if !QT_CONFIG(translation)
- case Type_TranslationById:
- case Type_Translation:
- return unit->stringAt(unit->unitData()->translations()[value.translationDataIndex].stringIndex);
-#else
- case Type_TranslationById: {
- const TranslationData &translation = unit->unitData()->translations()[value.translationDataIndex];
- QByteArray id = unit->stringAt(translation.stringIndex).toUtf8();
- return qtTrId(id.constData(), translation.number);
- }
- case Type_Translation: {
- const TranslationData &translation = unit->unitData()->translations()[value.translationDataIndex];
- // This code must match that in the qsTr() implementation
- const QString &path = unit->fileName();
- int lastSlash = path.lastIndexOf(QLatin1Char('/'));
- QStringRef context = (lastSlash > -1) ? path.midRef(lastSlash + 1, path.length() - lastSlash - 5)
- : QStringRef();
- QByteArray contextUtf8 = context.toUtf8();
- QByteArray comment = unit->stringAt(translation.commentIndex).toUtf8();
- QByteArray text = unit->stringAt(translation.stringIndex).toUtf8();
- return QCoreApplication::translate(contextUtf8.constData(), text.constData(),
- comment.constData(), translation.number);
- }
-#endif
- default:
- break;
- }
- return QString();
-}
-
-//reverse of Lexer::singleEscape()
-QString Binding::escapedString(const QString &string)
-{
- QString tmp = QLatin1String("\"");
- for (int i = 0; i < string.length(); ++i) {
- const QChar &c = string.at(i);
- switch (c.unicode()) {
- case 0x08:
- tmp += QLatin1String("\\b");
- break;
- case 0x09:
- tmp += QLatin1String("\\t");
- break;
- case 0x0A:
- tmp += QLatin1String("\\n");
- break;
- case 0x0B:
- tmp += QLatin1String("\\v");
- break;
- case 0x0C:
- tmp += QLatin1String("\\f");
- break;
- case 0x0D:
- tmp += QLatin1String("\\r");
- break;
- case 0x22:
- tmp += QLatin1String("\\\"");
- break;
- case 0x27:
- tmp += QLatin1String("\\\'");
- break;
- case 0x5C:
- tmp += QLatin1String("\\\\");
- break;
- default:
- tmp += c;
- break;
- }
- }
- tmp += QLatin1Char('\"');
- return tmp;
-}
-
-QString Binding::valueAsScriptString(const CompilationUnit *unit) const
-{
- if (type == Type_String)
- return escapedString(unit->stringAt(stringIndex));
- else
- return valueAsString(unit);
+ return CompiledData::SaveableUnitPointer(unitData()).saveToDisk<char>(
+ [&unitUrl, errorString](const char *data, quint32 size) {
+ return CompiledData::SaveableUnitPointer::writeDataToFile(localCacheFilePath(unitUrl), data,
+ size, errorString);
+ });
}
/*!
@@ -902,7 +739,8 @@ bool ResolvedTypeReference::addToHash(QCryptographicHash *hash, QQmlEngine *engi
}
if (!compilationUnit)
return false;
- hash->addData(compilationUnit->unitData()->md5Checksum, sizeof(compilationUnit->unitData()->md5Checksum));
+ hash->addData(compilationUnit->data->md5Checksum,
+ sizeof(compilationUnit->data->md5Checksum));
return true;
}
@@ -938,93 +776,106 @@ bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *e
return true;
}
-#endif
-
-void CompilationUnit::destroy()
+QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Binding *binding) const
{
-#if !defined(V4_BOOTSTRAP)
- if (qmlEngine)
- QQmlEnginePrivate::deleteInEngineThread(qmlEngine, this);
- else
+ using namespace CompiledData;
+ switch (binding->type) {
+ case Binding::Type_Script:
+ case Binding::Type_String:
+ return stringAt(binding->stringIndex);
+ case Binding::Type_Null:
+ return QStringLiteral("null");
+ case Binding::Type_Boolean:
+ return binding->value.b ? QStringLiteral("true") : QStringLiteral("false");
+ case Binding::Type_Number:
+ return QString::number(bindingValueAsNumber(binding), 'g', QLocale::FloatingPointShortest);
+ case Binding::Type_Invalid:
+ return QString();
+#if !QT_CONFIG(translation)
+ case Binding::Type_TranslationById:
+ case Binding::Type_Translation:
+ return stringAt(
+ data->translations()[binding->value.translationDataIndex].stringIndex);
+#else
+ case Binding::Type_TranslationById: {
+ const TranslationData &translation
+ = data->translations()[binding->value.translationDataIndex];
+ QByteArray id = stringAt(translation.stringIndex).toUtf8();
+ return qtTrId(id.constData(), translation.number);
+ }
+ case Binding::Type_Translation: {
+ const TranslationData &translation
+ = data->translations()[binding->value.translationDataIndex];
+ // This code must match that in the qsTr() implementation
+ const QString &path = fileName();
+ int lastSlash = path.lastIndexOf(QLatin1Char('/'));
+ QStringRef context = (lastSlash > -1) ? path.midRef(lastSlash + 1, path.length() - lastSlash - 5)
+ : QStringRef();
+ QByteArray contextUtf8 = context.toUtf8();
+ QByteArray comment = stringAt(translation.commentIndex).toUtf8();
+ QByteArray text = stringAt(translation.stringIndex).toUtf8();
+ return QCoreApplication::translate(contextUtf8.constData(), text.constData(),
+ comment.constData(), translation.number);
+ }
#endif
- delete this;
+ default:
+ break;
+ }
+ return QString();
}
-
-void Unit::generateChecksum()
+QString ExecutableCompilationUnit::bindingValueAsScriptString(
+ const CompiledData::Binding *binding) const
{
-#ifndef V4_BOOTSTRAP
- QCryptographicHash hash(QCryptographicHash::Md5);
-
- const int checksummableDataOffset = offsetof(QV4::CompiledData::Unit, md5Checksum) + sizeof(md5Checksum);
-
- const char *dataPtr = reinterpret_cast<const char *>(this) + checksummableDataOffset;
- hash.addData(dataPtr, unitSize - checksummableDataOffset);
-
- QByteArray checksum = hash.result();
- Q_ASSERT(checksum.size() == sizeof(md5Checksum));
- memcpy(md5Checksum, checksum.constData(), sizeof(md5Checksum));
-#else
- memset(md5Checksum, 0, sizeof(md5Checksum));
-#endif
+ return (binding->type == CompiledData::Binding::Type_String)
+ ? CompiledData::Binding::escapedString(stringAt(binding->stringIndex))
+ : bindingValueAsString(binding);
}
-bool Unit::verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const
+bool ExecutableCompilationUnit::verifyHeader(
+ const CompiledData::Unit *unit, QDateTime expectedSourceTimeStamp, QString *errorString)
{
-#ifndef V4_BOOTSTRAP
- if (strncmp(magic, CompiledData::magic_str, sizeof(magic))) {
+ if (strncmp(unit->magic, CompiledData::magic_str, sizeof(unit->magic))) {
*errorString = QStringLiteral("Magic bytes in the header do not match");
return false;
}
- if (version != quint32(QV4_DATA_STRUCTURE_VERSION)) {
- *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16);
+ if (unit->version != quint32(QV4_DATA_STRUCTURE_VERSION)) {
+ *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2")
+ .arg(unit->version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16);
return false;
}
- if (qtVersion != quint32(QT_VERSION)) {
- *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(qtVersion, 0, 16).arg(QT_VERSION, 0, 16);
+ if (unit->qtVersion != quint32(QT_VERSION)) {
+ *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2")
+ .arg(unit->qtVersion, 0, 16).arg(QT_VERSION, 0, 16);
return false;
}
- if (sourceTimeStamp) {
+ if (unit->sourceTimeStamp) {
// Files from the resource system do not have any time stamps, so fall back to the application
// executable.
if (!expectedSourceTimeStamp.isValid())
expectedSourceTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified();
- if (expectedSourceTimeStamp.isValid() && expectedSourceTimeStamp.toMSecsSinceEpoch() != sourceTimeStamp) {
+ if (expectedSourceTimeStamp.isValid()
+ && expectedSourceTimeStamp.toMSecsSinceEpoch() != unit->sourceTimeStamp) {
*errorString = QStringLiteral("QML source file has a different time stamp than cached file.");
return false;
}
}
#if defined(QML_COMPILE_HASH)
- if (qstrcmp(CompiledData::qml_compile_hash, libraryVersionHash) != 0) {
+ if (qstrcmp(qml_compile_hash, unit->libraryVersionHash) != 0) {
*errorString = QStringLiteral("QML library version mismatch. Expected compile hash does not match");
return false;
}
#else
#error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
#endif
-
return true;
-#else
- Q_UNUSED(expectedSourceTimeStamp)
- Q_UNUSED(errorString)
- return false;
-#endif
}
-Location &Location::operator=(const QQmlJS::AST::SourceLocation &astLocation)
-{
- line = astLocation.startLine;
- column = astLocation.startColumn;
- return *this;
-}
-
-}
-
-}
+} // namespace QV4
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4executablecompilationunit_p.h b/src/qml/jsruntime/qv4executablecompilationunit_p.h
new file mode 100644
index 0000000000..6eef3b12c3
--- /dev/null
+++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h
@@ -0,0 +1,333 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4EXECUTABLECOMPILATIONUNIT_P_H
+#define QV4EXECUTABLECOMPILATIONUNIT_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/qv4compileddata_p.h>
+#include <private/qv4identifier_p.h>
+#include <private/qqmlrefcount_p.h>
+#include <private/qintrusivelist_p.h>
+#include <private/qqmlpropertycachevector_p.h>
+#include <private/qqmltype_p.h>
+#include <private/qqmlnullablevalue_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlScriptData;
+class QQmlEnginePrivate;
+namespace QV4 {
+
+// index is per-object binding index
+typedef QVector<QQmlPropertyData*> BindingPropertyData;
+
+class CompilationUnitMapper;
+struct ResolvedTypeReference;
+// map from name index
+// While this could be a hash, a map is chosen here to provide a stable
+// order, which is used to calculating a check-sum on dependent meta-objects.
+struct ResolvedTypeReferenceMap: public QMap<int, ResolvedTypeReference*>
+{
+ bool addToHash(QCryptographicHash *hash, QQmlEngine *engine) const;
+};
+
+class Q_QML_PRIVATE_EXPORT ExecutableCompilationUnit final: public CompiledData::CompilationUnit,
+ public QQmlRefCount
+{
+ Q_DISABLE_COPY_MOVE(ExecutableCompilationUnit)
+public:
+ friend class QQmlRefPointer<ExecutableCompilationUnit>;
+
+ static QQmlRefPointer<ExecutableCompilationUnit> create(
+ CompiledData::CompilationUnit &&compilationUnit)
+ {
+ return QQmlRefPointer<ExecutableCompilationUnit>(
+ new ExecutableCompilationUnit(std::move(compilationUnit)),
+ QQmlRefPointer<ExecutableCompilationUnit>::Adopt);
+ }
+
+ static QQmlRefPointer<ExecutableCompilationUnit> create()
+ {
+ return QQmlRefPointer<ExecutableCompilationUnit>(
+ new ExecutableCompilationUnit,
+ QQmlRefPointer<ExecutableCompilationUnit>::Adopt);
+ }
+
+ QIntrusiveListNode nextCompilationUnit;
+ ExecutionEngine *engine = nullptr;
+ QQmlEnginePrivate *qmlEngine = nullptr; // only used in QML environment for composite types, not in plain QJSEngine case.
+
+ // url() and fileName() shall be used to load the actual QML/JS code or to show errors or
+ // warnings about that code. They include any potential URL interceptions and thus represent the
+ // "physical" location of the code.
+ //
+ // finalUrl() and finalUrlString() shall be used to resolve further URLs referred to in the code
+ // They are _not_ intercepted and thus represent the "logical" name for the code.
+
+ QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; }
+ QUrl finalUrl() const
+ {
+ if (m_finalUrl.isNull)
+ m_finalUrl = QUrl(finalUrlString());
+ return m_finalUrl;
+ }
+
+ QV4::Lookup *runtimeLookups = nullptr;
+ QVector<QV4::Function *> runtimeFunctions;
+ QVector<QV4::Heap::InternalClass *> runtimeBlocks;
+ mutable QVector<QV4::Heap::Object *> templateObjects;
+ mutable QQmlNullableValue<QUrl> m_url;
+ mutable QQmlNullableValue<QUrl> m_finalUrl;
+
+ // QML specific fields
+ QQmlPropertyCacheVector propertyCaches;
+ QQmlRefPointer<QQmlPropertyCache> rootPropertyCache() const { return propertyCaches.at(/*root object*/0); }
+
+ QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
+
+ // index is object index. This allows fast access to the
+ // property data when initializing bindings, avoiding expensive
+ // lookups by string (property name).
+ QVector<BindingPropertyData> bindingPropertyDataPerObject;
+
+ // mapping from component object index (CompiledData::Unit object index that points to component) to identifier hash of named objects
+ // this is initialized on-demand by QQmlContextData
+ QHash<int, IdentifierHash> namedObjectsPerComponentCache;
+ inline IdentifierHash namedObjectsPerComponent(int componentObjectIndex);
+
+ void finalizeCompositeType(QQmlEnginePrivate *qmlEngine);
+
+ int totalBindingsCount = 0; // Number of bindings used in this type
+ int totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses
+ int totalObjectCount = 0; // Number of objects explicitly instantiated
+
+ QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts;
+ ResolvedTypeReferenceMap resolvedTypes;
+ ResolvedTypeReference *resolvedType(int id) const { return resolvedTypes.value(id); }
+
+ bool verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const;
+
+ int metaTypeId = -1;
+ int listMetaTypeId = -1;
+ bool isRegisteredWithEngine = false;
+
+ QScopedPointer<CompilationUnitMapper> backingFile;
+
+ // --- interface for QQmlPropertyCacheCreator
+ using CompiledObject = CompiledData::Object;
+ using CompiledFunction = CompiledData::Function;
+
+ int objectCount() const { return qmlData->nObjects; }
+ const CompiledObject *objectAt(int index) const
+ {
+ return qmlData->objectAt(index);
+ }
+
+ int importCount() const { return qmlData->nImports; }
+ const CompiledData::Import *importAt(int index) const
+ {
+ return qmlData->importAt(index);
+ }
+
+ Heap::Object *templateObjectAt(int index) const;
+
+ struct FunctionIterator
+ {
+ FunctionIterator(const CompiledData::Unit *unit, const CompiledObject *object, int index)
+ : unit(unit), object(object), index(index) {}
+ const CompiledData::Unit *unit;
+ const CompiledObject *object;
+ int index;
+
+ const CompiledFunction *operator->() const
+ {
+ return unit->functionAt(object->functionOffsetTable()[index]);
+ }
+
+ void operator++() { ++index; }
+ bool operator==(const FunctionIterator &rhs) const { return index == rhs.index; }
+ bool operator!=(const FunctionIterator &rhs) const { return index != rhs.index; }
+ };
+
+ FunctionIterator objectFunctionsBegin(const CompiledObject *object) const
+ {
+ return FunctionIterator(data, object, 0);
+ }
+
+ FunctionIterator objectFunctionsEnd(const CompiledObject *object) const
+ {
+ return FunctionIterator(data, object, object->nFunctions);
+ }
+
+ bool isESModule() const
+ {
+ return data->flags & CompiledData::Unit::IsESModule;
+ }
+
+ bool isSharedLibrary() const
+ {
+ return data->flags & CompiledData::Unit::IsSharedLibrary;
+ }
+
+ QStringList moduleRequests() const;
+ Heap::Module *instantiate(ExecutionEngine *engine);
+ const Value *resolveExport(QV4::String *exportName)
+ {
+ QVector<ResolveSetEntry> resolveSet;
+ return resolveExportRecursively(exportName, &resolveSet);
+ }
+
+ QStringList exportedNames() const
+ {
+ QStringList names;
+ QVector<const ExecutableCompilationUnit*> exportNameSet;
+ getExportedNamesRecursively(&names, &exportNameSet);
+ names.sort();
+ auto last = std::unique(names.begin(), names.end());
+ names.erase(last, names.end());
+ return names;
+ }
+
+ void evaluate();
+ void evaluateModuleRequests();
+
+ QV4::Function *linkToEngine(QV4::ExecutionEngine *engine);
+ void unlink();
+
+ void markObjects(MarkStack *markStack);
+
+ bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString);
+
+ static QString localCacheFilePath(const QUrl &url);
+ bool saveToDisk(const QUrl &unitUrl, QString *errorString);
+
+ QString bindingValueAsString(const CompiledData::Binding *binding) const;
+ QString bindingValueAsScriptString(const CompiledData::Binding *binding) const;
+ double bindingValueAsNumber(const CompiledData::Binding *binding) const
+ {
+ if (binding->type != CompiledData::Binding::Type_Number)
+ return 0.0;
+ return constants[binding->value.constantValueIndex].doubleValue();
+ }
+
+ static bool verifyHeader(const CompiledData::Unit *unit, QDateTime expectedSourceTimeStamp,
+ QString *errorString);
+
+protected:
+ quint32 totalStringCount() const
+ { return data->stringTableSize; }
+
+private:
+ struct ResolveSetEntry
+ {
+ ResolveSetEntry() {}
+ ResolveSetEntry(ExecutableCompilationUnit *module, QV4::String *exportName)
+ : module(module), exportName(exportName) {}
+ ExecutableCompilationUnit *module = nullptr;
+ QV4::String *exportName = nullptr;
+ };
+
+ ExecutableCompilationUnit();
+ ExecutableCompilationUnit(CompiledData::CompilationUnit &&compilationUnit);
+ ~ExecutableCompilationUnit();
+
+ const Value *resolveExportRecursively(QV4::String *exportName,
+ QVector<ResolveSetEntry> *resolveSet);
+
+ QUrl urlAt(int index) const { return QUrl(stringAt(index)); }
+
+ Q_NEVER_INLINE IdentifierHash createNamedObjectsPerComponent(int componentObjectIndex);
+ const CompiledData::ExportEntry *lookupNameInExportTable(
+ const CompiledData::ExportEntry *firstExportEntry, int tableSize,
+ QV4::String *name) const;
+
+ void getExportedNamesRecursively(
+ QStringList *names, QVector<const ExecutableCompilationUnit *> *exportNameSet,
+ bool includeDefaultExport = true) const;
+};
+
+struct ResolvedTypeReference
+{
+ ResolvedTypeReference()
+ : majorVersion(0)
+ , minorVersion(0)
+ , isFullyDynamicType(false)
+ {}
+
+ QQmlType type;
+ QQmlRefPointer<QQmlPropertyCache> typePropertyCache;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
+
+ int majorVersion;
+ int minorVersion;
+ // Types such as QQmlPropertyMap can add properties dynamically at run-time and
+ // therefore cannot have a property cache installed when instantiated.
+ bool isFullyDynamicType;
+
+ QQmlRefPointer<QQmlPropertyCache> propertyCache() const;
+ QQmlRefPointer<QQmlPropertyCache> createPropertyCache(QQmlEngine *);
+ bool addToHash(QCryptographicHash *hash, QQmlEngine *engine);
+
+ void doDynamicTypeCheck();
+};
+
+IdentifierHash ExecutableCompilationUnit::namedObjectsPerComponent(int componentObjectIndex)
+{
+ auto it = namedObjectsPerComponentCache.find(componentObjectIndex);
+ if (Q_UNLIKELY(it == namedObjectsPerComponentCache.end()))
+ return createNamedObjectsPerComponent(componentObjectIndex);
+ return *it;
+}
+
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4EXECUTABLECOMPILATIONUNIT_P_H
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp
index 1bd4329fe8..aeb4835c40 100644
--- a/src/qml/jsruntime/qv4function.cpp
+++ b/src/qml/jsruntime/qv4function.cpp
@@ -73,24 +73,19 @@ ReturnedValue Function::call(const Value *thisObject, const Value *argv, int arg
return result;
}
-Function *Function::create(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function)
+Function *Function::create(ExecutionEngine *engine, ExecutableCompilationUnit *unit,
+ const CompiledData::Function *function)
{
- quint16 traceSlotCount = 0;
-#if QT_CONFIG(qml_tracing)
- traceSlotCount = function->nTraceInfos == CompiledData::Function::NoTracing()
- ? 1
- : function->nTraceInfos;
-#endif
- quint8 *storage = new quint8[sizeof(Function) + traceSlotCount];
- return new(storage) Function(engine, unit, function);
+ return new Function(engine, unit, function);
}
void Function::destroy()
{
- delete[] reinterpret_cast<quint8 *>(this);
+ delete this;
}
-Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function)
+Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit,
+ const CompiledData::Function *function)
: FunctionData(unit)
, compiledFunction(function)
, codeData(function->code())
@@ -105,19 +100,12 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit,
for (quint32 i = 0; i < compiledFunction->nLocals; ++i)
ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
- const quint32_le *formalsIndices = compiledFunction->formalsTable();
+ const CompiledData::Parameter *formalsIndices = compiledFunction->formalsTable();
for (quint32 i = 0; i < compiledFunction->nFormals; ++i)
- ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable);
+ ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[formalsIndices[i].nameIndex]), Attr_NotConfigurable);
internalClass = ic->d();
nFormals = compiledFunction->nFormals;
-
-#if QT_CONFIG(qml_tracing)
- if (tracingEnabled()) {
- for (uint i = 0; i < function->nTraceInfos; ++i)
- *traceInfo(i) = 0;
- }
-#endif
}
Function::~Function()
@@ -160,8 +148,11 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr
// first locals
const quint32_le *localsIndices = compiledFunction->localsTable();
- for (quint32 i = 0; i < compiledFunction->nLocals; ++i)
- internalClass = internalClass->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
+ for (quint32 i = 0; i < compiledFunction->nLocals; ++i) {
+ internalClass = internalClass->addMember(
+ engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]),
+ Attr_NotConfigurable);
+ }
Scope scope(engine);
ScopedString arg(scope);
@@ -188,22 +179,4 @@ QQmlSourceLocation Function::sourceLocation() const
return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column);
}
-QString Function::traceInfoToString()
-{
- QString info = QLatin1String("=== Trace information for ") + name()->toQString() + QLatin1Char(':');
- if (!tracingEnabled())
- return info + QStringLiteral(" disabled. Interpreter call count: %1\n").arg(interpreterCallCount);
- if (compiledFunction->nTraceInfos == 0)
- return info + QLatin1String(" none.\n");
-
- info += QLatin1Char('\n');
- for (uint i = 0, ei = compiledFunction->nTraceInfos; i < ei; ++i) {
- auto bits = QString::number(*traceInfo(i), 2);
- if (bits.size() < 8)
- bits.prepend(QString(8 - bits.size(), '0'));
- info += QStringLiteral(" %1: %2\n").arg(QString::number(i), bits);
- }
- return info;
-}
-
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h
index f8125a58f8..51960863c4 100644
--- a/src/qml/jsruntime/qv4function_p.h
+++ b/src/qml/jsruntime/qv4function_p.h
@@ -51,8 +51,9 @@
//
#include "qv4global_p.h"
-#include <private/qv4compileddata_p.h>
+#include <private/qv4executablecompilationunit_p.h>
#include <private/qv4context_p.h>
+#include <private/qv4string_p.h>
namespace JSC {
class MacroAssemblerCodeRef;
@@ -65,9 +66,13 @@ struct QQmlSourceLocation;
namespace QV4 {
struct Q_QML_EXPORT FunctionData {
- CompiledData::CompilationUnit *compilationUnit;
+ CompiledData::CompilationUnitBase *compilationUnit;
- FunctionData(CompiledData::CompilationUnit *compilationUnit)
+ // Intentionally require an ExecutableCompilationUnit but save only a pointer to
+ // CompilationUnitBase. This is so that we can take advantage of the standard layout
+ // of CompilationUnitBase in the JIT. Furthermore we can safely static_cast to
+ // ExecutableCompilationUnit where we need it.
+ FunctionData(ExecutableCompilationUnit *compilationUnit)
: compilationUnit(compilationUnit)
{}
};
@@ -76,12 +81,24 @@ Q_STATIC_ASSERT(std::is_standard_layout< FunctionData >::value);
struct Q_QML_EXPORT Function : public FunctionData {
private:
- Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function);
+ Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit,
+ const CompiledData::Function *function);
~Function();
public:
const CompiledData::Function *compiledFunction;
+ QV4::ExecutableCompilationUnit *executableCompilationUnit() const
+ {
+ // This is safe: We require an ExecutableCompilationUnit in the ctor.
+ return static_cast<QV4::ExecutableCompilationUnit *>(compilationUnit);
+ }
+
+ QV4::Heap::String *runtimeString(uint i) const
+ {
+ return compilationUnit->runtimeStrings[i];
+ }
+
ReturnedValue call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context);
const char *codeData;
@@ -96,20 +113,21 @@ public:
int interpreterCallCount = 0;
bool isEval = false;
- static Function *create(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function);
+ static Function *create(ExecutionEngine *engine, ExecutableCompilationUnit *unit,
+ const CompiledData::Function *function);
void destroy();
// used when dynamically assigning signal handlers (QQmlConnection)
void updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> &parameters);
inline Heap::String *name() const {
- return compilationUnit->runtimeStrings[compiledFunction->nameIndex];
+ return runtimeString(compiledFunction->nameIndex);
}
static QString prettyName(const Function *function, const void *address);
- inline QString sourceFile() const { return compilationUnit->fileName(); }
- inline QUrl finalUrl() const { return compilationUnit->finalUrl(); }
+ inline QString sourceFile() const { return executableCompilationUnit()->fileName(); }
+ inline QUrl finalUrl() const { return executableCompilationUnit()->finalUrl(); }
inline bool isStrict() const { return compiledFunction->flags & CompiledData::Function::IsStrict; }
inline bool isArrowFunction() const { return compiledFunction->flags & CompiledData::Function::IsArrowFunction; }
@@ -121,32 +139,7 @@ public:
{
if (compiledFunction->nestedFunctionIndex == std::numeric_limits<uint32_t>::max())
return nullptr;
- return compilationUnit->runtimeFunctions[compiledFunction->nestedFunctionIndex];
- }
-
- Q_NEVER_INLINE QString traceInfoToString();
-
- quint8 *traceInfo(uint i)
- {
-#if QT_CONFIG(qml_tracing)
- Q_ASSERT((tracingEnabled() && i < traceInfoCount()) || (i == 0));
- return reinterpret_cast<quint8 *>(this) + sizeof(Function) + i;
-#else
- Q_UNUSED(i);
- return nullptr;
-#endif
- }
-
- quint32 traceInfoCount() const
- { return compiledFunction->nTraceInfos; }
-
- bool tracingEnabled() const
- {
-#if QT_CONFIG(qml_tracing)
- return traceInfoCount() != CompiledData::Function::NoTracing();
-#else
- return false;
-#endif
+ return executableCompilationUnit()->runtimeFunctions[compiledFunction->nestedFunctionIndex];
}
};
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index 41a21ba379..6fb7946023 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -59,10 +59,10 @@
#include "private/qqmlbuiltinfunctions_p.h"
#include <private/qv4jscall_p.h>
#include <private/qv4vme_moth_p.h>
+#include <private/qv4alloca_p.h>
#include <QtCore/QDebug>
#include <algorithm>
-#include "qv4alloca_p.h"
#include "qv4profiling_p.h"
using namespace QV4;
@@ -135,13 +135,13 @@ void Heap::FunctionObject::setFunction(Function *f)
{
if (f) {
function = f;
- function->compilationUnit->addref();
+ function->executableCompilationUnit()->addref();
}
}
void Heap::FunctionObject::destroy()
{
if (function)
- function->compilationUnit->release();
+ function->executableCompilationUnit()->release();
Object::destroy();
}
@@ -229,7 +229,7 @@ void Heap::FunctionCtor::init(QV4::ExecutionContext *scope)
}
// 15.3.2
-QQmlRefPointer<CompiledData::CompilationUnit> FunctionCtor::parse(ExecutionEngine *engine, const Value *argv, int argc, Type t)
+QQmlRefPointer<ExecutableCompilationUnit> FunctionCtor::parse(ExecutionEngine *engine, const Value *argv, int argc, Type t)
{
QString arguments;
QString body;
@@ -273,14 +273,15 @@ QQmlRefPointer<CompiledData::CompilationUnit> FunctionCtor::parse(ExecutionEngin
if (engine->hasException)
return nullptr;
- return cg.generateCompilationUnit();
+ return ExecutableCompilationUnit::create(cg.generateCompilationUnit());
}
ReturnedValue FunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
{
ExecutionEngine *engine = f->engine();
- QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Function);
+ QQmlRefPointer<ExecutableCompilationUnit> compilationUnit
+ = parse(engine, argv, argc, Type_Function);
if (engine->hasException)
return Encode::undefined();
diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h
index e03d49c74d..c99cad8e33 100644
--- a/src/qml/jsruntime/qv4functionobject_p.h
+++ b/src/qml/jsruntime/qv4functionobject_p.h
@@ -87,11 +87,11 @@ DECLARE_HEAP_OBJECT(FunctionObject, Object) {
}
Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name, VTable::Call call);
- void init(QV4::ExecutionContext *scope, QV4::String *name = nullptr);
- void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr);
- void init(QV4::ExecutionContext *scope, const QString &name);
- void init();
- void destroy();
+ Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name = nullptr);
+ Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr);
+ Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, const QString &name);
+ Q_QML_PRIVATE_EXPORT void init();
+ Q_QML_PRIVATE_EXPORT void destroy();
void setFunction(Function *f);
@@ -244,7 +244,7 @@ protected:
Type_Function,
Type_Generator
};
- static QQmlRefPointer<CompiledData::CompilationUnit> parse(ExecutionEngine *engine, const Value *argv, int argc, Type t = Type_Function);
+ static QQmlRefPointer<ExecutableCompilationUnit> parse(ExecutionEngine *engine, const Value *argv, int argc, Type t = Type_Function);
};
struct FunctionPrototype: FunctionObject
@@ -260,7 +260,7 @@ struct FunctionPrototype: FunctionObject
static ReturnedValue method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
-struct IndexedBuiltinFunction : FunctionObject
+struct Q_QML_PRIVATE_EXPORT IndexedBuiltinFunction : FunctionObject
{
V4_OBJECT2(IndexedBuiltinFunction, FunctionObject)
};
diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp
index 14caa6953f..4eee6f4338 100644
--- a/src/qml/jsruntime/qv4generatorobject.cpp
+++ b/src/qml/jsruntime/qv4generatorobject.cpp
@@ -58,7 +58,7 @@ ReturnedValue GeneratorFunctionCtor::virtualCallAsConstructor(const FunctionObje
{
ExecutionEngine *engine = f->engine();
- QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Generator);
+ QQmlRefPointer<ExecutableCompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Generator);
if (engine->hasException)
return Encode::undefined();
diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h
index d47393b3bb..c6a737b467 100644
--- a/src/qml/jsruntime/qv4global_p.h
+++ b/src/qml/jsruntime/qv4global_p.h
@@ -52,6 +52,7 @@
//
#include <QtCore/qglobal.h>
+#include <private/qv4compilerglobal_p.h>
#include <QString>
#ifdef QT_NO_DEBUG
@@ -63,10 +64,6 @@
#include <qtqmlglobal.h>
#include <private/qtqmlglobal_p.h>
-#ifdef QT_QML_BOOTSTRAPPED
-#define V4_BOOTSTRAP
-#endif
-
#if defined(Q_CC_MSVC)
#include <float.h>
#include <math.h>
@@ -82,53 +79,9 @@ inline bool isfinite(double d) { return _finite(d); }
inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); }
#endif
-// Decide whether to enable or disable the JIT
-
-// White list architectures
-//
-// NOTE: This should match the logic in qv4targetplatform_p.h!
-
-#if defined(Q_PROCESSOR_X86_32) && (QT_POINTER_SIZE == 4) \
- && (defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD))
-# define V4_ENABLE_JIT
-#elif defined(Q_PROCESSOR_X86_64) && (QT_POINTER_SIZE == 8) \
- && (defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_MAC) || defined(Q_OS_FREEBSD))
-# define V4_ENABLE_JIT
-#elif defined(Q_PROCESSOR_ARM_32) && (QT_POINTER_SIZE == 4) \
- && (defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD) || defined(Q_OS_INTEGRITY))
-# if defined(thumb2) || defined(__thumb2__) || ((defined(__thumb) || defined(__thumb__)) && __TARGET_ARCH_THUMB-0 == 4)
-# define V4_ENABLE_JIT
-# elif defined(__ARM_ARCH_ISA_THUMB) && __ARM_ARCH_ISA_THUMB == 2 // clang 3.5 and later will set this if the core supports the Thumb-2 ISA.
-# define V4_ENABLE_JIT
-# endif
-#elif defined(Q_PROCESSOR_ARM_64) && (QT_POINTER_SIZE == 8)
-# if defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_INTEGRITY)
-# define V4_ENABLE_JIT
-# endif
-//#elif defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX)
-//# define V4_ENABLE_JIT
-#endif
-
-// check FPU with double precision on ARM platform
-#if (defined(Q_PROCESSOR_ARM_64) || defined(Q_PROCESSOR_ARM_32)) && defined(V4_ENABLE_JIT) && defined(__ARM_FP) && (__ARM_FP <= 0x04)
-# undef V4_ENABLE_JIT
-#endif
-
-// Black list some platforms
-#if defined(V4_ENABLE_JIT)
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
-# undef V4_ENABLE_JIT
-#endif
-#endif
-
-// For debug purposes: add CONFIG+=force-compile-jit to qmake's command-line to always compile the JIT.
-#if defined(V4_FORCE_COMPILE_JIT) && !defined(V4_ENABLE_JIT)
-# define V4_ENABLE_JIT
-#endif
-
// Do certain things depending on whether the JIT is enabled or disabled
-#ifdef V4_ENABLE_JIT
+#if QT_CONFIG(qml_jit)
#define ENABLE_YARR_JIT 1
#define ENABLE_JIT 1
#define ENABLE_ASSEMBLER 1
@@ -255,11 +208,6 @@ struct SetMapObject;
struct PromiseObject;
struct PromiseCapability;
-// ReturnedValue is used to return values from runtime methods
-// the type has to be a primitive type (no struct or union), so that the compiler
-// will return it in a register on all platforms.
-// It will be returned in rax on x64, [eax,edx] on x86 and [r0,r1] on arm
-typedef quint64 ReturnedValue;
struct CallData;
struct Scope;
struct ScopedValue;
@@ -280,20 +228,6 @@ struct IdentifierTable;
class RegExpCache;
class MultiplyWrappedQObjectMap;
-enum class ObservedTraceValues : quint8 {
- Integer = 1 << 0,
- Boolean = 1 << 1,
- Double = 1 << 2,
- Other = 1 << 3,
- TypeMask = Integer | Boolean | Double | Other,
-
- TruePathTaken = 1 << 0,
- FalsePathTaken = 1 << 1,
-
- ArrayWasAccessed = 1 << 7,
- ArrayAccessNeededFallback = 1 << 6,
-};
-
enum PropertyFlag {
Attr_Data = 0,
Attr_Accessor = 0x1,
@@ -403,13 +337,6 @@ struct Q_QML_EXPORT StackFrame {
};
typedef QVector<StackFrame> StackTrace;
-enum class ObjectLiteralArgument {
- Value,
- Method,
- Getter,
- Setter
-};
-
namespace JIT {
enum class CallResultDestination {
diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp
index becdc3bc55..bb81fb52d4 100644
--- a/src/qml/jsruntime/qv4globalobject.cpp
+++ b/src/qml/jsruntime/qv4globalobject.cpp
@@ -49,18 +49,14 @@
#include "qv4string_p.h"
#include "qv4jscall_p.h"
-#include <private/qqmljsengine_p.h>
-#include <private/qqmljslexer_p.h>
-#include <private/qqmljsparser_p.h>
-#include <private/qqmljsast_p.h>
-#include <qv4codegen_p.h>
+#include <private/qv4codegen_p.h>
+#include <private/qv4alloca_p.h>
#include "private/qlocale_tools_p.h"
#include "private/qtools_p.h"
#include <QtCore/QDebug>
#include <QtCore/QString>
#include <iostream>
-#include "qv4alloca_p.h"
#include <wtf/MathExtras.h>
diff --git a/src/qml/jsruntime/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp
index 5db5bd46ec..c3d7165f71 100644
--- a/src/qml/jsruntime/qv4identifier.cpp
+++ b/src/qml/jsruntime/qv4identifier.cpp
@@ -39,29 +39,19 @@
#include "qv4identifier_p.h"
#include "qv4identifiertable_p.h"
#include "qv4string_p.h"
+#include <private/qprimefornumbits_p.h>
QT_BEGIN_NAMESPACE
namespace QV4 {
-static const uchar prime_deltas[] = {
- 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3,
- 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0
-};
-
-static inline int primeForNumBits(int numBits)
-{
- return (1 << numBits) + prime_deltas[numBits];
-}
-
-
IdentifierHashData::IdentifierHashData(IdentifierTable *table, int numBits)
: size(0)
, numBits(numBits)
, identifierTable(table)
{
- refCount.store(1);
- alloc = primeForNumBits(numBits);
+ refCount.storeRelaxed(1);
+ alloc = qPrimeForNumBits(numBits);
entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry));
memset(entries, 0, alloc*sizeof(IdentifierHashEntry));
identifierTable->addIdentifierHash(this);
@@ -72,7 +62,7 @@ IdentifierHashData::IdentifierHashData(IdentifierHashData *other)
, numBits(other->numBits)
, identifierTable(other->identifierTable)
{
- refCount.store(1);
+ refCount.storeRelaxed(1);
alloc = other->alloc;
entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry));
memcpy(entries, other->entries, alloc*sizeof(IdentifierHashEntry));
@@ -92,7 +82,7 @@ IdentifierHash::IdentifierHash(ExecutionEngine *engine)
void IdentifierHash::detach()
{
- if (!d || d->refCount == 1)
+ if (!d || d->refCount.loadAcquire() == 1)
return;
IdentifierHashData *newData = new IdentifierHashData(d);
if (d && !d->refCount.deref())
@@ -110,7 +100,7 @@ IdentifierHashEntry *IdentifierHash::addEntry(PropertyKey identifier)
if (grow) {
++d->numBits;
- int newAlloc = primeForNumBits(d->numBits);
+ int newAlloc = qPrimeForNumBits(d->numBits);
IdentifierHashEntry *newEntries = (IdentifierHashEntry *)malloc(newAlloc * sizeof(IdentifierHashEntry));
memset(newEntries, 0, newAlloc*sizeof(IdentifierHashEntry));
for (int i = 0; i < d->alloc; ++i) {
diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp
index 4305bc4647..21b47c3909 100644
--- a/src/qml/jsruntime/qv4identifiertable.cpp
+++ b/src/qml/jsruntime/qv4identifiertable.cpp
@@ -38,28 +38,18 @@
****************************************************************************/
#include "qv4identifiertable_p.h"
#include "qv4symbol_p.h"
+#include <private/qprimefornumbits_p.h>
QT_BEGIN_NAMESPACE
namespace QV4 {
-static const uchar prime_deltas[] = {
- 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3,
- 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0
-};
-
-static inline int primeForNumBits(int numBits)
-{
- return (1 << numBits) + prime_deltas[numBits];
-}
-
-
IdentifierTable::IdentifierTable(ExecutionEngine *engine, int numBits)
: engine(engine)
, size(0)
, numBits(numBits)
{
- alloc = primeForNumBits(numBits);
+ alloc = qPrimeForNumBits(numBits);
entriesByHash = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *));
entriesById = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *));
memset(entriesByHash, 0, alloc*sizeof(Heap::String *));
@@ -87,7 +77,7 @@ void IdentifierTable::addEntry(Heap::StringOrSymbol *str)
if (grow) {
++numBits;
- int newAlloc = primeForNumBits(numBits);
+ int newAlloc = qPrimeForNumBits(numBits);
Heap::StringOrSymbol **newEntries = (Heap::StringOrSymbol **)malloc(newAlloc*sizeof(Heap::String *));
memset(newEntries, 0, newAlloc*sizeof(Heap::StringOrSymbol *));
for (uint i = 0; i < alloc; ++i) {
@@ -216,9 +206,8 @@ PropertyKey IdentifierTable::asPropertyKeyImpl(const Heap::String *str)
Heap::StringOrSymbol *IdentifierTable::resolveId(PropertyKey i) const
{
- uint arrayIdx = i.asArrayIndex();
- if (arrayIdx < UINT_MAX)
- return engine->newString(QString::number(arrayIdx));
+ if (i.isArrayIndex())
+ return engine->newString(QString::number(i.asArrayIndex()));
if (!i.isValid())
return nullptr;
diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp
index 36569b0a60..92face6f94 100644
--- a/src/qml/jsruntime/qv4include.cpp
+++ b/src/qml/jsruntime/qv4include.cpp
@@ -72,13 +72,17 @@ QV4Include::QV4Include(const QUrl &url, QV4::ExecutionEngine *engine,
m_resultObject.set(v4, resultValue(v4));
#if QT_CONFIG(qml_network)
- m_network = engine->v8Engine->networkAccessManager();
+ if (QQmlEngine *qmlEngine = engine->qmlEngine()) {
+ m_network = qmlEngine->networkAccessManager();
- QNetworkRequest request;
- request.setUrl(url);
+ QNetworkRequest request;
+ request.setUrl(url);
- m_reply = m_network->get(request);
- QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished()));
+ m_reply = m_network->get(request);
+ QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished()));
+ } else {
+ finished();
+ }
#else
finished();
#endif
@@ -163,7 +167,6 @@ void QV4Include::finished()
QByteArray data = m_reply->readAll();
QString code = QString::fromUtf8(data);
- QmlIR::Document::removeScriptPragmas(code);
QV4::Scoped<QV4::QmlContext> qml(scope, m_qmlContext.value());
QV4::Script script(v4, qml, /*parse as QML binding*/false, code, m_url.toString());
@@ -197,7 +200,7 @@ void QV4Include::finished()
}
/*
- Documented in qv8engine.cpp
+ Documented in qv4engine.cpp
*/
QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *argv, int argc)
{
@@ -214,7 +217,6 @@ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, cons
if (argc >= 2 && argv[1].as<QV4::FunctionObject>())
callbackFunction = argv[1];
-#if QT_CONFIG(qml_network)
QUrl url(scope.engine->resolvedUrl(argv[0].toQStringNoThrow()));
if (scope.engine->qmlEngine() && scope.engine->qmlEngine()->urlInterceptor())
url = scope.engine->qmlEngine()->urlInterceptor()->intercept(url, QQmlAbstractUrlInterceptor::JavaScriptFile);
@@ -225,9 +227,13 @@ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, cons
QV4::Scoped<QV4::QmlContext> qmlcontext(scope, scope.engine->qmlContext());
if (localFile.isEmpty()) {
+#if QT_CONFIG(qml_network)
QV4Include *i = new QV4Include(url, scope.engine, qmlcontext, callbackFunction);
result = i->result();
-
+#else
+ result = resultValue(scope.engine, NetworkError);
+ callback(callbackFunction, result);
+#endif
} else {
QScopedPointer<QV4::Script> script;
QString error;
@@ -252,12 +258,6 @@ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, cons
callback(callbackFunction, result);
}
-#else
- QV4::ScopedValue result(scope);
- result = resultValue(scope.engine, NetworkError);
- callback(callbackFunction, result);
-#endif
-
return result->asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp
index a10fda79f2..70849775cb 100644
--- a/src/qml/jsruntime/qv4internalclass.cpp
+++ b/src/qml/jsruntime/qv4internalclass.cpp
@@ -45,27 +45,18 @@
#include "qv4identifiertable_p.h"
#include "qv4value_p.h"
#include "qv4mm_p.h"
+#include <private/qprimefornumbits_p.h>
QT_BEGIN_NAMESPACE
namespace QV4 {
-static const uchar prime_deltas[] = {
- 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3,
- 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0
-};
-
-static inline int primeForNumBits(int numBits)
-{
- return (1 << numBits) + prime_deltas[numBits];
-}
-
PropertyHashData::PropertyHashData(int numBits)
: refCount(1)
, size(0)
, numBits(numBits)
{
- alloc = primeForNumBits(numBits);
+ alloc = qPrimeForNumBits(numBits);
entries = (PropertyHash::Entry *)malloc(alloc*sizeof(PropertyHash::Entry));
memset(entries, 0, alloc*sizeof(PropertyHash::Entry));
}
@@ -299,7 +290,6 @@ void InternalClass::init(ExecutionEngine *engine)
void InternalClass::init(Heap::InternalClass *other)
{
Base::init();
- Q_ASSERT(!other->isFrozen);
new (&propertyTable) PropertyHash(other->propertyTable);
new (&nameMap) SharedInternalClassData<PropertyKey>(other->nameMap);
new (&propertyData) SharedInternalClassData<PropertyAttributes>(other->propertyData);
@@ -573,22 +563,6 @@ Heap::InternalClass *InternalClass::sealed()
if (isSealed)
return this;
- bool alreadySealed = !extensible;
- for (uint i = 0; i < size; ++i) {
- PropertyAttributes attrs = propertyData.at(i);
- if (attrs.isEmpty())
- continue;
- if (attrs.isConfigurable()) {
- alreadySealed = false;
- break;
- }
- }
-
- if (alreadySealed) {
- isSealed = true;
- return this;
- }
-
Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Sealed };
Transition &t = lookupOrInsertTransition(temp);
@@ -601,14 +575,15 @@ Heap::InternalClass *InternalClass::sealed()
Scoped<QV4::InternalClass> ic(scope, engine->newClass(this));
Heap::InternalClass *s = ic->d();
- for (uint i = 0; i < size; ++i) {
- PropertyAttributes attrs = propertyData.at(i);
- if (attrs.isEmpty())
- continue;
- attrs.setConfigurable(false);
- s->propertyData.set(i, attrs);
+ if (!isFrozen) { // freezing also makes all properties non-configurable
+ for (uint i = 0; i < size; ++i) {
+ PropertyAttributes attrs = propertyData.at(i);
+ if (attrs.isEmpty())
+ continue;
+ attrs.setConfigurable(false);
+ s->propertyData.set(i, attrs);
+ }
}
- s->extensible = false;
s->isSealed = true;
t.lookup = s;
@@ -620,28 +595,11 @@ Heap::InternalClass *InternalClass::frozen()
if (isFrozen)
return this;
- bool alreadyFrozen = !extensible;
- for (uint i = 0; i < size; ++i) {
- PropertyAttributes attrs = propertyData.at(i);
- if (attrs.isEmpty())
- continue;
- if ((attrs.isData() && attrs.isWritable()) || attrs.isConfigurable()) {
- alreadyFrozen = false;
- break;
- }
- }
-
- if (alreadyFrozen) {
- isSealed = true;
- isFrozen = true;
- return this;
- }
-
Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Frozen };
Transition &t = lookupOrInsertTransition(temp);
if (t.lookup) {
- Q_ASSERT(t.lookup && t.lookup->isSealed && t.lookup->isFrozen);
+ Q_ASSERT(t.lookup && t.lookup->isFrozen);
return t.lookup;
}
@@ -658,29 +616,42 @@ Heap::InternalClass *InternalClass::frozen()
attrs.setConfigurable(false);
f->propertyData.set(i, attrs);
}
- f->extensible = false;
- f->isSealed = true;
f->isFrozen = true;
t.lookup = f;
return f;
}
-Heap::InternalClass *InternalClass::propertiesFrozen()
+InternalClass *InternalClass::canned()
+{
+ // scope the intermediate result to prevent it from getting garbage collected
+ Scope scope(engine);
+ Scoped<QV4::InternalClass> ic(scope, sealed());
+ return ic->d()->nonExtensible();
+}
+
+InternalClass *InternalClass::cryopreserved()
{
+ // scope the intermediate result to prevent it from getting garbage collected
Scope scope(engine);
- Scoped<QV4::InternalClass> frozen(scope, this);
+ Scoped<QV4::InternalClass> ic(scope, frozen());
+ return ic->d()->canned();
+}
+
+bool InternalClass::isImplicitlyFrozen() const
+{
+ if (isFrozen)
+ return true;
+
for (uint i = 0; i < size; ++i) {
- PropertyAttributes attrs = propertyData.at(i);
- if (!nameMap.at(i).isValid())
+ const PropertyAttributes attrs = propertyData.at(i);
+ if (attrs.isEmpty())
continue;
- if (!attrs.isEmpty()) {
- attrs.setWritable(false);
- attrs.setConfigurable(false);
- }
- frozen = frozen->changeMember(nameMap.at(i), attrs);
+ if ((attrs.isData() && attrs.isWritable()) || attrs.isConfigurable())
+ return false;
}
- return frozen->d();
+
+ return true;
}
Heap::InternalClass *InternalClass::asProtoClass()
diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h
index 7bb10f47a3..403702ae55 100644
--- a/src/qml/jsruntime/qv4internalclass_p.h
+++ b/src/qml/jsruntime/qv4internalclass_p.h
@@ -432,7 +432,9 @@ struct InternalClass : Base {
Q_REQUIRED_RESULT InternalClass *sealed();
Q_REQUIRED_RESULT InternalClass *frozen();
- Q_REQUIRED_RESULT InternalClass *propertiesFrozen();
+ Q_REQUIRED_RESULT InternalClass *canned(); // sealed + nonExtensible
+ Q_REQUIRED_RESULT InternalClass *cryopreserved(); // frozen + sealed + nonExtensible
+ bool isImplicitlyFrozen() const;
Q_REQUIRED_RESULT InternalClass *asProtoClass();
diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp
index 6b8fb509a8..0cda6b864a 100644
--- a/src/qml/jsruntime/qv4lookup.cpp
+++ b/src/qml/jsruntime/qv4lookup.cpp
@@ -467,11 +467,11 @@ bool Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object,
return false;
}
- if (l->setter == Lookup::setter0 || l->setter == Lookup::setter0Inline) {
+ if (l->setter == Lookup::setter0MemberData || l->setter == Lookup::setter0Inline) {
l->objectLookupTwoClasses.ic = first.objectLookup.ic;
l->objectLookupTwoClasses.ic2 = second.objectLookup.ic;
- l->objectLookupTwoClasses.offset = first.objectLookup.offset;
- l->objectLookupTwoClasses.offset2 = second.objectLookup.offset;
+ l->objectLookupTwoClasses.offset = first.objectLookup.index;
+ l->objectLookupTwoClasses.offset2 = second.objectLookup.index;
l->setter = setter0setter0;
return true;
}
@@ -492,11 +492,11 @@ bool Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, c
return o->put(name, value);
}
-bool Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
+bool Lookup::setter0MemberData(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o && o->internalClass == l->objectLookup.ic) {
- o->setProperty(engine, l->objectLookup.offset, value);
+ o->memberData->values.set(engine, l->objectLookup.offset, value);
return true;
}
@@ -507,7 +507,7 @@ bool Lookup::setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, co
{
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o && o->internalClass == l->objectLookup.ic) {
- o->setInlineProperty(engine, l->objectLookup.offset, value);
+ o->setInlinePropertyWithOffset(engine, l->objectLookup.offset, value);
return true;
}
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h
index 4fd5e133f7..31c90b31f6 100644
--- a/src/qml/jsruntime/qv4lookup_p.h
+++ b/src/qml/jsruntime/qv4lookup_p.h
@@ -54,17 +54,14 @@
#include "qv4runtime_p.h"
#include "qv4engine_p.h"
#include "qv4context_p.h"
-
-#if !defined(V4_BOOTSTRAP)
#include "qv4object_p.h"
#include "qv4internalclass_p.h"
-#endif
QT_BEGIN_NAMESPACE
namespace QV4 {
-struct Lookup {
+struct Q_QML_PRIVATE_EXPORT Lookup {
union {
ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object);
ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine);
@@ -81,8 +78,9 @@ struct Lookup {
} markDef;
struct {
Heap::InternalClass *ic;
- quintptr _unused;
- int offset;
+ quintptr unused;
+ uint index;
+ uint offset;
} objectLookup;
struct {
quintptr protoId;
@@ -92,8 +90,8 @@ struct Lookup {
struct {
Heap::InternalClass *ic;
Heap::InternalClass *ic2;
- int offset;
- int offset2;
+ uint offset;
+ uint offset2;
} objectLookupTwoClasses;
struct {
quintptr protoId;
@@ -111,12 +109,14 @@ struct Lookup {
struct {
Heap::InternalClass *newClass;
quintptr protoId;
- int offset;
+ uint offset;
+ uint unused;
} insertionLookup;
struct {
quintptr _unused;
quintptr _unused2;
uint index;
+ uint unused;
} indexedLookup;
struct {
Heap::InternalClass *ic;
@@ -151,6 +151,19 @@ struct Lookup {
quintptr reserved3;
ReturnedValue (*getterTrampoline)(Lookup *l, ExecutionEngine *engine);
} qmlContextGlobalLookup;
+ struct {
+ Heap::Object *qmlTypeWrapper;
+ quintptr unused2;
+ } qmlTypeLookup;
+ struct {
+ Heap::InternalClass *ic;
+ quintptr unused;
+ ReturnedValue encodedEnumValue;
+ } qmlEnumValueLookup;
+ struct {
+ Heap::InternalClass *ic;
+ Heap::Object *qmlScopedEnumWrapper;
+ } qmlScopedEnumWrapperLookup;
};
uint nameIndex;
@@ -187,7 +200,7 @@ struct Lookup {
static bool setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
Q_NEVER_INLINE static bool setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
- static bool setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
+ static bool setter0MemberData(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h
index d85b30a056..4f22dc7330 100644
--- a/src/qml/jsruntime/qv4managed_p.h
+++ b/src/qml/jsruntime/qv4managed_p.h
@@ -54,7 +54,6 @@
#include "qv4value_p.h"
#include "qv4enginebase_p.h"
#include <private/qv4heap_p.h>
-#include <private/qv4writebarrier_p.h>
#include <private/qv4vtable_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/qml/jsruntime/qv4mapobject.cpp b/src/qml/jsruntime/qv4mapobject.cpp
index 68741e7677..90e1908a84 100644
--- a/src/qml/jsruntime/qv4mapobject.cpp
+++ b/src/qml/jsruntime/qv4mapobject.cpp
@@ -80,7 +80,7 @@ ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv,
if (!adder)
return scope.engine->throwTypeError();
- ScopedObject iter(scope, Runtime::method_getIterator(scope.engine, iterable, true));
+ ScopedObject iter(scope, Runtime::GetIterator::call(scope.engine, iterable, true));
if (scope.hasException())
return Encode::undefined();
Q_ASSERT(iter);
@@ -89,7 +89,7 @@ ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv,
Value *arguments = scope.alloc(2);
ScopedValue done(scope);
forever {
- done = Runtime::method_iteratorNext(scope.engine, iter, obj);
+ done = Runtime::IteratorNext::call(scope.engine, iter, obj);
if (scope.hasException())
break;
if (done->toBoolean())
@@ -112,7 +112,7 @@ ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv,
break;
}
ScopedValue falsey(scope, Encode(false));
- return Runtime::method_iteratorClose(scope.engine, iter, falsey);
+ return Runtime::IteratorClose::call(scope.engine, iter, falsey);
}
}
return a->asReturnedValue();
diff --git a/src/qml/jsruntime/qv4math_p.h b/src/qml/jsruntime/qv4math_p.h
index a60a49a811..6632d69c27 100644
--- a/src/qml/jsruntime/qv4math_p.h
+++ b/src/qml/jsruntime/qv4math_p.h
@@ -52,6 +52,7 @@
#include <qglobal.h>
+#include <private/qv4staticvalue_p.h>
#include <QtCore/qnumeric.h>
#include <QtCore/private/qnumeric_p.h>
#include <cmath>
@@ -66,43 +67,28 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
-static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b, quint8 *traceInfo = nullptr)
+static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b)
{
int result;
- if (Q_UNLIKELY(add_overflow(a, b, &result))) {
- if (traceInfo)
- *traceInfo |= quint8(QV4::ObservedTraceValues::Double);
- return Value::fromDouble(static_cast<double>(a) + b).asReturnedValue();
- }
- if (traceInfo)
- *traceInfo |= quint8(QV4::ObservedTraceValues::Integer);
- return Value::fromInt32(result).asReturnedValue();
+ if (Q_UNLIKELY(add_overflow(a, b, &result)))
+ return StaticValue::fromDouble(static_cast<double>(a) + b).asReturnedValue();
+ return StaticValue::fromInt32(result).asReturnedValue();
}
-static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b, quint8 *traceInfo = nullptr)
+static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b)
{
int result;
- if (Q_UNLIKELY(sub_overflow(a, b, &result))) {
- if (traceInfo)
- *traceInfo |= quint8(QV4::ObservedTraceValues::Double);
- return Value::fromDouble(static_cast<double>(a) - b).asReturnedValue();
- }
- if (traceInfo)
- *traceInfo |= quint8(QV4::ObservedTraceValues::Integer);
- return Value::fromInt32(result).asReturnedValue();
+ if (Q_UNLIKELY(sub_overflow(a, b, &result)))
+ return StaticValue::fromDouble(static_cast<double>(a) - b).asReturnedValue();
+ return StaticValue::fromInt32(result).asReturnedValue();
}
-static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b, quint8 *traceInfo = nullptr)
+static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b)
{
int result;
- if (Q_UNLIKELY(mul_overflow(a, b, &result))) {
- if (traceInfo)
- *traceInfo |= quint8(QV4::ObservedTraceValues::Double);
- return Value::fromDouble(static_cast<double>(a) * b).asReturnedValue();
- }
- if (traceInfo)
- *traceInfo |= quint8(QV4::ObservedTraceValues::Integer);
- return Value::fromInt32(result).asReturnedValue();
+ if (Q_UNLIKELY(mul_overflow(a, b, &result)))
+ return StaticValue::fromDouble(static_cast<double>(a) * b).asReturnedValue();
+ return StaticValue::fromInt32(result).asReturnedValue();
}
}
diff --git a/src/qml/jsruntime/qv4module.cpp b/src/qml/jsruntime/qv4module.cpp
index 237ada8321..08a1900383 100644
--- a/src/qml/jsruntime/qv4module.cpp
+++ b/src/qml/jsruntime/qv4module.cpp
@@ -52,7 +52,7 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(Module);
-void Heap::Module::init(ExecutionEngine *engine, CompiledData::CompilationUnit *moduleUnit)
+void Heap::Module::init(ExecutionEngine *engine, ExecutableCompilationUnit *moduleUnit)
{
Object::init();
@@ -106,7 +106,7 @@ void Module::evaluate()
return;
d()->evaluated = true;
- CompiledData::CompilationUnit *unit = d()->unit;
+ ExecutableCompilationUnit *unit = d()->unit;
unit->evaluateModuleRequests();
diff --git a/src/qml/jsruntime/qv4module_p.h b/src/qml/jsruntime/qv4module_p.h
index dca0678fe9..aabb2e005e 100644
--- a/src/qml/jsruntime/qv4module_p.h
+++ b/src/qml/jsruntime/qv4module_p.h
@@ -60,7 +60,7 @@ namespace QV4 {
namespace Heap {
#define ModuleMembers(class, Member) \
- Member(class, NoMark, CompiledData::CompilationUnit *, unit) \
+ Member(class, NoMark, ExecutableCompilationUnit *, unit) \
Member(class, Pointer, CallContext *, scope) \
Member(class, HeapValue, HeapValue, self) \
Member(class, NoMark, bool, evaluated)
@@ -68,7 +68,7 @@ namespace Heap {
DECLARE_EXPORTED_HEAP_OBJECT(Module, Object) {
DECLARE_MARKOBJECTS(Module)
- void init(ExecutionEngine *engine, CompiledData::CompilationUnit *moduleUnit);
+ void init(ExecutionEngine *engine, ExecutableCompilationUnit *moduleUnit);
};
}
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index 02524b7da6..89161433ed 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -407,8 +407,8 @@ ReturnedValue Object::internalGet(PropertyKey id, const Value *receiver, bool *h
{
Heap::Object *o = d();
- uint index = id.asArrayIndex();
- if (index != UINT_MAX) {
+ if (id.isArrayIndex()) {
+ const uint index = id.asArrayIndex();
Scope scope(this);
PropertyAttributes attrs;
ScopedProperty pd(scope);
@@ -432,8 +432,6 @@ ReturnedValue Object::internalGet(PropertyKey id, const Value *receiver, bool *h
break;
}
} else {
- Q_ASSERT(!id.isArrayIndex());
-
while (1) {
auto idx = o->internalClass->findValueOrGetter(id);
if (idx.isValid()) {
@@ -471,14 +469,13 @@ bool Object::internalPut(PropertyKey id, const Value &value, Value *receiver)
if (d()->internalClass->vtable->getOwnProperty == Object::virtualGetOwnProperty) {
// This object standard methods in the vtable, so we can take a shortcut
// and avoid the calls to getOwnProperty and defineOwnProperty
- uint index = id.asArrayIndex();
PropertyAttributes attrs;
PropertyIndex propertyIndex{nullptr, nullptr};
- if (index != UINT_MAX) {
+ if (id.isArrayIndex()) {
if (arrayData())
- propertyIndex = arrayData()->getValueOrSetter(index, &attrs);
+ propertyIndex = arrayData()->getValueOrSetter(id.asArrayIndex(), &attrs);
} else {
auto member = internalClass()->findValueOrSetter(id);
if (member.isValid()) {
@@ -547,12 +544,11 @@ bool Object::internalPut(PropertyKey id, const Value &value, Value *receiver)
if (r->internalClass()->vtable->defineOwnProperty == virtualDefineOwnProperty) {
// standard object, we can avoid some more checks
- uint index = id.asArrayIndex();
- if (index == UINT_MAX) {
+ if (id.isArrayIndex()) {
+ r->arraySet(id.asArrayIndex(), value);
+ } else {
ScopedStringOrSymbol s(scope, id.asStringOrSymbol());
r->insertMember(s, value);
- } else {
- r->arraySet(index, value);
}
return true;
}
@@ -787,8 +783,15 @@ bool Object::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine,
return lookup->setter(lookup, engine, *object, value);
} else if (idx.attrs.isData() && idx.attrs.isWritable()) {
lookup->objectLookup.ic = object->internalClass();
- lookup->objectLookup.offset = idx.index;
- lookup->setter = idx.index < object->d()->vtable()->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0;
+ lookup->objectLookup.index = idx.index;
+ const auto nInline = object->d()->vtable()->nInlineProperties;
+ if (idx.index < nInline) {
+ lookup->setter = Lookup::setter0Inline;
+ lookup->objectLookup.offset = idx.index + object->d()->vtable()->inlinePropertyOffset;
+ } else {
+ lookup->setter = Lookup::setter0MemberData;
+ lookup->objectLookup.offset = idx.index - nInline;
+ }
return lookup->setter(lookup, engine, *object, value);
} else {
// ### handle setter
diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h
index bee4aadafe..f3375929a3 100644
--- a/src/qml/jsruntime/qv4object_p.h
+++ b/src/qml/jsruntime/qv4object_p.h
@@ -79,21 +79,21 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) {
}
const Value *inlinePropertyDataWithOffset(uint indexWithOffset) const {
- Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < vtable()->inlinePropertyOffset + vtable()->nInlineProperties);
+ Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < uint(vtable()->inlinePropertyOffset + vtable()->nInlineProperties));
return reinterpret_cast<const Value *>(this) + indexWithOffset;
}
const Value *inlinePropertyData(uint index) const {
Q_ASSERT(index < vtable()->nInlineProperties);
return reinterpret_cast<const Value *>(this) + vtable()->inlinePropertyOffset + index;
}
- void setInlineProperty(ExecutionEngine *e, uint index, Value v) {
- Q_ASSERT(index < vtable()->nInlineProperties);
- Value *prop = reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index;
+ void setInlinePropertyWithOffset(ExecutionEngine *e, uint indexWithOffset, Value v) {
+ Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < uint(vtable()->inlinePropertyOffset + vtable()->nInlineProperties));
+ Value *prop = reinterpret_cast<Value *>(this) + indexWithOffset;
WriteBarrier::write(e, this, prop->data_ptr(), v.asReturnedValue());
}
- void setInlineProperty(ExecutionEngine *e, uint index, Heap::Base *b) {
- Q_ASSERT(index < vtable()->nInlineProperties);
- Value *prop = reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index;
+ void setInlinePropertyWithOffset(ExecutionEngine *e, uint indexWithOffset, Heap::Base *b) {
+ Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < uint(vtable()->inlinePropertyOffset + vtable()->nInlineProperties));
+ Value *prop = reinterpret_cast<Value *>(this) + indexWithOffset;
WriteBarrier::write(e, this, prop->data_ptr(), Value::fromHeapObject(b).asReturnedValue());
}
@@ -115,7 +115,7 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) {
void setProperty(ExecutionEngine *e, uint index, Value v) {
uint nInline = vtable()->nInlineProperties;
if (index < nInline) {
- setInlineProperty(e, index, v);
+ setInlinePropertyWithOffset(e, index + vtable()->inlinePropertyOffset, v);
return;
}
index -= nInline;
@@ -124,7 +124,7 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) {
void setProperty(ExecutionEngine *e, uint index, Heap::Base *b) {
uint nInline = vtable()->nInlineProperties;
if (index < nInline) {
- setInlineProperty(e, index, b);
+ setInlinePropertyWithOffset(e, index + vtable()->inlinePropertyOffset, b);
return;
}
index -= nInline;
@@ -410,7 +410,7 @@ private:
friend struct ObjectPrototype;
};
-struct ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator
+struct Q_QML_PRIVATE_EXPORT ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator
{
uint arrayIndex = 0;
uint memberIndex = 0;
@@ -543,13 +543,11 @@ inline const ArrayObject *Value::as() const {
return isManaged() && m()->internalClass->vtable->type == Managed::Type_ArrayObject ? static_cast<const ArrayObject *>(this) : nullptr;
}
-#ifndef V4_BOOTSTRAP
template<>
inline ReturnedValue value_convert<Object>(ExecutionEngine *e, const Value &v)
{
return v.toObject(e)->asReturnedValue();
}
-#endif
}
diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp
index 6b4c3ba71a..3d3b3f413f 100644
--- a/src/qml/jsruntime/qv4objectproto.cpp
+++ b/src/qml/jsruntime/qv4objectproto.cpp
@@ -422,7 +422,7 @@ ReturnedValue ObjectPrototype::method_seal(const FunctionObject *b, const Value
Scope scope(b);
ScopedObject o(scope, a);
- o->setInternalClass(o->internalClass()->sealed());
+ o->setInternalClass(o->internalClass()->canned());
if (o->arrayData()) {
ArrayData::ensureAttributes(o);
@@ -448,7 +448,7 @@ ReturnedValue ObjectPrototype::method_freeze(const FunctionObject *b, const Valu
if (ArgumentsObject::isNonStrictArgumentsObject(o))
static_cast<ArgumentsObject *>(o.getPointer())->fullyCreate();
- o->setInternalClass(o->internalClass()->frozen());
+ o->setInternalClass(o->internalClass()->cryopreserved());
if (o->arrayData()) {
ArrayData::ensureAttributes(o);
@@ -489,7 +489,7 @@ ReturnedValue ObjectPrototype::method_isSealed(const FunctionObject *b, const Va
if (o->isExtensible())
return Encode(false);
- if (o->internalClass() != o->internalClass()->sealed())
+ if (o->internalClass() != o->internalClass()->canned())
return Encode(false);
if (!o->arrayData() || !o->arrayData()->length())
@@ -521,7 +521,7 @@ ReturnedValue ObjectPrototype::method_isFrozen(const FunctionObject *b, const Va
if (o->isExtensible())
return Encode(false);
- if (o->internalClass() != o->internalClass()->frozen())
+ if (!o->internalClass()->isImplicitlyFrozen())
return Encode(false);
if (!o->arrayData() || !o->arrayData()->length())
diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h
index e9515b5b68..8707305dc2 100644
--- a/src/qml/jsruntime/qv4objectproto_p.h
+++ b/src/qml/jsruntime/qv4objectproto_p.h
@@ -74,7 +74,7 @@ struct ObjectCtor: FunctionObject
static ReturnedValue virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc);
};
-struct ObjectPrototype: Object
+struct Q_QML_PRIVATE_EXPORT ObjectPrototype: Object
{
void init(ExecutionEngine *engine, Object *ctor);
diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp
index b337243204..26e1074fe3 100644
--- a/src/qml/jsruntime/qv4profiling.cpp
+++ b/src/qml/jsruntime/qv4profiling.cpp
@@ -49,7 +49,7 @@ namespace Profiling {
FunctionLocation FunctionCall::resolveLocation() const
{
return FunctionLocation(m_function->name()->toQString(),
- m_function->compilationUnit->fileName(),
+ m_function->executableCompilationUnit()->fileName(),
m_function->compiledFunction->location.line,
m_function->compiledFunction->location.column);
}
diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h
index 8461384e9a..ccf7c9210d 100644
--- a/src/qml/jsruntime/qv4profiling_p.h
+++ b/src/qml/jsruntime/qv4profiling_p.h
@@ -144,19 +144,19 @@ public:
FunctionCall(Function *function, qint64 start, qint64 end) :
m_function(function), m_start(start), m_end(end)
- { m_function->compilationUnit->addref(); }
+ { m_function->executableCompilationUnit()->addref(); }
FunctionCall(const FunctionCall &other) :
m_function(other.m_function), m_start(other.m_start), m_end(other.m_end)
- { m_function->compilationUnit->addref(); }
+ { m_function->executableCompilationUnit()->addref(); }
~FunctionCall()
- { m_function->compilationUnit->release(); }
+ { m_function->executableCompilationUnit()->release(); }
FunctionCall &operator=(const FunctionCall &other) {
if (&other != this) {
- other.m_function->compilationUnit->addref();
- m_function->compilationUnit->release();
+ other.m_function->executableCompilationUnit()->addref();
+ m_function->executableCompilationUnit()->release();
m_function = other.m_function;
m_start = other.m_start;
m_end = other.m_end;
@@ -189,22 +189,22 @@ public:
SentMarker(const SentMarker &other) : m_function(other.m_function)
{
if (m_function)
- m_function->compilationUnit->addref();
+ m_function->executableCompilationUnit()->addref();
}
~SentMarker()
{
if (m_function)
- m_function->compilationUnit->release();
+ m_function->executableCompilationUnit()->release();
}
SentMarker &operator=(const SentMarker &other)
{
if (&other != this) {
if (m_function)
- m_function->compilationUnit->release();
+ m_function->executableCompilationUnit()->release();
m_function = other.m_function;
- m_function->compilationUnit->addref();
+ m_function->executableCompilationUnit()->addref();
}
return *this;
}
@@ -213,7 +213,7 @@ public:
{
Q_ASSERT(m_function == nullptr);
m_function = function;
- m_function->compilationUnit->addref();
+ m_function->executableCompilationUnit()->addref();
}
bool isValid() const
diff --git a/src/qml/jsruntime/qv4promiseobject.cpp b/src/qml/jsruntime/qv4promiseobject.cpp
index 851fee7bd8..17d218a6eb 100644
--- a/src/qml/jsruntime/qv4promiseobject.cpp
+++ b/src/qml/jsruntime/qv4promiseobject.cpp
@@ -558,7 +558,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this
ScopedFunctionObject reject(scope, capability->d()->reject);
ScopedObject itemsObject(scope, argv);
- ScopedObject iteratorObject(scope, Runtime::method_getIterator(e, itemsObject, true));
+ ScopedObject iteratorObject(scope, Runtime::GetIterator::call(e, itemsObject, true));
if (!iteratorObject || scope.hasException()) {
ScopedObject error(scope);
if (scope.hasException()) {
@@ -583,7 +583,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this
for (;;) {
Scope scope(e);
ScopedValue nextValue(scope);
- doneValue = Value::fromReturnedValue(Runtime::method_iteratorNext(e, iteratorObject, nextValue));
+ doneValue = Value::fromReturnedValue(Runtime::IteratorNext::call(e, iteratorObject, nextValue));
if (doneValue->toBoolean())
break;
@@ -611,7 +611,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this
}
if (!doneValue->toBoolean())
- completion = Runtime::method_iteratorClose(e, iteratorObject, doneValue);
+ completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue);
reject->call(newPromise, completion, 1);
return newPromise.asReturnedValue();
@@ -619,7 +619,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this
ScopedObject nextPromise(scope, Value::fromReturnedValue(resolve->call(thisObject, nextValue, 1)));
if (!nextPromise || scope.hasException()) {
- ScopedValue completion(scope, Runtime::method_iteratorClose(e, iteratorObject, doneValue));
+ ScopedValue completion(scope, Runtime::IteratorClose::call(e, iteratorObject, doneValue));
if (scope.hasException()) {
completion = e->exceptionValue->asReturnedValue();
dropException(e);
@@ -641,7 +641,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this
}
if (!doneValue->toBoolean())
- completion = Runtime::method_iteratorClose(scope.engine, iteratorObject, doneValue);
+ completion = Runtime::IteratorClose::call(scope.engine, iteratorObject, doneValue);
reject->call(newPromise, completion, 1);
return newPromise.asReturnedValue();
@@ -660,7 +660,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this
dropException(e);
if (!doneValue->toBoolean())
- completion = Runtime::method_iteratorClose(scope.engine, iteratorObject, doneValue);
+ completion = Runtime::IteratorClose::call(scope.engine, iteratorObject, doneValue);
reject->call(newPromise, completion, 1);
return newPromise.asReturnedValue();
@@ -708,7 +708,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi
ScopedFunctionObject reject(scope, capability->d()->reject);
ScopedObject itemsObject(scope, argv);
- ScopedObject iteratorObject(scope, Runtime::method_getIterator(e, itemsObject, true));
+ ScopedObject iteratorObject(scope, Runtime::GetIterator::call(e, itemsObject, true));
if (!iteratorObject) {
ScopedObject error(scope, e->newTypeErrorObject(QStringLiteral("Type error")));
reject->call(newPromise, error, 1);
@@ -719,10 +719,10 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi
for (;;) {
Scope scope(e);
ScopedValue nextValue(scope);
- doneValue = Value::fromReturnedValue(Runtime::method_iteratorNext(e, iteratorObject, nextValue));
+ doneValue = Value::fromReturnedValue(Runtime::IteratorNext::call(e, iteratorObject, nextValue));
if (scope.hasException()) {
- ScopedValue completion(scope, Runtime::method_iteratorClose(e, iteratorObject, doneValue));
+ ScopedValue completion(scope, Runtime::IteratorClose::call(e, iteratorObject, doneValue));
if (scope.hasException()) {
completion = e->exceptionValue->asReturnedValue();
dropException(e);
@@ -757,7 +757,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi
}
if (!doneValue->toBoolean())
- completion = Runtime::method_iteratorClose(e, iteratorObject, doneValue);
+ completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue);
reject->call(newPromise, completion, 1);
return newPromise.asReturnedValue();
@@ -765,7 +765,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi
ScopedObject nextPromise(scope, Value::fromReturnedValue(resolve->call(thisObject, nextValue, 1)));
if (!nextPromise || scope.hasException()) {
- ScopedValue completion(scope, Runtime::method_iteratorClose(e, iteratorObject, doneValue));
+ ScopedValue completion(scope, Runtime::IteratorClose::call(e, iteratorObject, doneValue));
if (scope.hasException()) {
completion = e->exceptionValue->asReturnedValue();
dropException(e);
@@ -785,7 +785,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi
}
if (!doneValue->toBoolean())
- completion = Runtime::method_iteratorClose(e, iteratorObject, doneValue);
+ completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue);
reject->call(newPromise, completion, 1);
return newPromise.asReturnedValue();
@@ -804,7 +804,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi
dropException(e);
if (!doneValue->toBoolean())
- completion = Runtime::method_iteratorClose(e, iteratorObject, doneValue);
+ completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue);
reject->call(newPromise, completion, 1);
return newPromise.asReturnedValue();
diff --git a/src/qml/jsruntime/qv4propertykey_p.h b/src/qml/jsruntime/qv4propertykey_p.h
index 47867765db..b2a2ec3dea 100644
--- a/src/qml/jsruntime/qv4propertykey_p.h
+++ b/src/qml/jsruntime/qv4propertykey_p.h
@@ -113,8 +113,8 @@ public:
static PropertyKey invalid() { PropertyKey key; key.val = 0; return key; }
static PropertyKey fromArrayIndex(uint idx) { PropertyKey key; key.val = ArrayIndexMask | static_cast<quint64>(idx); return key; }
bool isStringOrSymbol() const { return isManaged() && val != 0; }
- uint asArrayIndex() const { return (isManaged() || val == 0) ? std::numeric_limits<uint>::max() : static_cast<uint>(val & 0xffffffff); }
- uint isArrayIndex() const { return !isManaged() && val != 0 && static_cast<uint>(val & 0xffffffff) != std::numeric_limits<uint>::max(); }
+ uint asArrayIndex() const { Q_ASSERT(isArrayIndex()); return static_cast<uint>(val & 0xffffffff); }
+ uint isArrayIndex() const { return !isManaged() && val != 0; }
bool isValid() const { return val != 0; }
static PropertyKey fromStringOrSymbol(Heap::StringOrSymbol *b)
{ PropertyKey key; key.setM(b); return key; }
@@ -124,7 +124,7 @@ public:
return m();
}
- bool isString() const;
+ Q_QML_EXPORT bool isString() const;
bool isSymbol() const;
bool isCanonicalNumericIndexString() const;
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp
index 969d44ecf6..e2d3b98ff6 100644
--- a/src/qml/jsruntime/qv4qmlcontext.cpp
+++ b/src/qml/jsruntime/qv4qmlcontext.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qv4qmlcontext_p.h"
-#include <private/qv8engine_p.h>
#include <private/qqmlengine_p.h>
#include <private/qqmlcontext_p.h>
@@ -232,28 +231,32 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r
} else if (r.type.isValid()) {
if (lookup) {
if (r.type.isSingleton()) {
- QQmlEngine *e = v4->qmlEngine();
- QQmlType::SingletonInstanceInfo *siinfo = r.type.singletonInstanceInfo();
- siinfo->init(e);
- if (siinfo->qobjectApi(e)) {
+ QQmlEnginePrivate *e = QQmlEnginePrivate::get(v4->qmlEngine());
+ if (r.type.isQObjectSingleton() || r.type.isCompositeSingleton()) {
+ e->singletonInstance<QObject*>(r.type);
lookup->qmlContextSingletonLookup.singleton =
static_cast<Heap::Object*>(
Value::fromReturnedValue(
QQmlTypeWrapper::create(v4, nullptr, r.type)
).heapObject());
} else {
- QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e)));
+ QJSValue singleton = e->singletonInstance<QJSValue>(r.type);
+ QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, singleton));
lookup->qmlContextSingletonLookup.singleton = o->d();
}
lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupSingleton;
return lookup->qmlContextPropertyGetter(lookup, v4, base);
}
}
- return QQmlTypeWrapper::create(v4, scopeObject, r.type);
+ result = QQmlTypeWrapper::create(v4, scopeObject, r.type);
} else if (r.importNamespace) {
- return QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace);
+ result = QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace);
}
- Q_ASSERT(!"Unreachable");
+ if (lookup) {
+ lookup->qmlTypeLookup.qmlTypeWrapper = static_cast<Heap::Object*>(result->heapObject());
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupType;
+ }
+ return result->asReturnedValue();
}
// Fall through
@@ -660,6 +663,27 @@ ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, Exec
return Encode::undefined();
}
+ReturnedValue QQmlContextWrapper::lookupType(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Scope scope(engine);
+ Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
+ if (!qmlContext)
+ return QV4::Encode::undefined();
+
+ QObject *scopeObject = qmlContext->qmlScope();
+ if (scopeObject && QQmlData::wasDeleted(scopeObject))
+ return QV4::Encode::undefined();
+
+ Heap::Object *heapObject = l->qmlTypeLookup.qmlTypeWrapper;
+ if (static_cast<Heap::QQmlTypeWrapper *>(heapObject)->object != scopeObject) {
+ l->qmlTypeLookup.qmlTypeWrapper = nullptr;
+ l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
+ return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base);
+ }
+
+ return Value::fromHeapObject(heapObject).asReturnedValue();
+}
+
void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml)
{
Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext);
diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h
index 4c8287ef2f..e3e7239fe5 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 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);
+ static ReturnedValue lookupType(Lookup *l, ExecutionEngine *engine, Value *base);
};
struct Q_QML_EXPORT QmlContext : public ExecutionContext
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 63b6435112..9d0b65cc0a 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -39,7 +39,7 @@
#include "qv4qobjectwrapper_p.h"
-#include <private/qqmlpropertycache_p.h>
+#include <private/qqmlstaticmetaobject_p.h>
#include <private/qqmlengine_p.h>
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlbinding_p.h>
@@ -50,7 +50,6 @@
#include <private/qqmlvaluetypewrapper_p.h>
#include <private/qqmllistwrapper_p.h>
#include <private/qqmlbuiltinfunctions_p.h>
-#include <private/qv8engine_p.h>
#include <private/qv4arraybuffer_p.h>
#include <private/qv4functionobject_p.h>
@@ -58,6 +57,7 @@
#include <private/qv4variantobject_p.h>
#include <private/qv4identifiertable_p.h>
#include <private/qv4lookup_p.h>
+#include <private/qv4qmlcontext_p.h>
#if QT_CONFIG(qml_sequence_object)
#include <private/qv4sequenceobject_p.h>
@@ -81,7 +81,9 @@
#include <QtCore/qtimer.h>
#include <QtCore/qatomic.h>
#include <QtCore/qmetaobject.h>
+#if QT_CONFIG(qml_itemmodel)
#include <QtCore/qabstractitemmodel.h>
+#endif
#include <QtCore/qloggingcategory.h>
#include <vector>
@@ -165,10 +167,6 @@ static QV4::ReturnedValue loadProperty(QV4::ExecutionEngine *v4, QObject *object
double v = 0;
property.readProperty(object, &v);
return QV4::Encode(v);
- } else if (property.isV4Handle()) {
- QQmlV4Handle handle;
- property.readProperty(object, &handle);
- return handle;
} else if (property.propType() == qMetaTypeId<QJSValue>()) {
QJSValue v;
property.readProperty(object, &v);
@@ -768,6 +766,8 @@ struct QObjectWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
~QObjectWrapperOwnPropertyKeyIterator() override = default;
PropertyKey next(const QV4::Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
+private:
+ QSet<QByteArray> m_alreadySeen;
};
PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Property *pd, PropertyAttributes *attrs)
@@ -808,6 +808,11 @@ PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Pro
++propertyIndex;
if (method.access() == QMetaMethod::Private || (preventDestruction && (index == deleteLaterIdx || index == destroyedIdx1 || index == destroyedIdx2)))
continue;
+ // filter out duplicates due to overloads:
+ if (m_alreadySeen.contains(method.name()))
+ continue;
+ else
+ m_alreadySeen.insert(method.name());
ExecutionEngine *thatEngine = that->engine();
Scope scope(thatEngine);
ScopedString methodName(scope, thatEngine->newString(QString::fromUtf8(method.name())));
@@ -1193,15 +1198,14 @@ DEFINE_OBJECT_VTABLE(QObjectWrapper);
namespace {
-template<typename A, typename B, typename C, typename D, typename E,
- typename F, typename G, typename H>
-class MaxSizeOf8 {
+template<typename A, typename B, typename C, typename D, typename E, typename F, typename G>
+class MaxSizeOf7 {
template<typename Z, typename X>
struct SMax {
char dummy[sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X)];
};
public:
- static const size_t Size = sizeof(SMax<A, SMax<B, SMax<C, SMax<D, SMax<E, SMax<F, SMax<G, H> > > > > > >);
+ static const size_t Size = sizeof(SMax<A, SMax<B, SMax<C, SMax<D, SMax<E, SMax<F, G> > > > > >);
};
struct CallArgument {
@@ -1232,16 +1236,17 @@ private:
std::vector<bool> *stdVectorBoolPtr;
std::vector<QString> *stdVectorQStringPtr;
std::vector<QUrl> *stdVectorQUrlPtr;
+#if QT_CONFIG(qml_itemmodel)
std::vector<QModelIndex> *stdVectorQModelIndexPtr;
+#endif
- char allocData[MaxSizeOf8<QVariant,
- QString,
- QList<QObject *>,
- QJSValue,
- QQmlV4Handle,
- QJsonArray,
- QJsonObject,
- QJsonValue>::Size];
+ char allocData[MaxSizeOf7<QVariant,
+ QString,
+ QList<QObject *>,
+ QJSValue,
+ QJsonArray,
+ QJsonObject,
+ QJsonValue>::Size];
qint64 q_for_alignment;
};
@@ -1252,7 +1257,6 @@ private:
QVariant *qvariantPtr;
QList<QObject *> *qlistPtr;
QJSValue *qjsValuePtr;
- QQmlV4Handle *handlePtr;
QJsonArray *jsonArrayPtr;
QJsonObject *jsonObjectPtr;
QJsonValue *jsonValuePtr;
@@ -1271,7 +1275,8 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index
QVarLengthArray<CallArgument, 9> args(argCount + 1);
args[0].initAsType(returnType);
for (int ii = 0; ii < argCount; ++ii) {
- if (!args[ii + 1].fromValue(argTypes[ii], engine, callArgs->args[ii])) {
+ if (!args[ii + 1].fromValue(argTypes[ii], engine,
+ callArgs->args[ii].asValue<QV4::Value>())) {
qWarning() << QString::fromLatin1("Could not convert argument %1 at").arg(ii);
const StackTrace stack = engine->stackTrace();
for (const StackFrame &frame : stack) {
@@ -1383,6 +1388,9 @@ static int MatchScore(const QV4::Value &actual, int conversionType)
} else if (actual.as<QV4::RegExpObject>()) {
switch (conversionType) {
case QMetaType::QRegExp:
+#if QT_CONFIG(regularexpression)
+ case QMetaType::QRegularExpression:
+#endif
return 0;
default:
return 10;
@@ -1613,8 +1621,10 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const
continue; // We already have a better option
int methodMatchScore = 0;
- for (int ii = 0; ii < methodArgumentCount; ++ii)
- methodMatchScore += MatchScore((v = callArgs->args[ii]), methodArgTypes[ii]);
+ for (int ii = 0; ii < methodArgumentCount; ++ii) {
+ methodMatchScore += MatchScore((v = QV4::Value::fromStaticValue(callArgs->args[ii])),
+ methodArgTypes[ii]);
+ }
if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
best = *attempt;
@@ -1688,8 +1698,10 @@ void *CallArgument::dataPtr()
return stdVectorQStringPtr;
else if (type == qMetaTypeId<std::vector<QUrl>>())
return stdVectorQUrlPtr;
+#if QT_CONFIG(qml_itemmodel)
else if (type == qMetaTypeId<std::vector<QModelIndex>>())
return stdVectorQModelIndexPtr;
+#endif
else if (type != 0)
return (void *)&allocData;
return nullptr;
@@ -1721,9 +1733,6 @@ void CallArgument::initAsType(int callType)
} else if (callType == qMetaTypeId<QList<QObject *> >()) {
type = callType;
qlistPtr = new (&allocData) QList<QObject *>();
- } else if (callType == qMetaTypeId<QQmlV4Handle>()) {
- type = callType;
- handlePtr = new (&allocData) QQmlV4Handle;
} else if (callType == QMetaType::QJsonArray) {
type = callType;
jsonArrayPtr = new (&allocData) QJsonArray();
@@ -1824,9 +1833,6 @@ bool CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q
return false;
}
}
- } else if (callType == qMetaTypeId<QQmlV4Handle>()) {
- handlePtr = new (&allocData) QQmlV4Handle(value.asReturnedValue());
- type = callType;
} else if (callType == QMetaType::QJsonArray) {
QV4::ScopedArrayObject a(scope, value);
jsonArrayPtr = new (&allocData) QJsonArray(QV4::JsonObject::toJsonArray(a));
@@ -1846,7 +1852,10 @@ bool CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q
|| callType == qMetaTypeId<std::vector<bool>>()
|| callType == qMetaTypeId<std::vector<QString>>()
|| callType == qMetaTypeId<std::vector<QUrl>>()
- || callType == qMetaTypeId<std::vector<QModelIndex>>()) {
+#if QT_CONFIG(qml_itemmodel)
+ || callType == qMetaTypeId<std::vector<QModelIndex>>()
+#endif
+ ) {
queryEngine = true;
const QV4::Object* object = value.as<QV4::Object>();
if (callType == qMetaTypeId<std::vector<int>>()) {
@@ -1864,9 +1873,11 @@ bool CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q
} else if (callType == qMetaTypeId<std::vector<QUrl>>()) {
stdVectorQUrlPtr = nullptr;
fromContainerValue<std::vector<QUrl>>(object, callType, &CallArgument::stdVectorQUrlPtr, queryEngine);
+#if QT_CONFIG(qml_itemmodel)
} else if (callType == qMetaTypeId<std::vector<QModelIndex>>()) {
stdVectorQModelIndexPtr = nullptr;
fromContainerValue<std::vector<QModelIndex>>(object, callType, &CallArgument::stdVectorQModelIndexPtr, queryEngine);
+#endif
}
#endif
} else if (QMetaType::typeFlags(callType)
@@ -1951,8 +1962,6 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine)
array->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(scope.engine, list.at(ii))));
array->setArrayLengthUnchecked(list.count());
return array.asReturnedValue();
- } else if (type == qMetaTypeId<QQmlV4Handle>()) {
- return *handlePtr;
} else if (type == QMetaType::QJsonArray) {
return QV4::JsonObject::fromJsonArray(scope.engine, *jsonArrayPtr);
} else if (type == QMetaType::QJsonObject) {
@@ -2258,8 +2267,10 @@ ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine
continue; // We already have a better option
int methodMatchScore = 0;
- for (int ii = 0; ii < methodArgumentCount; ++ii)
- methodMatchScore += MatchScore((v = callArgs->args[ii]), methodArgTypes[ii]);
+ for (int ii = 0; ii < methodArgumentCount; ++ii) {
+ methodMatchScore += MatchScore((v = QV4::Value::fromStaticValue(callArgs->args[ii])),
+ methodArgTypes[ii]);
+ }
if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
best = attempt;
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index 5543c4d5a6..ac9cad2bdb 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -55,9 +55,7 @@
#include <QtCore/qmetatype.h>
#include <QtCore/qpair.h>
#include <QtCore/qhash.h>
-#include <private/qhashedstring_p.h>
#include <private/qqmldata_p.h>
-#include <private/qqmlpropertycache_p.h>
#include <private/qintrusivelist_p.h>
#include <private/qv4value_p.h>
diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp
index 15dcb602eb..0772770d63 100644
--- a/src/qml/jsruntime/qv4reflect.cpp
+++ b/src/qml/jsruntime/qv4reflect.cpp
@@ -148,7 +148,7 @@ ReturnedValue Reflect::method_deleteProperty(const FunctionObject *f, const Valu
if (!argc || !argv[0].isObject())
return e->throwTypeError();
- bool result = Runtime::method_deleteProperty(e, argv[0], argc > 1 ? argv[1] : Value::undefinedValue());
+ bool result = Runtime::DeleteProperty_NoThrow::call(e, argv[0], argc > 1 ? argv[1] : Value::undefinedValue());
return Encode(result);
}
diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp
index 39a2e96b45..c1a42c4afa 100644
--- a/src/qml/jsruntime/qv4regexpobject.cpp
+++ b/src/qml/jsruntime/qv4regexpobject.cpp
@@ -50,10 +50,13 @@
#include <QtCore/QDebug>
#include <QtCore/qregexp.h>
+#if QT_CONFIG(regularexpression)
+#include <QtCore/qregularexpression.h>
+#endif
#include <cassert>
#include <typeinfo>
#include <iostream>
-#include "qv4alloca_p.h"
+#include <private/qv4alloca_p.h>
QT_BEGIN_NAMESPACE
@@ -134,6 +137,25 @@ void Heap::RegExpObject::init(const QRegExp &re)
o->initProperties();
}
+#if QT_CONFIG(regularexpression)
+// Converts a QRegularExpression to a JS RegExp.
+// The conversion is not 100% exact since ECMA regexp and QRegularExpression
+// have different semantics/flags, but we try to do our best.
+void Heap::RegExpObject::init(const QRegularExpression &re)
+{
+ Object::init();
+
+ Scope scope(internalClass->engine);
+ Scoped<QV4::RegExpObject> o(scope, this);
+
+ const uint flags = (re.patternOptions() & QRegularExpression::CaseInsensitiveOption)
+ ? CompiledData::RegExp::RegExp_IgnoreCase
+ : CompiledData::RegExp::RegExp_NoFlags;
+ o->d()->value.set(scope.engine, QV4::RegExp::create(scope.engine, re.pattern(), flags));
+ o->initProperties();
+}
+#endif
+
void RegExpObject::initProperties()
{
setProperty(Index_LastIndex, Value::fromInt32(0));
@@ -150,6 +172,20 @@ QRegExp RegExpObject::toQRegExp() const
return QRegExp(*value()->pattern, caseSensitivity, QRegExp::RegExp2);
}
+#if QT_CONFIG(regularexpression)
+// Converts a JS RegExp to a QRegularExpression.
+// The conversion is not 100% exact since ECMA regexp and QRegularExpression
+// have different semantics/flags, but we try to do our best.
+QRegularExpression RegExpObject::toQRegularExpression() const
+{
+ QRegularExpression::PatternOptions caseSensitivity
+ = (value()->flags & CompiledData::RegExp::RegExp_IgnoreCase)
+ ? QRegularExpression::CaseInsensitiveOption
+ : QRegularExpression::NoPatternOption;
+ return QRegularExpression(*value()->pattern, caseSensitivity);
+}
+#endif
+
QString RegExpObject::toString() const
{
QString p = *value()->pattern;
@@ -162,13 +198,6 @@ QString RegExpObject::toString() const
return p;
}
-QString RegExpObject::source() const
-{
- Scope scope(engine());
- ScopedValue s(scope, get(scope.engine->id_source()));
- return s->toQString();
-}
-
ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine, const String *str)
{
QString s = str->toQString();
diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h
index a584404c0b..b94889e9f0 100644
--- a/src/qml/jsruntime/qv4regexpobject_p.h
+++ b/src/qml/jsruntime/qv4regexpobject_p.h
@@ -81,6 +81,9 @@ DECLARE_HEAP_OBJECT(RegExpObject, Object) {
void init();
void init(QV4::RegExp *value);
void init(const QRegExp &re);
+#if QT_CONFIG(regularexpression)
+ void init(const QRegularExpression &re);
+#endif
};
#define RegExpCtorMembers(class, Member) \
@@ -98,7 +101,7 @@ DECLARE_HEAP_OBJECT(RegExpCtor, FunctionObject) {
}
-struct RegExpObject: Object {
+struct Q_QML_PRIVATE_EXPORT RegExpObject: Object {
V4_OBJECT2(RegExpObject, Object)
Q_MANAGED_TYPE(RegExpObject)
V4_INTERNALCLASS(RegExpObject)
@@ -138,8 +141,16 @@ struct RegExpObject: Object {
}
QRegExp toQRegExp() const;
+#if QT_CONFIG(regularexpression)
+ QRegularExpression toQRegularExpression() const;
+#endif
QString toString() const;
- QString source() const;
+ QString source() const
+ {
+ Scope scope(engine());
+ ScopedValue s(scope, get(scope.engine->id_source()));
+ return s->toQString();
+ }
Heap::RegExp *value() const { return d()->value; }
uint flags() const { return d()->value->flags; }
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index f7c339dc26..aaa198c62a 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -38,9 +38,8 @@
****************************************************************************/
#include "qv4global_p.h"
-#include "qv4engine_p.h"
#include "qv4runtime_p.h"
-#ifndef V4_BOOTSTRAP
+#include "qv4engine_p.h"
#include "qv4object_p.h"
#include "qv4objectproto_p.h"
#include "qv4globalobject_p.h"
@@ -60,11 +59,10 @@
#include <private/qqmltypewrapper_p.h>
#include <private/qqmlengine_p.h>
#include <private/qqmljavascriptexpression_p.h>
+#include <private/qqmljsast_p.h>
#include "qv4qobjectwrapper_p.h"
#include "qv4symbol_p.h"
#include "qv4generatorobject_p.h"
-#include <private/qv8engine_p.h>
-#endif
#include <QtCore/QDebug>
#include <cassert>
@@ -223,13 +221,9 @@ void RuntimeCounters::count(const char *func, uint tag1, uint tag2)
#endif // QV4_COUNT_RUNTIME_FUNCTIONS
-#ifndef V4_BOOTSTRAP
-
-Runtime::Runtime()
+static QV4::Lookup *runtimeLookup(Function *f, uint i)
{
-#define INIT_METHOD(returnvalue, name, args) runtimeMethods[name] = reinterpret_cast<void*>(&method_##name);
-FOR_EACH_RUNTIME_METHOD(INIT_METHOD)
-#undef INIT_METHOD
+ return f->executableCompilationUnit()->runtimeLookups + i;
}
void RuntimeHelpers::numberToString(QString *result, double num, int radix)
@@ -320,9 +314,10 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix)
result->prepend(QLatin1Char('-'));
}
-ReturnedValue Runtime::method_closure(ExecutionEngine *engine, int functionId)
+ReturnedValue Runtime::Closure::call(ExecutionEngine *engine, int functionId)
{
- QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeFunctions[functionId];
+ QV4::Function *clos = engine->currentStackFrame->v4Function->executableCompilationUnit()
+ ->runtimeFunctions[functionId];
Q_ASSERT(clos);
ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context);
if (clos->isGenerator())
@@ -330,7 +325,7 @@ ReturnedValue Runtime::method_closure(ExecutionEngine *engine, int functionId)
return FunctionObject::createScriptFunction(current, clos)->asReturnedValue();
}
-bool Runtime::method_deleteProperty(ExecutionEngine *engine, const Value &base, const Value &index)
+Bool Runtime::DeleteProperty_NoThrow::call(ExecutionEngine *engine, const Value &base, const Value &index)
{
Scope scope(engine);
ScopedObject o(scope, base.toObject(engine));
@@ -344,14 +339,36 @@ bool Runtime::method_deleteProperty(ExecutionEngine *engine, const Value &base,
return o->deleteProperty(key);
}
-bool Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex)
+ReturnedValue Runtime::DeleteProperty::call(ExecutionEngine *engine, QV4::Function *function, const QV4::Value &base, const QV4::Value &index)
+{
+ if (!Runtime::DeleteProperty_NoThrow::call(engine, base, index)) {
+ if (function->isStrict())
+ engine->throwTypeError();
+ return Encode(false);
+ } else {
+ return Encode(true);
+ }
+}
+
+Bool Runtime::DeleteName_NoThrow::call(ExecutionEngine *engine, int nameIndex)
{
Scope scope(engine);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).deleteProperty(name);
}
-QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Value &lval, const Value &rval)
+ReturnedValue Runtime::DeleteName::call(ExecutionEngine *engine, Function *function, int name)
+{
+ if (!Runtime::DeleteName_NoThrow::call(engine, name)) {
+ if (function->isStrict())
+ engine->throwTypeError();
+ return Encode(false);
+ } else {
+ return Encode(true);
+ }
+}
+
+QV4::ReturnedValue Runtime::Instanceof::call(ExecutionEngine *engine, const Value &lval, const Value &rval)
{
// 11.8.6, 5: rval must be an Object
const Object *rhs = rval.as<Object>();
@@ -376,7 +393,7 @@ QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Val
return Encode(result->toBoolean());
}
-QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left, const Value &right)
+QV4::ReturnedValue Runtime::In::call(ExecutionEngine *engine, const Value &left, const Value &right)
{
Object *ro = right.objectValue();
if (!ro)
@@ -604,13 +621,12 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu
return Encode(x + y);
}
-ReturnedValue RuntimeHelpers::getTemplateObject(Function *function, int index)
+ReturnedValue Runtime::GetTemplateObject::call(Function *function, int index)
{
- return function->compilationUnit->templateObjectAt(index)->asReturnedValue();
+ return function->executableCompilationUnit()->templateObjectAt(index)->asReturnedValue();
}
-
-void Runtime::method_storeProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)
+void Runtime::StoreProperty::call(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)
{
Scope scope(engine);
QV4::Function *v4Function = engine->currentStackFrame->v4Function;
@@ -683,7 +699,7 @@ static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine,
return o->get(name);
}
-ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value &object, const Value &index)
+ReturnedValue Runtime::LoadElement::call(ExecutionEngine *engine, const Value &object, const Value &index)
{
if (index.isPositiveInt()) {
uint idx = static_cast<uint>(index.int_32());
@@ -704,30 +720,6 @@ ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value &
return getElementFallback(engine, object, index);
}
-ReturnedValue Runtime::method_loadElement_traced(ExecutionEngine *engine, const Value &object, const Value &index, quint8 *traceSlot)
-{
- *traceSlot |= quint8(ObservedTraceValues::ArrayWasAccessed);
- if (index.isPositiveInt()) {
- uint idx = static_cast<uint>(index.int_32());
- if (Heap::Base *b = object.heapObject()) {
- if (b->internalClass->vtable->isObject) {
- Heap::Object *o = static_cast<Heap::Object *>(b);
- if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
- Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>();
- if (idx < s->values.size)
- if (!s->data(idx).isEmpty())
- return s->data(idx).asReturnedValue();
- }
- }
- }
- *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback);
- return getElementIntFallback(engine, object, idx);
- }
-
- *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback);
- return getElementFallback(engine, object, index);
-}
-
static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
{
Scope scope(engine);
@@ -761,7 +753,7 @@ static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Val
return o->put(name, value);
}
-void Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
+void Runtime::StoreElement::call(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
{
if (index.isPositiveInt()) {
uint idx = static_cast<uint>(index.int_32());
@@ -783,31 +775,7 @@ void Runtime::method_storeElement(ExecutionEngine *engine, const Value &object,
engine->throwTypeError();
}
-void Runtime::method_storeElement_traced(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value, quint8 *traceSlot)
-{
- *traceSlot |= quint8(ObservedTraceValues::ArrayWasAccessed);
- if (index.isPositiveInt()) {
- uint idx = static_cast<uint>(index.int_32());
- if (Heap::Base *b = object.heapObject()) {
- if (b->internalClass->vtable->isObject) {
- Heap::Object *o = static_cast<Heap::Object *>(b);
- if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
- Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>();
- if (idx < s->values.size) {
- s->setData(engine, idx, value);
- return;
- }
- }
- }
- }
- }
-
- *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback);
- if (!setElementFallback(engine, object, index, value) && engine->currentStackFrame->v4Function->isStrict())
- engine->throwTypeError();
-}
-
-ReturnedValue Runtime::method_getIterator(ExecutionEngine *engine, const Value &in, int iterator)
+ReturnedValue Runtime::GetIterator::call(ExecutionEngine *engine, const Value &in, int iterator)
{
Scope scope(engine);
ScopedObject o(scope, (Object *)nullptr);
@@ -830,7 +798,7 @@ ReturnedValue Runtime::method_getIterator(ExecutionEngine *engine, const Value &
return engine->newForInIteratorObject(o)->asReturnedValue();
}
-ReturnedValue Runtime::method_iteratorNext(ExecutionEngine *engine, const Value &iterator, Value *value)
+ReturnedValue Runtime::IteratorNext::call(ExecutionEngine *engine, const Value &iterator, Value *value)
{
// if we throw an exception from here, return true, not undefined. This is to ensure iteratorDone is set to true
// and the stack unwinding won't close the iterator
@@ -866,7 +834,7 @@ ReturnedValue Runtime::method_iteratorNext(ExecutionEngine *engine, const Value
return Encode(false);
}
-ReturnedValue Runtime::method_iteratorNextForYieldStar(ExecutionEngine *engine, const Value &received, const Value &iterator, Value *object)
+ReturnedValue Runtime::IteratorNextForYieldStar::call(ExecutionEngine *engine, const Value &received, const Value &iterator, Value *object)
{
// the return value encodes how to continue the yield* iteration.
// true implies iteration is done, false for iteration to continue
@@ -903,7 +871,7 @@ ReturnedValue Runtime::method_iteratorNextForYieldStar(ExecutionEngine *engine,
if (t->isUndefined()) {
// no throw method on the iterator
ScopedValue done(scope, Encode(false));
- method_iteratorClose(engine, iterator, done);
+ IteratorClose::call(engine, iterator, done);
if (engine->hasException)
return Encode::undefined();
return engine->throwTypeError();
@@ -938,7 +906,7 @@ ReturnedValue Runtime::method_iteratorNextForYieldStar(ExecutionEngine *engine,
return Encode(false);
}
-ReturnedValue Runtime::method_iteratorClose(ExecutionEngine *engine, const Value &iterator, const Value &done)
+ReturnedValue Runtime::IteratorClose::call(ExecutionEngine *engine, const Value &iterator, const Value &done)
{
Q_ASSERT(iterator.isObject());
Q_ASSERT(done.isBoolean());
@@ -978,7 +946,7 @@ ReturnedValue Runtime::method_iteratorClose(ExecutionEngine *engine, const Value
return originalCompletion();
}
-ReturnedValue Runtime::method_destructureRestElement(ExecutionEngine *engine, const Value &iterator)
+ReturnedValue Runtime::DestructureRestElement::call(ExecutionEngine *engine, const Value &iterator)
{
Q_ASSERT(iterator.isObject());
@@ -988,7 +956,7 @@ ReturnedValue Runtime::method_destructureRestElement(ExecutionEngine *engine, co
uint index = 0;
while (1) {
ScopedValue n(scope);
- ScopedValue done(scope, method_iteratorNext(engine, iterator, n));
+ ScopedValue done(scope, IteratorNext::call(engine, iterator, n));
if (engine->hasException)
return Encode::undefined();
Q_ASSERT(done->isBoolean());
@@ -1000,7 +968,7 @@ ReturnedValue Runtime::method_destructureRestElement(ExecutionEngine *engine, co
return array->asReturnedValue();
}
-void Runtime::method_storeNameSloppy(ExecutionEngine *engine, int nameIndex, const Value &value)
+void Runtime::StoreNameSloppy::call(ExecutionEngine *engine, int nameIndex, const Value &value)
{
Scope scope(engine);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
@@ -1010,7 +978,7 @@ void Runtime::method_storeNameSloppy(ExecutionEngine *engine, int nameIndex, con
engine->globalObject->put(name, value);
}
-void Runtime::method_storeNameStrict(ExecutionEngine *engine, int nameIndex, const Value &value)
+void Runtime::StoreNameStrict::call(ExecutionEngine *engine, int nameIndex, const Value &value)
{
Scope scope(engine);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
@@ -1021,7 +989,7 @@ void Runtime::method_storeNameStrict(ExecutionEngine *engine, int nameIndex, con
engine->throwReferenceError(name);
}
-ReturnedValue Runtime::method_loadProperty(ExecutionEngine *engine, const Value &object, int nameIndex)
+ReturnedValue Runtime::LoadProperty::call(ExecutionEngine *engine, const Value &object, int nameIndex)
{
Scope scope(engine);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
@@ -1041,7 +1009,7 @@ ReturnedValue Runtime::method_loadProperty(ExecutionEngine *engine, const Value
return o->get(name);
}
-ReturnedValue Runtime::method_loadName(ExecutionEngine *engine, int nameIndex)
+ReturnedValue Runtime::LoadName::call(ExecutionEngine *engine, int nameIndex)
{
Scope scope(engine);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
@@ -1055,7 +1023,8 @@ static Object *getSuperBase(Scope &scope)
return nullptr;
}
- ScopedFunctionObject f(scope, scope.engine->currentStackFrame->jsFrame->function);
+ ScopedFunctionObject f(
+ scope, Value::fromStaticValue(scope.engine->currentStackFrame->jsFrame->function));
ScopedObject homeObject(scope, f->getHomeObject());
if (!homeObject) {
ScopedContext ctx(scope, static_cast<ExecutionContext *>(&scope.engine->currentStackFrame->jsFrame->context));
@@ -1084,7 +1053,7 @@ static Object *getSuperBase(Scope &scope)
return proto;
}
-ReturnedValue Runtime::method_loadSuperProperty(ExecutionEngine *engine, const Value &property)
+ReturnedValue Runtime::LoadSuperProperty::call(ExecutionEngine *engine, const Value &property)
{
Scope scope(engine);
Object *base = getSuperBase(scope);
@@ -1093,10 +1062,11 @@ ReturnedValue Runtime::method_loadSuperProperty(ExecutionEngine *engine, const V
ScopedPropertyKey key(scope, property.toPropertyKey(engine));
if (engine->hasException)
return Encode::undefined();
- return base->get(key, &engine->currentStackFrame->jsFrame->thisObject);
+ return base->get(
+ key, &(engine->currentStackFrame->jsFrame->thisObject.asValue<Value>()));
}
-void Runtime::method_storeSuperProperty(ExecutionEngine *engine, const Value &property, const Value &value)
+void Runtime::StoreSuperProperty::call(ExecutionEngine *engine, const Value &property, const Value &value)
{
Scope scope(engine);
Object *base = getSuperBase(scope);
@@ -1105,12 +1075,46 @@ void Runtime::method_storeSuperProperty(ExecutionEngine *engine, const Value &pr
ScopedPropertyKey key(scope, property.toPropertyKey(engine));
if (engine->hasException)
return;
- bool result = base->put(key, value, &engine->currentStackFrame->jsFrame->thisObject);
+ bool result = base->put(
+ key, value, &(engine->currentStackFrame->jsFrame->thisObject.asValue<Value>()));
if (!result && engine->currentStackFrame->v4Function->isStrict())
engine->throwTypeError();
}
-ReturnedValue Runtime::method_loadSuperConstructor(ExecutionEngine *engine, const Value &t)
+ReturnedValue Runtime::LoadGlobalLookup::call(ExecutionEngine *engine, Function *f, int index)
+{
+ Lookup *l = runtimeLookup(f, index);
+ return l->globalGetter(l, engine);
+}
+
+ReturnedValue Runtime::LoadQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index)
+{
+ Lookup *l = runtimeLookup(engine->currentStackFrame->v4Function, index);
+ return l->qmlContextPropertyGetter(l, engine, nullptr);
+}
+
+ReturnedValue Runtime::GetLookup::call(ExecutionEngine *engine, Function *f, const Value &base, int index)
+{
+ Lookup *l = runtimeLookup(f, index);
+ return l->getter(l, engine, base);
+}
+
+void Runtime::SetLookupSloppy::call(Function *f, const Value &base, int index, const Value &value)
+{
+ ExecutionEngine *engine = f->internalClass->engine;
+ QV4::Lookup *l = runtimeLookup(f, index);
+ l->setter(l, engine, const_cast<Value &>(base), value);
+}
+
+void Runtime::SetLookupStrict::call(Function *f, const Value &base, int index, const Value &value)
+{
+ ExecutionEngine *engine = f->internalClass->engine;
+ QV4::Lookup *l = runtimeLookup(f, index);
+ if (!l->setter(l, engine, const_cast<Value &>(base), value))
+ engine->throwTypeError();
+}
+
+ReturnedValue Runtime::LoadSuperConstructor::call(ExecutionEngine *engine, const Value &t)
{
if (engine->currentStackFrame->thisObject() != Value::emptyValue().asReturnedValue()) {
return engine->throwReferenceError(QStringLiteral("super() already called."), QString(), 0, 0); // ### fix line number
@@ -1124,8 +1128,6 @@ ReturnedValue Runtime::method_loadSuperConstructor(ExecutionEngine *engine, cons
return c->asReturnedValue();
}
-#endif // V4_BOOTSTRAP
-
uint RuntimeHelpers::equalHelper(const Value &x, const Value &y)
{
Q_ASSERT(x.type() != y.type() || (x.isManaged() && (x.isString() != y.isString())));
@@ -1143,25 +1145,21 @@ uint RuntimeHelpers::equalHelper(const Value &x, const Value &y)
double dx = RuntimeHelpers::toNumber(x);
return dx == y.asDouble();
} else if (x.isBoolean()) {
- return Runtime::method_compareEqual(Value::fromDouble((double) x.booleanValue()), y);
+ return Runtime::CompareEqual::call(Value::fromDouble((double) x.booleanValue()), y);
} else if (y.isBoolean()) {
- return Runtime::method_compareEqual(x, Value::fromDouble((double) y.booleanValue()));
+ return Runtime::CompareEqual::call(x, Value::fromDouble((double) y.booleanValue()));
} else {
-#ifdef V4_BOOTSTRAP
- Q_UNIMPLEMENTED();
-#else
Object *xo = x.objectValue();
Object *yo = y.objectValue();
if (yo && (x.isNumber() || x.isString())) {
Scope scope(yo->engine());
ScopedValue py(scope, RuntimeHelpers::objectDefaultValue(yo, PREFERREDTYPE_HINT));
- return Runtime::method_compareEqual(x, py);
+ return Runtime::CompareEqual::call(x, py);
} else if (xo && (y.isNumber() || y.isString())) {
Scope scope(xo->engine());
ScopedValue px(scope, RuntimeHelpers::objectDefaultValue(xo, PREFERREDTYPE_HINT));
- return Runtime::method_compareEqual(px, y);
+ return Runtime::CompareEqual::call(px, y);
}
-#endif
}
return false;
@@ -1177,12 +1175,13 @@ Bool RuntimeHelpers::strictEqual(const Value &x, const Value &y)
if (x.isNumber())
return y.isNumber() && x.asDouble() == y.asDouble();
- if (x.isManaged())
+ if (x.isManaged()) {
return y.isManaged() && x.cast<Managed>()->isEqualTo(y.cast<Managed>());
+ }
return false;
}
-QV4::Bool Runtime::method_compareGreaterThan(const Value &l, const Value &r)
+QV4::Bool Runtime::CompareGreaterThan::call(const Value &l, const Value &r)
{
TRACE2(l, r);
if (l.isInteger() && r.isInteger())
@@ -1192,26 +1191,17 @@ QV4::Bool Runtime::method_compareGreaterThan(const Value &l, const Value &r)
String *sl = l.stringValue();
String *sr = r.stringValue();
if (sl && sr) {
-#ifdef V4_BOOTSTRAP
- Q_UNIMPLEMENTED();
- return false;
-#else
return sr->lessThan(sl);
-#endif
}
Object *ro = r.objectValue();
Object *lo = l.objectValue();
if (ro || lo) {
-#ifdef V4_BOOTSTRAP
- Q_UNIMPLEMENTED();
-#else
QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
QV4::Scope scope(e);
QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue());
QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue());
- return Runtime::method_compareGreaterThan(pl, pr);
-#endif
+ return Runtime::CompareGreaterThan::call(pl, pr);
}
double dl = RuntimeHelpers::toNumber(l);
@@ -1219,7 +1209,7 @@ QV4::Bool Runtime::method_compareGreaterThan(const Value &l, const Value &r)
return dl > dr;
}
-QV4::Bool Runtime::method_compareLessThan(const Value &l, const Value &r)
+QV4::Bool Runtime::CompareLessThan::call(const Value &l, const Value &r)
{
TRACE2(l, r);
if (l.isInteger() && r.isInteger())
@@ -1229,26 +1219,17 @@ QV4::Bool Runtime::method_compareLessThan(const Value &l, const Value &r)
String *sl = l.stringValue();
String *sr = r.stringValue();
if (sl && sr) {
-#ifdef V4_BOOTSTRAP
- Q_UNIMPLEMENTED();
- return false;
-#else
return sl->lessThan(sr);
-#endif
}
Object *ro = r.objectValue();
Object *lo = l.objectValue();
if (ro || lo) {
-#ifdef V4_BOOTSTRAP
- Q_UNIMPLEMENTED();
-#else
QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
QV4::Scope scope(e);
QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue());
QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue());
- return Runtime::method_compareLessThan(pl, pr);
-#endif
+ return Runtime::CompareLessThan::call(pl, pr);
}
double dl = RuntimeHelpers::toNumber(l);
@@ -1256,7 +1237,7 @@ QV4::Bool Runtime::method_compareLessThan(const Value &l, const Value &r)
return dl < dr;
}
-QV4::Bool Runtime::method_compareGreaterEqual(const Value &l, const Value &r)
+QV4::Bool Runtime::CompareGreaterEqual::call(const Value &l, const Value &r)
{
TRACE2(l, r);
if (l.isInteger() && r.isInteger())
@@ -1266,26 +1247,17 @@ QV4::Bool Runtime::method_compareGreaterEqual(const Value &l, const Value &r)
String *sl = l.stringValue();
String *sr = r.stringValue();
if (sl && sr) {
-#ifdef V4_BOOTSTRAP
- Q_UNIMPLEMENTED();
- return false;
-#else
return !sl->lessThan(sr);
-#endif
}
Object *ro = r.objectValue();
Object *lo = l.objectValue();
if (ro || lo) {
-#ifdef V4_BOOTSTRAP
- Q_UNIMPLEMENTED();
-#else
QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
QV4::Scope scope(e);
QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue());
QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue());
- return Runtime::method_compareGreaterEqual(pl, pr);
-#endif
+ return Runtime::CompareGreaterEqual::call(pl, pr);
}
double dl = RuntimeHelpers::toNumber(l);
@@ -1293,7 +1265,7 @@ QV4::Bool Runtime::method_compareGreaterEqual(const Value &l, const Value &r)
return dl >= dr;
}
-QV4::Bool Runtime::method_compareLessEqual(const Value &l, const Value &r)
+QV4::Bool Runtime::CompareLessEqual::call(const Value &l, const Value &r)
{
TRACE2(l, r);
if (l.isInteger() && r.isInteger())
@@ -1303,26 +1275,17 @@ QV4::Bool Runtime::method_compareLessEqual(const Value &l, const Value &r)
String *sl = l.stringValue();
String *sr = r.stringValue();
if (sl && sr) {
-#ifdef V4_BOOTSTRAP
- Q_UNIMPLEMENTED();
- return false;
-#else
return !sr->lessThan(sl);
-#endif
}
Object *ro = r.objectValue();
Object *lo = l.objectValue();
if (ro || lo) {
-#ifdef V4_BOOTSTRAP
- Q_UNIMPLEMENTED();
-#else
QV4::ExecutionEngine *e = (lo ? lo : ro)->engine();
QV4::Scope scope(e);
QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue());
QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue());
- return Runtime::method_compareLessEqual(pl, pr);
-#endif
+ return Runtime::CompareLessEqual::call(pl, pr);
}
double dl = RuntimeHelpers::toNumber(l);
@@ -1330,22 +1293,21 @@ QV4::Bool Runtime::method_compareLessEqual(const Value &l, const Value &r)
return dl <= dr;
}
-#ifndef V4_BOOTSTRAP
-Bool Runtime::method_compareInstanceof(ExecutionEngine *engine, const Value &left, const Value &right)
+Bool Runtime::CompareInstanceof::call(ExecutionEngine *engine, const Value &left, const Value &right)
{
TRACE2(left, right);
Scope scope(engine);
- ScopedValue v(scope, method_instanceof(engine, left, right));
+ ScopedValue v(scope, Instanceof::call(engine, left, right));
return v->booleanValue();
}
-uint Runtime::method_compareIn(ExecutionEngine *engine, const Value &left, const Value &right)
+uint Runtime::CompareIn::call(ExecutionEngine *engine, const Value &left, const Value &right)
{
TRACE2(left, right);
Scope scope(engine);
- ScopedValue v(scope, method_in(engine, left, right));
+ ScopedValue v(scope, In::call(engine, left, right));
return v->booleanValue();
}
@@ -1359,33 +1321,36 @@ static ReturnedValue throwPropertyIsNotAFunctionTypeError(ExecutionEngine *engin
return engine->throwTypeError(msg);
}
-ReturnedValue Runtime::method_callGlobalLookup(ExecutionEngine *engine, uint index, Value *argv, int argc)
+ReturnedValue Runtime::CallGlobalLookup::call(ExecutionEngine *engine, uint index, Value argv[], int argc)
{
Scope scope(engine);
- Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
+ Lookup *l = runtimeLookup(engine->currentStackFrame->v4Function, index);
Value function = Value::fromReturnedValue(l->globalGetter(l, engine));
Value thisObject = Value::undefinedValue();
- if (!function.isFunctionObject())
+ if (!function.isFunctionObject()) {
return throwPropertyIsNotAFunctionTypeError(engine, &thisObject,
engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString());
+ }
return static_cast<FunctionObject &>(function).call(&thisObject, argv, argc);
}
-ReturnedValue Runtime::method_callQmlContextPropertyLookup(ExecutionEngine *engine, uint index, Value *argv, int argc)
+ReturnedValue Runtime::CallQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index,
+ Value *argv, int argc)
{
Scope scope(engine);
ScopedValue thisObject(scope);
- Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
+ Lookup *l = runtimeLookup(engine->currentStackFrame->v4Function, index);
Value function = Value::fromReturnedValue(l->qmlContextPropertyGetter(l, engine, thisObject));
- if (!function.isFunctionObject())
+ if (!function.isFunctionObject()) {
return throwPropertyIsNotAFunctionTypeError(engine, thisObject,
engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString());
+ }
return static_cast<FunctionObject &>(function).call(thisObject, argv, argc);
}
-ReturnedValue Runtime::method_callPossiblyDirectEval(ExecutionEngine *engine, Value *argv, int argc)
+ReturnedValue Runtime::CallPossiblyDirectEval::call(ExecutionEngine *engine, Value *argv, int argc)
{
Scope scope(engine);
ScopedValue thisObject(scope);
@@ -1404,7 +1369,7 @@ ReturnedValue Runtime::method_callPossiblyDirectEval(ExecutionEngine *engine, Va
return function->call(thisObject, argv, argc);
}
-ReturnedValue Runtime::method_callName(ExecutionEngine *engine, int nameIndex, Value *argv, int argc)
+ReturnedValue Runtime::CallName::call(ExecutionEngine *engine, int nameIndex, Value *argv, int argc)
{
Scope scope(engine);
ScopedValue thisObject(scope);
@@ -1415,17 +1380,22 @@ ReturnedValue Runtime::method_callName(ExecutionEngine *engine, int nameIndex, V
if (engine->hasException)
return Encode::undefined();
- if (!f)
- return throwPropertyIsNotAFunctionTypeError(engine, thisObject,
- engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString());
+ if (!f) {
+ return throwPropertyIsNotAFunctionTypeError(
+ engine, thisObject, engine->currentStackFrame->v4Function->compilationUnit
+ ->runtimeStrings[nameIndex]->toQString());
+ }
return f->call(thisObject, argv, argc);
}
-ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc)
+ReturnedValue Runtime::CallProperty::call(ExecutionEngine *engine, const Value &baseRef, int nameIndex, Value *argv, int argc)
{
+ const Value *base = &baseRef;
Scope scope(engine);
- ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ ScopedString name(
+ scope,
+ engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
ScopedObject lookupObject(scope, base);
if (!lookupObject) {
@@ -1437,7 +1407,7 @@ ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base,
}
if (base->isManaged()) {
- Managed *m = static_cast<Managed *>(base);
+ const Managed *m = static_cast<const Managed *>(base);
lookupObject = m->internalClass()->prototype;
Q_ASSERT(m->internalClass()->prototype);
} else {
@@ -1461,20 +1431,21 @@ ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base,
return f->call(base, argv, argc);
}
-ReturnedValue Runtime::method_callPropertyLookup(ExecutionEngine *engine, Value *base, uint index, Value *argv, int argc)
+ReturnedValue Runtime::CallPropertyLookup::call(ExecutionEngine *engine, const Value &base, uint index, Value *argv, int argc)
{
- Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
+ Lookup *l = runtimeLookup(engine->currentStackFrame->v4Function, index);
// ok to have the value on the stack here
- Value f = Value::fromReturnedValue(l->getter(l, engine, *base));
+ Value f = Value::fromReturnedValue(l->getter(l, engine, base));
if (!f.isFunctionObject())
return engine->throwTypeError();
- return static_cast<FunctionObject &>(f).call(base, argv, argc);
+ return static_cast<FunctionObject &>(f).call(&base, argv, argc);
}
-ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, Value *base, const Value &index, Value *argv, int argc)
+ReturnedValue Runtime::CallElement::call(ExecutionEngine *engine, const Value &baseRef, const Value &index, Value *argv, int argc)
{
+ const Value *base = &baseRef;
Scope scope(engine);
ScopedValue thisObject(scope, base->toObject(engine));
base = thisObject;
@@ -1483,14 +1454,14 @@ ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, Value *base,
if (engine->hasException)
return Encode::undefined();
- ScopedFunctionObject f(scope, static_cast<Object *>(base)->get(str));
+ ScopedFunctionObject f(scope, static_cast<const Object *>(base)->get(str));
if (!f)
return engine->throwTypeError();
return f->call(base, argv, argc);
}
-ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &func, Value *argv, int argc)
+ReturnedValue Runtime::CallValue::call(ExecutionEngine *engine, const Value &func, Value *argv, int argc)
{
if (!func.isFunctionObject())
return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
@@ -1498,11 +1469,12 @@ ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &fu
return static_cast<const FunctionObject &>(func).call(&undef, argv, argc);
}
-ReturnedValue Runtime::method_callWithReceiver(ExecutionEngine *engine, const Value &func, const Value *thisObject, Value *argv, int argc)
+ReturnedValue Runtime::CallWithReceiver::call(ExecutionEngine *engine, const Value &func,
+ const Value &thisObject, Value argv[], int argc)
{
if (!func.isFunctionObject())
return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
- return static_cast<const FunctionObject &>(func).call(thisObject, argv, argc);
+ return static_cast<const FunctionObject &>(func).call(&thisObject, argv, argc);
}
struct CallArgs {
@@ -1528,11 +1500,11 @@ static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc)
}
// spread element
++i;
- it = Runtime::method_getIterator(scope.engine, argv[i], /* ForInIterator */ 1);
+ it = Runtime::GetIterator::call(scope.engine, argv[i], /* ForInIterator */ 1);
if (scope.engine->hasException)
return { nullptr, 0 };
while (1) {
- done = Runtime::method_iteratorNext(scope.engine, it, v);
+ done = Runtime::IteratorNext::call(scope.engine, it, v);
if (scope.engine->hasException)
return { nullptr, 0 };
Q_ASSERT(done->isBoolean());
@@ -1545,7 +1517,7 @@ static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc)
return { arguments, argCount };
}
-ReturnedValue Runtime::method_callWithSpread(ExecutionEngine *engine, const Value &function, const Value &thisObject, Value *argv, int argc)
+ReturnedValue Runtime::CallWithSpread::call(ExecutionEngine *engine, const Value &function, const Value &thisObject, Value *argv, int argc)
{
Q_ASSERT(argc >= 1);
if (!function.isFunctionObject())
@@ -1559,7 +1531,7 @@ ReturnedValue Runtime::method_callWithSpread(ExecutionEngine *engine, const Valu
return static_cast<const FunctionObject &>(function).call(&thisObject, arguments.argv, arguments.argc);
}
-ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc)
+ReturnedValue Runtime::Construct::call(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc)
{
if (!function.isFunctionObject())
return engine->throwTypeError();
@@ -1567,7 +1539,7 @@ ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &fu
return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc, &newTarget);
}
-ReturnedValue Runtime::method_constructWithSpread(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc)
+ReturnedValue Runtime::ConstructWithSpread::call(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc)
{
if (!function.isFunctionObject())
return engine->throwTypeError();
@@ -1580,7 +1552,7 @@ ReturnedValue Runtime::method_constructWithSpread(ExecutionEngine *engine, const
return static_cast<const FunctionObject &>(function).callAsConstructor(arguments.argv, arguments.argc, &newTarget);
}
-ReturnedValue Runtime::method_tailCall(CppStackFrame *frame, ExecutionEngine *engine)
+ReturnedValue Runtime::TailCall::call(CppStackFrame *frame, ExecutionEngine *engine)
{
// IMPORTANT! The JIT assumes that this method has the same amount (or less) arguments than
// the jitted function, so it can safely do a tail call.
@@ -1603,20 +1575,21 @@ ReturnedValue Runtime::method_tailCall(CppStackFrame *frame, ExecutionEngine *en
}
memcpy(frame->jsFrame->args, argv, argc * sizeof(Value));
- frame->init(engine, fo.function(), frame->jsFrame->args, argc, frame->callerCanHandleTailCall);
+ frame->init(engine, fo.function(), frame->jsFrame->argValues<Value>(), argc,
+ frame->callerCanHandleTailCall);
frame->setupJSFrame(frame->savedStackTop, fo, fo.scope(), thisObject, Primitive::undefinedValue());
engine->jsStackTop = frame->savedStackTop + frame->requiredJSStackFrameSize();
frame->pendingTailCall = true;
return Encode::undefined();
}
-void Runtime::method_throwException(ExecutionEngine *engine, const Value &value)
+void Runtime::ThrowException::call(ExecutionEngine *engine, const Value &value)
{
if (!value.isEmpty())
engine->throwError(value);
}
-ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value &value)
+ReturnedValue Runtime::TypeofValue::call(ExecutionEngine *engine, const Value &value)
{
Scope scope(engine);
ScopedString res(scope);
@@ -1647,83 +1620,110 @@ ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value &
return res.asReturnedValue();
}
-QV4::ReturnedValue Runtime::method_typeofName(ExecutionEngine *engine, int nameIndex)
+QV4::ReturnedValue Runtime::TypeofName::call(ExecutionEngine *engine, int nameIndex)
{
Scope scope(engine);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
ScopedValue prop(scope, static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name));
// typeof doesn't throw. clear any possible exception
scope.engine->hasException = false;
- return method_typeofValue(engine, prop);
+ return TypeofValue::call(engine, prop);
}
-ReturnedValue Runtime::method_createWithContext(ExecutionEngine *engine, Value *jsStackFrame)
+void Runtime::PushCallContext::call(CppStackFrame *frame)
{
- QV4::Value &accumulator = jsStackFrame[CallData::Accumulator];
- accumulator = accumulator.toObject(engine);
- if (engine->hasException)
- return Encode::undefined();
- Q_ASSERT(accumulator.isObject());
- const Object &obj = static_cast<const Object &>(accumulator);
- ExecutionContext *context = static_cast<ExecutionContext *>(jsStackFrame + CallData::Context);
- return context->newWithContext(obj.d())->asReturnedValue();
+ frame->jsFrame->context = ExecutionContext::newCallContext(frame)->asReturnedValue();
}
-ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex)
+ReturnedValue Runtime::PushWithContext::call(ExecutionEngine *engine, const Value &acc)
{
- ExecutionEngine *e = parent->engine();
- return parent->newCatchContext(e->currentStackFrame, blockIndex,
- e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex])->asReturnedValue();
+ CallData *jsFrame = engine->currentStackFrame->jsFrame;
+ Value &newAcc = jsFrame->accumulator.asValue<Value>();
+ newAcc = Value::fromHeapObject(acc.toObject(engine));
+ if (!engine->hasException) {
+ Q_ASSERT(newAcc.isObject());
+ const Object &obj = static_cast<const Object &>(newAcc);
+ Value &context = jsFrame->context.asValue<Value>();
+ auto ec = static_cast<const ExecutionContext *>(&context);
+ context = ec->newWithContext(obj.d())->asReturnedValue();
+ }
+ return newAcc.asReturnedValue();
}
-ReturnedValue Runtime::method_createBlockContext(ExecutionContext *parent, int index)
+void Runtime::PushCatchContext::call(ExecutionEngine *engine, int blockIndex, int exceptionVarNameIndex)
{
- ExecutionEngine *e = parent->engine();
- return parent->newBlockContext(e->currentStackFrame, index)->asReturnedValue();
+ auto name = engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex];
+ engine->currentStackFrame->jsFrame->context = ExecutionContext::newCatchContext(engine->currentStackFrame, blockIndex, name)->asReturnedValue();
}
-ReturnedValue Runtime::method_cloneBlockContext(ExecutionContext *previous)
+void Runtime::PushBlockContext::call(ExecutionEngine *engine, int index)
{
- return ExecutionContext::cloneBlockContext(static_cast<Heap::CallContext *>(previous->d()))->asReturnedValue();
+ engine->currentStackFrame->jsFrame->context = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue();
}
+void Runtime::CloneBlockContext::call(ExecutionEngine *engine)
+{
+ auto frame = engine->currentStackFrame;
+ auto context = static_cast<Heap::CallContext *>(
+ Value::fromStaticValue(frame->jsFrame->context).m());
+ frame->jsFrame->context =
+ ExecutionContext::cloneBlockContext(engine, context)->asReturnedValue();
+}
-ReturnedValue Runtime::method_createScriptContext(ExecutionEngine *engine, int index)
+void Runtime::PushScriptContext::call(ExecutionEngine *engine, int index)
{
Q_ASSERT(engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_GlobalContext ||
engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_QmlContext);
ReturnedValue c = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue();
engine->setScriptContext(c);
- return c;
+ engine->currentStackFrame->jsFrame->context = c;
}
-ReturnedValue Runtime::method_popScriptContext(ExecutionEngine *engine)
+void Runtime::PopScriptContext::call(ExecutionEngine *engine)
{
ReturnedValue root = engine->rootContext()->asReturnedValue();
engine->setScriptContext(root);
- return root;
+ engine->currentStackFrame->jsFrame->context = root;
}
-void Runtime::method_throwReferenceError(ExecutionEngine *engine, int nameIndex)
+void Runtime::ThrowReferenceError::call(ExecutionEngine *engine, int nameIndex)
{
Scope scope(engine);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
engine->throwReferenceError(name);
}
-void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex)
+void Runtime::ThrowOnNullOrUndefined::call(ExecutionEngine *engine, const Value &v)
+{
+ if (v.isNullOrUndefined())
+ engine->throwTypeError();
+}
+
+ReturnedValue Runtime::ConvertThisToObject::call(ExecutionEngine *engine, const Value &t)
+{
+ if (!t.isObject()) {
+ if (t.isNullOrUndefined()) {
+ return engine->globalObject->asReturnedValue();
+ } else {
+ return t.toObject(engine)->asReturnedValue();
+ }
+ }
+ return t.asReturnedValue();
+}
+
+void Runtime::DeclareVar::call(ExecutionEngine *engine, Bool deletable, int nameIndex)
{
Scope scope(engine);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).createMutableBinding(name, deletable);
}
-ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *values, uint length)
+ReturnedValue Runtime::ArrayLiteral::call(ExecutionEngine *engine, Value *values, uint length)
{
return engine->newArrayObject(values, length)->asReturnedValue();
}
-ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId, const QV4::Value *args, int argc)
+ReturnedValue Runtime::ObjectLiteral::call(ExecutionEngine *engine, int classId, QV4::Value args[], int argc)
{
Scope scope(engine);
Scoped<InternalClass> klass(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeClasses[classId]);
@@ -1755,7 +1755,8 @@ ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId
if (arg != ObjectLiteralArgument::Value) {
Q_ASSERT(args[2].isInteger());
int functionId = args[2].integerValue();
- QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeFunctions[functionId];
+ QV4::Function *clos = engine->currentStackFrame->v4Function->executableCompilationUnit()
+ ->runtimeFunctions[functionId];
Q_ASSERT(clos);
PropertyKey::FunctionNamePrefix prefix = PropertyKey::None;
@@ -1796,9 +1797,11 @@ ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId
return o.asReturnedValue();
}
-ReturnedValue Runtime::method_createClass(ExecutionEngine *engine, int classIndex, const Value &superClass, const Value *computedNames)
+ReturnedValue Runtime::CreateClass::call(ExecutionEngine *engine, int classIndex,
+ const Value &superClass, Value computedNames[])
{
- const CompiledData::CompilationUnit *unit = engine->currentStackFrame->v4Function->compilationUnit;
+ const QV4::ExecutableCompilationUnit *unit
+ = engine->currentStackFrame->v4Function->executableCompilationUnit();
const QV4::CompiledData::Class *cls = unit->unitData()->classAt(classIndex);
Scope scope(engine);
@@ -1898,20 +1901,20 @@ ReturnedValue Runtime::method_createClass(ExecutionEngine *engine, int classInde
return constructor->asReturnedValue();
}
-QV4::ReturnedValue Runtime::method_createMappedArgumentsObject(ExecutionEngine *engine)
+QV4::ReturnedValue Runtime::CreateMappedArgumentsObject::call(ExecutionEngine *engine)
{
Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_CallContext);
Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_ArgumentsObject);
return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue();
}
-QV4::ReturnedValue Runtime::method_createUnmappedArgumentsObject(ExecutionEngine *engine)
+QV4::ReturnedValue Runtime::CreateUnmappedArgumentsObject::call(ExecutionEngine *engine)
{
Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_StrictArgumentsObject);
return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue();
}
-QV4::ReturnedValue Runtime::method_createRestParameter(ExecutionEngine *engine, int argIndex)
+QV4::ReturnedValue Runtime::CreateRestParameter::call(ExecutionEngine *engine, int argIndex)
{
const Value *values = engine->currentStackFrame->originalArguments + argIndex;
int nValues = engine->currentStackFrame->originalArgumentsCount - argIndex;
@@ -1920,14 +1923,33 @@ QV4::ReturnedValue Runtime::method_createRestParameter(ExecutionEngine *engine,
return engine->newArrayObject(values, nValues)->asReturnedValue();
}
-ReturnedValue Runtime::method_regexpLiteral(ExecutionEngine *engine, int id)
+ReturnedValue Runtime::RegexpLiteral::call(ExecutionEngine *engine, int id)
{
- Heap::RegExpObject *ro = engine->newRegExpObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id].as<RegExp>());
+ const auto val
+ = engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id];
+ Heap::RegExpObject *ro = engine->newRegExpObject(Value::fromStaticValue(val).as<RegExp>());
return ro->asReturnedValue();
}
-#endif // V4_BOOTSTRAP
-ReturnedValue Runtime::method_uMinus(const Value &value)
+ReturnedValue Runtime::ToObject::call(ExecutionEngine *engine, const Value &obj)
+{
+ if (obj.isObject())
+ return obj.asReturnedValue();
+
+ return obj.toObject(engine)->asReturnedValue();
+}
+
+Bool Runtime::ToBoolean::call(const Value &obj)
+{
+ return obj.toBoolean();
+}
+
+ReturnedValue Runtime::ToNumber::call(ExecutionEngine *, const Value &v)
+{
+ return Encode(v.toNumber());
+}
+
+ReturnedValue Runtime::UMinus::call(const Value &value)
{
TRACE1(value);
@@ -1943,8 +1965,7 @@ ReturnedValue Runtime::method_uMinus(const Value &value)
// binary operators
-#ifndef V4_BOOTSTRAP
-ReturnedValue Runtime::method_add(ExecutionEngine *engine, const Value &left, const Value &right)
+ReturnedValue Runtime::Add::call(ExecutionEngine *engine, const Value &left, const Value &right)
{
TRACE2(left, right);
@@ -1956,7 +1977,7 @@ ReturnedValue Runtime::method_add(ExecutionEngine *engine, const Value &left, co
return RuntimeHelpers::addHelper(engine, left, right);
}
-ReturnedValue Runtime::method_sub(const Value &left, const Value &right)
+ReturnedValue Runtime::Sub::call(const Value &left, const Value &right)
{
TRACE2(left, right);
@@ -1969,7 +1990,7 @@ ReturnedValue Runtime::method_sub(const Value &left, const Value &right)
return Value::fromDouble(lval - rval).asReturnedValue();
}
-ReturnedValue Runtime::method_mul(const Value &left, const Value &right)
+ReturnedValue Runtime::Mul::call(const Value &left, const Value &right)
{
TRACE2(left, right);
@@ -1982,7 +2003,7 @@ ReturnedValue Runtime::method_mul(const Value &left, const Value &right)
return Value::fromDouble(lval * rval).asReturnedValue();
}
-ReturnedValue Runtime::method_div(const Value &left, const Value &right)
+ReturnedValue Runtime::Div::call(const Value &left, const Value &right)
{
TRACE2(left, right);
@@ -2003,7 +2024,7 @@ ReturnedValue Runtime::method_div(const Value &left, const Value &right)
return Value::fromDouble(lval / rval).asReturnedValue();
}
-ReturnedValue Runtime::method_mod(const Value &left, const Value &right)
+ReturnedValue Runtime::Mod::call(const Value &left, const Value &right)
{
TRACE2(left, right);
@@ -2024,7 +2045,43 @@ ReturnedValue Runtime::method_mod(const Value &left, const Value &right)
return Value::fromDouble(std::fmod(lval, rval)).asReturnedValue();
}
-ReturnedValue Runtime::method_shl(const Value &left, const Value &right)
+ReturnedValue Runtime::Exp::call(const Value &base, const Value &exp)
+{
+ double b = base.toNumber();
+ double e = exp.toNumber();
+ if (qt_is_inf(e) && (b == 1 || b == -1))
+ return Encode(qt_qnan());
+ return Encode(pow(b,e));
+}
+
+ReturnedValue Runtime::BitAnd::call(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ int lval = left.toInt32();
+ int rval = right.toInt32();
+ return Encode((int)(lval & rval));
+}
+
+ReturnedValue Runtime::BitOr::call(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ int lval = left.toInt32();
+ int rval = right.toInt32();
+ return Encode((int)(lval | rval));
+}
+
+ReturnedValue Runtime::BitXor::call(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ int lval = left.toInt32();
+ int rval = right.toInt32();
+ return Encode((int)(lval ^ rval));
+}
+
+ReturnedValue Runtime::Shl::call(const Value &left, const Value &right)
{
TRACE2(left, right);
@@ -2033,7 +2090,7 @@ ReturnedValue Runtime::method_shl(const Value &left, const Value &right)
return Encode((int)(lval << rval));
}
-ReturnedValue Runtime::method_shr(const Value &left, const Value &right)
+ReturnedValue Runtime::Shr::call(const Value &left, const Value &right)
{
TRACE2(left, right);
@@ -2042,7 +2099,7 @@ ReturnedValue Runtime::method_shr(const Value &left, const Value &right)
return Encode((int)(lval >> rval));
}
-ReturnedValue Runtime::method_ushr(const Value &left, const Value &right)
+ReturnedValue Runtime::UShr::call(const Value &left, const Value &right)
{
TRACE2(left, right);
@@ -2053,37 +2110,35 @@ ReturnedValue Runtime::method_ushr(const Value &left, const Value &right)
return Encode(res);
}
-#endif // V4_BOOTSTRAP
-
-ReturnedValue Runtime::method_greaterThan(const Value &left, const Value &right)
+ReturnedValue Runtime::GreaterThan::call(const Value &left, const Value &right)
{
TRACE2(left, right);
- bool r = method_compareGreaterThan(left, right);
+ bool r = CompareGreaterThan::call(left, right);
return Encode(r);
}
-ReturnedValue Runtime::method_lessThan(const Value &left, const Value &right)
+ReturnedValue Runtime::LessThan::call(const Value &left, const Value &right)
{
TRACE2(left, right);
- bool r = method_compareLessThan(left, right);
+ bool r = CompareLessThan::call(left, right);
return Encode(r);
}
-ReturnedValue Runtime::method_greaterEqual(const Value &left, const Value &right)
+ReturnedValue Runtime::GreaterEqual::call(const Value &left, const Value &right)
{
TRACE2(left, right);
- bool r = method_compareGreaterEqual(left, right);
+ bool r = CompareGreaterEqual::call(left, right);
return Encode(r);
}
-ReturnedValue Runtime::method_lessEqual(const Value &left, const Value &right)
+ReturnedValue Runtime::LessEqual::call(const Value &left, const Value &right)
{
TRACE2(left, right);
- bool r = method_compareLessEqual(left, right);
+ bool r = CompareLessEqual::call(left, right);
return Encode(r);
}
@@ -2107,18 +2162,16 @@ struct LazyScope
}
};
-Bool Runtime::method_compareEqual(const Value &left, const Value &right)
+Bool Runtime::CompareEqual::call(const Value &left, const Value &right)
{
TRACE2(left, right);
Value lhs = left;
Value rhs = right;
-#ifndef V4_BOOTSTRAP
LazyScope scope;
Value *lhsGuard = nullptr;
Value *rhsGuard = nullptr;
-#endif
redo:
if (lhs.asReturnedValue() == rhs.asReturnedValue())
@@ -2148,7 +2201,6 @@ Bool Runtime::method_compareEqual(const Value &left, const Value &right)
case QV4::Value::QT_ManagedOrUndefined1:
case QV4::Value::QT_ManagedOrUndefined2:
case QV4::Value::QT_ManagedOrUndefined3: {
-#ifndef V4_BOOTSTRAP
// RHS: Managed
Heap::Base *l = lhs.m();
Heap::Base *r = rhs.m();
@@ -2166,7 +2218,6 @@ Bool Runtime::method_compareEqual(const Value &left, const Value &right)
lhs = lhsGuard->asReturnedValue();
break;
}
-#endif
return false;
}
case QV4::Value::QT_Empty:
@@ -2178,16 +2229,12 @@ Bool Runtime::method_compareEqual(const Value &left, const Value &right)
rhs = Value::fromDouble(rhs.int_32());
// fall through
default: // double
-#ifndef V4_BOOTSTRAP
if (lhs.m()->internalClass->vtable->isStringOrSymbol) {
return lhs.m()->internalClass->vtable->isString ? (RuntimeHelpers::toNumber(lhs) == rhs.doubleValue()) : false;
} else {
scope.set(&lhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT), lhs.m()->internalClass->engine);
lhs = lhsGuard->asReturnedValue();
}
-#else
- Q_UNIMPLEMENTED();
-#endif
}
goto redo;
case QV4::Value::QT_Empty:
@@ -2216,23 +2263,23 @@ Bool Runtime::method_compareEqual(const Value &left, const Value &right)
}
}
-ReturnedValue Runtime::method_equal(const Value &left, const Value &right)
+ReturnedValue Runtime::Equal::call(const Value &left, const Value &right)
{
TRACE2(left, right);
- bool r = method_compareEqual(left, right);
+ bool r = CompareEqual::call(left, right);
return Encode(r);
}
-ReturnedValue Runtime::method_notEqual(const Value &left, const Value &right)
+ReturnedValue Runtime::NotEqual::call(const Value &left, const Value &right)
{
TRACE2(left, right);
- bool r = !method_compareEqual(left, right);
+ bool r = !CompareEqual::call(left, right);
return Encode(r);
}
-ReturnedValue Runtime::method_strictEqual(const Value &left, const Value &right)
+ReturnedValue Runtime::StrictEqual::call(const Value &left, const Value &right)
{
TRACE2(left, right);
@@ -2240,7 +2287,7 @@ ReturnedValue Runtime::method_strictEqual(const Value &left, const Value &right)
return Encode(r);
}
-ReturnedValue Runtime::method_strictNotEqual(const Value &left, const Value &right)
+ReturnedValue Runtime::StrictNotEqual::call(const Value &left, const Value &right)
{
TRACE2(left, right);
@@ -2248,21 +2295,21 @@ ReturnedValue Runtime::method_strictNotEqual(const Value &left, const Value &rig
return Encode(r);
}
-Bool Runtime::method_compareNotEqual(const Value &left, const Value &right)
+Bool Runtime::CompareNotEqual::call(const Value &left, const Value &right)
{
TRACE2(left, right);
- return !Runtime::method_compareEqual(left, right);
+ return !Runtime::CompareEqual::call(left, right);
}
-Bool Runtime::method_compareStrictEqual(const Value &left, const Value &right)
+Bool Runtime::CompareStrictEqual::call(const Value &left, const Value &right)
{
TRACE2(left, right);
return RuntimeHelpers::strictEqual(left, right);
}
-Bool Runtime::method_compareStrictNotEqual(const Value &left, const Value &right)
+Bool Runtime::CompareStrictNotEqual::call(const Value &left, const Value &right)
{
TRACE2(left, right);
diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h
index 2be3ebf012..cb89b7e6c0 100644
--- a/src/qml/jsruntime/qv4runtime_p.h
+++ b/src/qml/jsruntime/qv4runtime_p.h
@@ -52,8 +52,6 @@
#include "qv4global_p.h"
#include "qv4value_p.h"
-#include "qv4context_p.h"
-#include "qv4engine_p.h"
#include "qv4math_p.h"
#include "qv4runtimeapi_p.h"
#include <QtCore/qnumeric.h>
@@ -114,20 +112,16 @@ struct Q_QML_PRIVATE_EXPORT RuntimeHelpers {
static Bool strictEqual(const Value &x, const Value &y);
static ReturnedValue addHelper(ExecutionEngine *engine, const Value &left, const Value &right);
-
- static ReturnedValue getTemplateObject(Function *function, int index);
};
// type conversion and testing
-#ifndef V4_BOOTSTRAP
inline ReturnedValue RuntimeHelpers::toPrimitive(const Value &value, TypeHint typeHint)
{
if (!value.isObject())
return value.asReturnedValue();
return RuntimeHelpers::objectDefaultValue(&reinterpret_cast<const Object &>(value), typeHint);
}
-#endif
inline double RuntimeHelpers::toNumber(const Value &value)
{
diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h
index ceec13a3bb..05ffb84d58 100644
--- a/src/qml/jsruntime/qv4runtimeapi_p.h
+++ b/src/qml/jsruntime/qv4runtimeapi_p.h
@@ -51,179 +51,449 @@
//
#include <private/qv4global_p.h>
+#include <private/qv4staticvalue_p.h>
QT_BEGIN_NAMESPACE
namespace QV4 {
typedef uint Bool;
-struct NoThrowEngine;
-namespace {
-template <typename T>
-struct ExceptionCheck {
- enum { NeedsCheck = 1 };
-};
-// push_catch and pop context methods shouldn't check for exceptions
-template <>
-struct ExceptionCheck<void (*)(QV4::NoThrowEngine *)> {
- enum { NeedsCheck = 0 };
-};
-template <typename A>
-struct ExceptionCheck<void (*)(A, QV4::NoThrowEngine)> {
- enum { NeedsCheck = 0 };
-};
-template <>
-struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *)> {
- enum { NeedsCheck = 0 };
-};
-template <typename A>
-struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *, A)> {
- enum { NeedsCheck = 0 };
-};
-template <typename A, typename B>
-struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *, A, B)> {
- enum { NeedsCheck = 0 };
-};
-template <typename A, typename B, typename C>
-struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
- enum { NeedsCheck = 0 };
-};
-} // anonymous namespace
-
-#define FOR_EACH_RUNTIME_METHOD(F) \
- /* call */ \
- F(ReturnedValue, callGlobalLookup, (ExecutionEngine *engine, uint index, Value *argv, int argc)) \
- F(ReturnedValue, callQmlContextPropertyLookup, (ExecutionEngine *engine, uint index, Value *argv, int argc)) \
- F(ReturnedValue, callName, (ExecutionEngine *engine, int nameIndex, Value *argv, int argc)) \
- F(ReturnedValue, callProperty, (ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc)) \
- F(ReturnedValue, callPropertyLookup, (ExecutionEngine *engine, Value *base, uint index, Value *argv, int argc)) \
- F(ReturnedValue, callElement, (ExecutionEngine *engine, Value *base, const Value &index, Value *argv, int argc)) \
- F(ReturnedValue, callValue, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \
- F(ReturnedValue, callWithReceiver, (ExecutionEngine *engine, const Value &func, const Value *thisObject, Value *argv, int argc)) \
- F(ReturnedValue, callPossiblyDirectEval, (ExecutionEngine *engine, Value *argv, int argc)) \
- F(ReturnedValue, callWithSpread, (ExecutionEngine *engine, const Value &func, const Value &thisObject, Value *argv, int argc)) \
- F(ReturnedValue, tailCall, (CppStackFrame *frame, ExecutionEngine *engine)) \
- \
- /* construct */ \
- F(ReturnedValue, construct, (ExecutionEngine *engine, const Value &func, const Value &newTarget, Value *argv, int argc)) \
- F(ReturnedValue, constructWithSpread, (ExecutionEngine *engine, const Value &func, const Value &newTarget, Value *argv, int argc)) \
- \
- /* load & store */ \
- F(void, storeNameStrict, (ExecutionEngine *engine, int nameIndex, const Value &value)) \
- F(void, storeNameSloppy, (ExecutionEngine *engine, int nameIndex, const Value &value)) \
- F(void, storeProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \
- F(void, storeElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \
- F(void, storeElement_traced, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value, quint8 *traceSlot)) \
- F(ReturnedValue, loadProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)) \
- F(ReturnedValue, loadName, (ExecutionEngine *engine, int nameIndex)) \
- F(ReturnedValue, loadElement, (ExecutionEngine *engine, const Value &object, const Value &index)) \
- F(ReturnedValue, loadElement_traced, (ExecutionEngine *engine, const Value &object, const Value &index, quint8 *traceSlot)) \
- F(ReturnedValue, loadSuperProperty, (ExecutionEngine *engine, const Value &property)) \
- F(void, storeSuperProperty, (ExecutionEngine *engine, const Value &property, const Value &value)) \
- F(ReturnedValue, loadSuperConstructor, (ExecutionEngine *engine, const Value &t)) \
- \
- /* typeof */ \
- F(ReturnedValue, typeofValue, (ExecutionEngine *engine, const Value &val)) \
- F(ReturnedValue, typeofName, (ExecutionEngine *engine, int nameIndex)) \
- \
- /* delete */ \
- F(bool, deleteProperty, (ExecutionEngine *engine, const Value &base, const Value &index)) \
- F(bool, deleteName, (ExecutionEngine *engine, int nameIndex)) \
- \
- /* exceptions & scopes */ \
- F(void, throwException, (ExecutionEngine *engine, const Value &value)) \
- F(ReturnedValue, createWithContext, (ExecutionEngine *, Value *jsStackFrame)) \
- F(ReturnedValue, createCatchContext, (ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex)) \
- F(ReturnedValue, createBlockContext, (ExecutionContext *parent, int index)) \
- F(ReturnedValue, createScriptContext, (ExecutionEngine *engine, int index)) \
- F(ReturnedValue, cloneBlockContext, (ExecutionContext *previous)) \
- F(ReturnedValue, popScriptContext, (ExecutionEngine *engine)) \
- F(void, throwReferenceError, (ExecutionEngine *engine, int nameIndex)) \
- \
- /* closures */ \
- F(ReturnedValue, closure, (ExecutionEngine *engine, int functionId)) \
- \
- /* function header */ \
- F(void, declareVar, (ExecutionEngine *engine, bool deletable, int nameIndex)) \
- F(ReturnedValue, createMappedArgumentsObject, (ExecutionEngine *engine)) \
- F(ReturnedValue, createUnmappedArgumentsObject, (ExecutionEngine *engine)) \
- F(ReturnedValue, createRestParameter, (ExecutionEngine *engine, int argIndex)) \
- \
- /* literals */ \
- F(ReturnedValue, arrayLiteral, (ExecutionEngine *engine, Value *values, uint length)) \
- F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, int classId, const Value *args, int argc)) \
- F(ReturnedValue, createClass, (ExecutionEngine *engine, int classIndex, const Value &heritage, const Value *computedNames)) \
- \
- /* for-in, for-of and array destructuring */ \
- F(ReturnedValue, getIterator, (ExecutionEngine *engine, const Value &in, int iterator)) \
- F(ReturnedValue, iteratorNext, (ExecutionEngine *engine, const Value &iterator, Value *value)) \
- F(ReturnedValue, iteratorNextForYieldStar, (ExecutionEngine *engine, const Value &received, const Value &iterator, Value *object)) \
- F(ReturnedValue, iteratorClose, (ExecutionEngine *engine, const Value &iterator, const Value &done)) \
- F(ReturnedValue, destructureRestElement, (ExecutionEngine *engine, const Value &iterator)) \
- \
- /* unary operators */ \
- F(ReturnedValue, uMinus, (const Value &value)) \
- \
- /* binary operators */ \
- F(ReturnedValue, instanceof, (ExecutionEngine *engine, const Value &left, const Value &right)) \
- F(ReturnedValue, in, (ExecutionEngine *engine, const Value &left, const Value &right)) \
- F(ReturnedValue, add, (ExecutionEngine *engine, const Value &left, const Value &right)) \
- F(ReturnedValue, sub, (const Value &left, const Value &right)) \
- F(ReturnedValue, mul, (const Value &left, const Value &right)) \
- F(ReturnedValue, div, (const Value &left, const Value &right)) \
- F(ReturnedValue, mod, (const Value &left, const Value &right)) \
- F(ReturnedValue, shl, (const Value &left, const Value &right)) \
- F(ReturnedValue, shr, (const Value &left, const Value &right)) \
- F(ReturnedValue, ushr, (const Value &left, const Value &right)) \
- F(ReturnedValue, greaterThan, (const Value &left, const Value &right)) \
- F(ReturnedValue, lessThan, (const Value &left, const Value &right)) \
- F(ReturnedValue, greaterEqual, (const Value &left, const Value &right)) \
- F(ReturnedValue, lessEqual, (const Value &left, const Value &right)) \
- F(ReturnedValue, equal, (const Value &left, const Value &right)) \
- F(ReturnedValue, notEqual, (const Value &left, const Value &right)) \
- F(ReturnedValue, strictEqual, (const Value &left, const Value &right)) \
- F(ReturnedValue, strictNotEqual, (const Value &left, const Value &right)) \
- \
- /* comparisons */ \
- F(Bool, compareGreaterThan, (const Value &l, const Value &r)) \
- F(Bool, compareLessThan, (const Value &l, const Value &r)) \
- F(Bool, compareGreaterEqual, (const Value &l, const Value &r)) \
- F(Bool, compareLessEqual, (const Value &l, const Value &r)) \
- F(Bool, compareEqual, (const Value &left, const Value &right)) \
- F(Bool, compareNotEqual, (const Value &left, const Value &right)) \
- F(Bool, compareStrictEqual, (const Value &left, const Value &right)) \
- F(Bool, compareStrictNotEqual, (const Value &left, const Value &right)) \
- \
- F(Bool, compareInstanceof, (ExecutionEngine *engine, const Value &left, const Value &right)) \
- F(Bool, compareIn, (ExecutionEngine *engine, const Value &left, const Value &right)) \
- \
- F(ReturnedValue, regexpLiteral, (ExecutionEngine *engine, int id))
struct Q_QML_PRIVATE_EXPORT Runtime {
- Runtime();
-
typedef ReturnedValue (*UnaryOperation)(const Value &value);
typedef ReturnedValue (*BinaryOperation)(const Value &left, const Value &right);
- typedef ReturnedValue (*BinaryOperationContext)(ExecutionEngine *engine, const Value &left, const Value &right);
+ typedef ReturnedValue (*BinaryOperationContext)(ExecutionEngine *, const Value &left, const Value &right);
-#define DEFINE_RUNTIME_METHOD_ENUM(returnvalue, name, args) name,
- enum RuntimeMethods {
- FOR_EACH_RUNTIME_METHOD(DEFINE_RUNTIME_METHOD_ENUM)
- RuntimeMethodCount,
- InvalidRuntimeMethod = RuntimeMethodCount
+ enum class Throws { No, Yes };
+ enum class ChangesContext { No, Yes };
+ enum class Pure { No, Yes };
+ enum class LastArgumentIsOutputValue { No, Yes };
+
+ template<Throws t, ChangesContext c = ChangesContext::No, Pure p = Pure::No,
+ LastArgumentIsOutputValue out = LastArgumentIsOutputValue::No>
+ struct Method
+ {
+ static constexpr bool throws = t == Throws::Yes;
+ static constexpr bool changesContext = c == ChangesContext::Yes;
+ static constexpr bool pure = p == Pure::Yes;
+ static constexpr bool lastArgumentIsOutputValue = out == LastArgumentIsOutputValue::Yes;
+ };
+ using PureMethod = Method<Throws::No, ChangesContext::No, Pure::Yes>;
+ using IteratorMethod = Method<Throws::Yes, ChangesContext::No, Pure::No,
+ LastArgumentIsOutputValue::Yes>;
+
+ /* call */
+ struct Q_QML_PRIVATE_EXPORT CallGlobalLookup : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, uint, Value[], int);
+ };
+ struct Q_QML_PRIVATE_EXPORT CallQmlContextPropertyLookup : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, uint, Value[], int);
+ };
+ struct Q_QML_PRIVATE_EXPORT CallName : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, int, Value[], int);
+ };
+ struct Q_QML_PRIVATE_EXPORT CallProperty : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &, int, Value[], int);
+ };
+ struct Q_QML_PRIVATE_EXPORT CallPropertyLookup : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &, uint, Value[], int);
+ };
+ struct Q_QML_PRIVATE_EXPORT CallElement : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int);
+ };
+ struct Q_QML_PRIVATE_EXPORT CallValue : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &, Value[], int);
+ };
+ struct Q_QML_PRIVATE_EXPORT CallWithReceiver : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int);
+ };
+ struct Q_QML_PRIVATE_EXPORT CallPossiblyDirectEval : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, Value[], int);
+ };
+ struct Q_QML_PRIVATE_EXPORT CallWithSpread : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int);
+ };
+ struct Q_QML_PRIVATE_EXPORT TailCall : Method<Throws::Yes>
+ {
+ static ReturnedValue call(CppStackFrame *, ExecutionEngine *);
};
-#undef DEFINE_RUNTIME_METHOD_ENUM
- void *runtimeMethods[RuntimeMethodCount];
+ /* construct */
+ struct Q_QML_PRIVATE_EXPORT Construct : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int);
+ };
+ struct Q_QML_PRIVATE_EXPORT ConstructWithSpread : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int);
+ };
- static uint runtimeMethodOffset(RuntimeMethods method) { return method*QT_POINTER_SIZE; }
+ /* load & store */
+ struct Q_QML_PRIVATE_EXPORT StoreNameStrict : Method<Throws::Yes>
+ {
+ static void call(ExecutionEngine *, int, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT StoreNameSloppy : Method<Throws::Yes>
+ {
+ static void call(ExecutionEngine *, int, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT StoreProperty : Method<Throws::Yes>
+ {
+ static void call(ExecutionEngine *, const Value &, int, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT StoreElement : Method<Throws::Yes>
+ {
+ static void call(ExecutionEngine *, const Value &, const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT LoadProperty : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &, int);
+ };
+ struct Q_QML_PRIVATE_EXPORT LoadName : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, int);
+ };
+ struct Q_QML_PRIVATE_EXPORT LoadElement : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT LoadSuperProperty : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT StoreSuperProperty : Method<Throws::Yes>
+ {
+ static void call(ExecutionEngine *, const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT LoadSuperConstructor : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT LoadGlobalLookup : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, Function *, int);
+ };
+ struct Q_QML_PRIVATE_EXPORT LoadQmlContextPropertyLookup : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, uint);
+ };
+ struct Q_QML_PRIVATE_EXPORT GetLookup : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, Function *, const Value &, int);
+ };
+ struct Q_QML_PRIVATE_EXPORT SetLookupStrict : Method<Throws::Yes>
+ {
+ static void call(Function *, const Value &, int, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT SetLookupSloppy : Method<Throws::Yes>
+ {
+ static void call(Function *, const Value &, int, const Value &);
+ };
+
+ /* typeof */
+ struct Q_QML_PRIVATE_EXPORT TypeofValue : PureMethod
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT TypeofName : Method<Throws::No>
+ {
+ static ReturnedValue call(ExecutionEngine *, int);
+ };
+
+ /* delete */
+ struct Q_QML_PRIVATE_EXPORT DeleteProperty_NoThrow : Method<Throws::No>
+ {
+ static Bool call(ExecutionEngine *, const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT DeleteProperty : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, Function *, const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT DeleteName_NoThrow : Method<Throws::No>
+ {
+ static Bool call(ExecutionEngine *, int);
+ };
+ struct Q_QML_PRIVATE_EXPORT DeleteName : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, Function *, int);
+ };
+
+ /* exceptions & scopes */
+ struct Q_QML_PRIVATE_EXPORT ThrowException : Method<Throws::Yes>
+ {
+ static void call(ExecutionEngine *, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT PushCallContext : Method<Throws::No, ChangesContext::Yes>
+ {
+ static void call(CppStackFrame *);
+ };
+ struct Q_QML_PRIVATE_EXPORT PushWithContext : Method<Throws::Yes, ChangesContext::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT PushCatchContext : Method<Throws::No, ChangesContext::Yes>
+ {
+ static void call(ExecutionEngine *, int, int);
+ };
+ struct Q_QML_PRIVATE_EXPORT PushBlockContext : Method<Throws::No, ChangesContext::Yes>
+ {
+ static void call(ExecutionEngine *, int);
+ };
+ struct Q_QML_PRIVATE_EXPORT CloneBlockContext : Method<Throws::No, ChangesContext::Yes>
+ {
+ static void call(ExecutionEngine *);
+ };
+ struct Q_QML_PRIVATE_EXPORT PushScriptContext : Method<Throws::No, ChangesContext::Yes>
+ {
+ static void call(ExecutionEngine *, int);
+ };
+ struct Q_QML_PRIVATE_EXPORT PopScriptContext : Method<Throws::No, ChangesContext::Yes>
+ {
+ static void call(ExecutionEngine *);
+ };
+ struct Q_QML_PRIVATE_EXPORT ThrowReferenceError : Method<Throws::Yes>
+ {
+ static void call(ExecutionEngine *, int);
+ };
+ struct Q_QML_PRIVATE_EXPORT ThrowOnNullOrUndefined : Method<Throws::Yes>
+ {
+ static void call(ExecutionEngine *, const Value &);
+ };
+
+ /* closures */
+ struct Q_QML_PRIVATE_EXPORT Closure : Method<Throws::No>
+ {
+ static ReturnedValue call(ExecutionEngine *, int);
+ };
-#define RUNTIME_METHOD(returnvalue, name, args) \
- typedef returnvalue (*Method_##name)args; \
- enum { Method_##name##_NeedsExceptionCheck = ExceptionCheck<Method_##name>::NeedsCheck }; \
- static returnvalue method_##name args;
- FOR_EACH_RUNTIME_METHOD(RUNTIME_METHOD)
-#undef RUNTIME_METHOD
+ /* Function header */
+ struct Q_QML_PRIVATE_EXPORT ConvertThisToObject : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT DeclareVar : Method<Throws::Yes>
+ {
+ static void call(ExecutionEngine *, Bool, int);
+ };
+ struct Q_QML_PRIVATE_EXPORT CreateMappedArgumentsObject : Method<Throws::No>
+ {
+ static ReturnedValue call(ExecutionEngine *);
+ };
+ struct Q_QML_PRIVATE_EXPORT CreateUnmappedArgumentsObject : Method<Throws::No>
+ {
+ static ReturnedValue call(ExecutionEngine *);
+ };
+ struct Q_QML_PRIVATE_EXPORT CreateRestParameter : PureMethod
+ {
+ static ReturnedValue call(ExecutionEngine *, int);
+ };
+
+ /* literals */
+ struct Q_QML_PRIVATE_EXPORT ArrayLiteral : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, Value[], uint);
+ };
+ struct Q_QML_PRIVATE_EXPORT ObjectLiteral : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, int, Value[], int);
+ };
+ struct Q_QML_PRIVATE_EXPORT CreateClass : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, int, const Value &, Value[]);
+ };
+
+ /* for-in, for-of and array destructuring */
+ struct Q_QML_PRIVATE_EXPORT GetIterator : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &, int);
+ };
+ struct Q_QML_PRIVATE_EXPORT IteratorNext : IteratorMethod
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &, Value *);
+ };
+ struct Q_QML_PRIVATE_EXPORT IteratorNextForYieldStar : IteratorMethod
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value *);
+ };
+ struct Q_QML_PRIVATE_EXPORT IteratorClose : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT DestructureRestElement : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &);
+ };
+
+ /* conversions */
+ struct Q_QML_PRIVATE_EXPORT ToObject : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT ToBoolean : PureMethod
+ {
+ static Bool call(const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT ToNumber : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &);
+ };
+ /* unary operators */
+ struct Q_QML_PRIVATE_EXPORT UMinus : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &);
+ };
+
+ /* binary operators */
+ struct Q_QML_PRIVATE_EXPORT Instanceof : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT In : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT Add : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT Sub : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT Mul : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT Div : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT Mod : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT Exp : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT BitAnd : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT BitOr : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT BitXor : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT Shl : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT Shr : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT UShr : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT GreaterThan : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT LessThan : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT GreaterEqual : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT LessEqual : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT Equal : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT NotEqual : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT StrictEqual : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT StrictNotEqual : Method<Throws::Yes>
+ {
+ static ReturnedValue call(const Value &, const Value &);
+ };
+
+ /* comparisons */
+ struct Q_QML_PRIVATE_EXPORT CompareGreaterThan : Method<Throws::Yes>
+ {
+ static Bool call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT CompareLessThan : Method<Throws::Yes>
+ {
+ static Bool call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT CompareGreaterEqual : Method<Throws::Yes>
+ {
+ static Bool call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT CompareLessEqual : Method<Throws::Yes>
+ {
+ static Bool call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT CompareEqual : Method<Throws::Yes>
+ {
+ static Bool call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT CompareNotEqual : Method<Throws::Yes>
+ {
+ static Bool call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT CompareStrictEqual : Method<Throws::Yes>
+ {
+ static Bool call(const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT CompareStrictNotEqual : Method<Throws::Yes>
+ {
+ static Bool call(const Value &, const Value &);
+ };
+
+ struct Q_QML_PRIVATE_EXPORT CompareInstanceof : Method<Throws::Yes>
+ {
+ static Bool call(ExecutionEngine *, const Value &, const Value &);
+ };
+ struct Q_QML_PRIVATE_EXPORT CompareIn : Method<Throws::Yes>
+ {
+ static Bool call(ExecutionEngine *, const Value &, const Value &);
+ };
+
+ struct Q_QML_PRIVATE_EXPORT RegexpLiteral : PureMethod
+ {
+ static ReturnedValue call(ExecutionEngine *, int);
+ };
+ struct Q_QML_PRIVATE_EXPORT GetTemplateObject : PureMethod
+ {
+ static ReturnedValue call(Function *, int);
+ };
struct StackOffsets {
static const int tailCall_function = -1;
@@ -234,7 +504,6 @@ struct Q_QML_PRIVATE_EXPORT Runtime {
};
static_assert(std::is_standard_layout<Runtime>::value, "Runtime needs to be standard layout in order for us to be able to use offsetof");
-static_assert(offsetof(Runtime, runtimeMethods) == 0, "JIT expects this to be the first member");
static_assert(sizeof(Runtime::BinaryOperation) == sizeof(void*), "JIT expects a function pointer to fit into a regular pointer, for cross-compilation offset translation");
} // namespace QV4
diff --git a/src/qml/jsruntime/qv4runtimecodegen.cpp b/src/qml/jsruntime/qv4runtimecodegen.cpp
index 9866966936..162d75db63 100644
--- a/src/qml/jsruntime/qv4runtimecodegen.cpp
+++ b/src/qml/jsruntime/qv4runtimecodegen.cpp
@@ -37,10 +37,12 @@
**
****************************************************************************/
+#include "qv4engine_p.h"
#include "qv4runtimecodegen_p.h"
-#include "qv4compilerscanfunctions_p.h"
+#include <private/qv4compilerscanfunctions_p.h>
using namespace QV4;
+using namespace QQmlJS;
void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName,
const QString &sourceCode,
@@ -58,7 +60,7 @@ void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName,
scan(ast);
scan.leaveEnvironment();
- if (hasError)
+ if (hasError())
return;
int index = defineFunction(ast->name.toString(), ast, ast->formals, ast->body);
@@ -67,17 +69,19 @@ void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName,
void RuntimeCodegen::throwSyntaxError(const AST::SourceLocation &loc, const QString &detail)
{
- if (hasError)
+ if (hasError())
return;
- hasError = true;
+
+ Codegen::throwSyntaxError(loc, detail);
engine->throwSyntaxError(detail, _module->fileName, loc.startLine, loc.startColumn);
}
void RuntimeCodegen::throwReferenceError(const AST::SourceLocation &loc, const QString &detail)
{
- if (hasError)
+ if (hasError())
return;
- hasError = true;
+
+ Codegen::throwReferenceError(loc, detail);
engine->throwReferenceError(detail, _module->fileName, loc.startLine, loc.startColumn);
}
diff --git a/src/qml/jsruntime/qv4runtimecodegen_p.h b/src/qml/jsruntime/qv4runtimecodegen_p.h
index 006a6a3cde..71aaf1fb55 100644
--- a/src/qml/jsruntime/qv4runtimecodegen_p.h
+++ b/src/qml/jsruntime/qv4runtimecodegen_p.h
@@ -66,11 +66,11 @@ public:
void generateFromFunctionExpression(const QString &fileName,
const QString &sourceCode,
- AST::FunctionExpression *ast,
+ QQmlJS::AST::FunctionExpression *ast,
Compiler::Module *module);
- void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) override;
- void throwReferenceError(const AST::SourceLocation &loc, const QString &detail) override;
+ void throwSyntaxError(const QQmlJS::AST::SourceLocation &loc, const QString &detail) override;
+ void throwReferenceError(const QQmlJS::AST::SourceLocation &loc, const QString &detail) override;
private:
ExecutionEngine *engine;
diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h
index e4aceef3ee..12a6381e6f 100644
--- a/src/qml/jsruntime/qv4scopedvalue_p.h
+++ b/src/qml/jsruntime/qv4scopedvalue_p.h
@@ -70,7 +70,7 @@ struct ScopedValue;
#define CHECK_EXCEPTION() \
do { \
- if (scope.hasException()) { \
+ if (scope.hasException() || scope.engine->isInterrupted.loadAcquire()) { \
return QV4::Encode::undefined(); \
} \
} while (false)
diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp
index 6cb2e95cdc..2fab9e4b7b 100644
--- a/src/qml/jsruntime/qv4script.cpp
+++ b/src/qml/jsruntime/qv4script.cpp
@@ -60,8 +60,9 @@
#include <QScopedValueRollback>
using namespace QV4;
+using namespace QQmlJS;
-Script::Script(ExecutionEngine *v4, QmlContext *qml, const QQmlRefPointer<CompiledData::CompilationUnit> &compilationUnit)
+Script::Script(ExecutionEngine *v4, QmlContext *qml, const QQmlRefPointer<ExecutableCompilationUnit> &compilationUnit)
: line(1), column(0), context(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false)
, compilationUnit(compilationUnit), vmFunction(nullptr), parseAsBinding(true)
{
@@ -108,10 +109,10 @@ void Script::parse()
const auto diagnosticMessages = parser.diagnosticMessages();
for (const DiagnosticMessage &m : diagnosticMessages) {
if (m.isError()) {
- valueScope.engine->throwSyntaxError(m.message, sourceFile, m.loc.startLine, m.loc.startColumn);
+ valueScope.engine->throwSyntaxError(m.message, sourceFile, m.line, m.column);
return;
} else {
- qWarning() << sourceFile << ':' << m.loc.startLine << ':' << m.loc.startColumn
+ qWarning() << sourceFile << ':' << m.line << ':' << m.column
<< ": warning: " << m.message;
}
}
@@ -133,7 +134,7 @@ void Script::parse()
if (v4->hasException)
return;
- compilationUnit = cg.generateCompilationUnit();
+ compilationUnit = QV4::ExecutableCompilationUnit::create(cg.generateCompilationUnit());
vmFunction = compilationUnit->linkToEngine(v4);
}
@@ -172,10 +173,11 @@ Function *Script::function()
return vmFunction;
}
-QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, Compiler::JSUnitGenerator *unitGenerator,
- const QString &fileName, const QString &finalUrl, const QString &source,
- QList<QQmlError> *reportedErrors,
- QV4::Compiler::ContextType contextType)
+QV4::CompiledData::CompilationUnit Script::precompile(
+ QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine,
+ Compiler::JSUnitGenerator *unitGenerator, const QString &fileName, const QString &finalUrl,
+ const QString &source, QList<QQmlError> *reportedErrors,
+ QV4::Compiler::ContextType contextType)
{
using namespace QV4::Compiler;
using namespace QQmlJS::AST;
@@ -202,10 +204,16 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compi
Codegen cg(unitGenerator, /*strict mode*/false);
cg.generateFromProgram(fileName, finalUrl, source, program, module, contextType);
- errors = cg.qmlErrors();
- if (!errors.isEmpty()) {
- if (reportedErrors)
- *reportedErrors << errors;
+ if (cg.hasError()) {
+ if (reportedErrors) {
+ const auto v4Error = cg.error();
+ QQmlError error;
+ error.setUrl(cg.url());
+ error.setLine(v4Error.line);
+ error.setColumn(v4Error.column);
+ error.setDescription(v4Error.message);
+ reportedErrors->append(error);
+ }
return nullptr;
}
@@ -219,8 +227,9 @@ Script *Script::createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlCo
QQmlMetaType::CachedUnitLookupError cacheError = QQmlMetaType::CachedUnitLookupError::NoError;
if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(originalUrl, &cacheError)) {
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> jsUnit;
- jsUnit.adopt(new QV4::CompiledData::CompilationUnit(cachedUnit));
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> jsUnit
+ = QV4::ExecutableCompilationUnit::create(
+ QV4::CompiledData::CompilationUnit(cachedUnit));
return new QV4::Script(engine, qmlContext, jsUnit);
}
@@ -237,7 +246,6 @@ Script *Script::createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlCo
QByteArray data = f.readAll();
QString sourceCode = QString::fromUtf8(data);
- QmlIR::Document::removeScriptPragmas(sourceCode);
auto result = new QV4::Script(engine, qmlContext, /*parseAsBinding*/false, sourceCode, originalUrl.toString());
result->contextType = QV4::Compiler::ContextType::ScriptImportedByQML;
diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h
index a1e9b83a8b..aecedea701 100644
--- a/src/qml/jsruntime/qv4script_p.h
+++ b/src/qml/jsruntime/qv4script_p.h
@@ -80,7 +80,7 @@ struct Q_QML_EXPORT Script {
if (qml)
qmlContext.set(engine, *qml);
}
- Script(ExecutionEngine *engine, QmlContext *qml, const QQmlRefPointer<CompiledData::CompilationUnit> &compilationUnit);
+ Script(ExecutionEngine *engine, QmlContext *qml, const QQmlRefPointer<ExecutableCompilationUnit> &compilationUnit);
~Script();
QString sourceFile;
int line;
@@ -92,7 +92,7 @@ struct Q_QML_EXPORT Script {
bool parsed;
QV4::Compiler::ContextType contextType = QV4::Compiler::ContextType::Eval;
QV4::PersistentValue qmlContext;
- QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit;
+ QQmlRefPointer<ExecutableCompilationUnit> compilationUnit;
Function *vmFunction;
bool parseAsBinding;
@@ -101,8 +101,10 @@ struct Q_QML_EXPORT Script {
Function *function();
- static QQmlRefPointer<CompiledData::CompilationUnit> precompile(QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, Compiler::JSUnitGenerator *unitGenerator,
- const QString &fileName, const QString &finalUrl, const QString &source,
+ static QV4::CompiledData::CompilationUnit precompile(
+ QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine,
+ Compiler::JSUnitGenerator *unitGenerator, const QString &fileName,
+ const QString &finalUrl, const QString &source,
QList<QQmlError> *reportedErrors = nullptr,
QV4::Compiler::ContextType contextType = QV4::Compiler::ContextType::Global);
static Script *createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlContext, const QString &fileName, const QUrl &originalUrl, QString *error);
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index 1eef12a491..77a98247ac 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -49,8 +49,10 @@
#include "qv4runtime_p.h"
#include "qv4objectiterator_p.h"
#include <private/qqmlvaluetypewrapper_p.h>
+#if QT_CONFIG(qml_itemmodel)
#include <private/qqmlmodelindexvaluetype_p.h>
#include <QtCore/qabstractitemmodel.h>
+#endif
#include <algorithm>
@@ -75,6 +77,16 @@ static void generateWarning(QV4::ExecutionEngine *v4, const QString& description
}
// F(elementType, elementTypeName, sequenceType, defaultValue)
+#if QT_CONFIG(qml_itemmodel)
+#define FOREACH_QML_SEQUENCE_TYPE_FOR_ITEMMODEL(F) \
+ F(QModelIndex, QModelIndex, QModelIndexList, QModelIndex()) \
+ F(QModelIndex, QModelIndexVector, QVector<QModelIndex>, QModelIndex()) \
+ F(QModelIndex, QModelIndexStdVector, std::vector<QModelIndex>, QModelIndex()) \
+ F(QItemSelectionRange, QItemSelectionRange, QItemSelection, QItemSelectionRange())
+#else
+#define FOREACH_QML_SEQUENCE_TYPE_FOR_ITEMMODEL(F)
+#endif
+
#define FOREACH_QML_SEQUENCE_TYPE(F) \
F(int, IntVector, QVector<int>, 0) \
F(qreal, RealVector, QVector<qreal>, 0.0) \
@@ -92,10 +104,7 @@ static void generateWarning(QV4::ExecutionEngine *v4, const QString& description
F(QUrl, Url, QList<QUrl>, QUrl()) \
F(QUrl, UrlVector, QVector<QUrl>, QUrl()) \
F(QUrl, UrlStdVector, std::vector<QUrl>, QUrl()) \
- F(QModelIndex, QModelIndex, QModelIndexList, QModelIndex()) \
- F(QModelIndex, QModelIndexVector, QVector<QModelIndex>, QModelIndex()) \
- F(QModelIndex, QModelIndexStdVector, std::vector<QModelIndex>, QModelIndex()) \
- F(QItemSelectionRange, QItemSelectionRange, QItemSelection, QItemSelectionRange())
+ FOREACH_QML_SEQUENCE_TYPE_FOR_ITEMMODEL(F)
static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QString &element)
{
@@ -112,6 +121,7 @@ static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, co
return engine->newString(element.toString())->asReturnedValue();
}
+#if QT_CONFIG(qml_itemmodel)
static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QModelIndex &element)
{
const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(QMetaType::QModelIndex);
@@ -124,6 +134,7 @@ static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, co
const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(metaTypeId);
return QV4::QQmlValueTypeWrapper::create(engine, QVariant::fromValue(element), vtmo, metaTypeId);
}
+#endif
static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *, qreal element)
{
@@ -150,6 +161,7 @@ static QString convertElementToString(const QUrl &element)
return element.toString();
}
+#if QT_CONFIG(qml_itemmodel)
static QString convertElementToString(const QModelIndex &element)
{
return reinterpret_cast<const QQmlModelIndexValueType *>(&element)->toString();
@@ -159,6 +171,7 @@ static QString convertElementToString(const QItemSelectionRange &element)
{
return reinterpret_cast<const QQmlItemSelectionRangeValueType *>(&element)->toString();
}
+#endif
static QString convertElementToString(qreal element)
{
@@ -192,6 +205,7 @@ template <> QUrl convertValueToElement(const Value &value)
return QUrl(value.toQString());
}
+#if QT_CONFIG(qml_itemmodel)
template <> QModelIndex convertValueToElement(const Value &value)
{
const QQmlValueTypeWrapper *v = value.as<QQmlValueTypeWrapper>();
@@ -207,6 +221,7 @@ template <> QItemSelectionRange convertValueToElement(const Value &value)
return v->toVariant().value<QItemSelectionRange>();
return QItemSelectionRange();
}
+#endif
template <> qreal convertValueToElement(const Value &value)
{
@@ -667,6 +682,7 @@ typedef QQmlSequence<QVector<QUrl> > QQmlUrlVectorList;
DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlUrlVectorList);
typedef QQmlSequence<std::vector<QUrl> > QQmlUrlStdVectorList;
DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlUrlStdVectorList);
+#if QT_CONFIG(qml_itemmodel)
typedef QQmlSequence<QModelIndexList> QQmlQModelIndexList;
DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQModelIndexList);
typedef QQmlSequence<QVector<QModelIndex> > QQmlQModelIndexVectorList;
@@ -675,6 +691,7 @@ typedef QQmlSequence<std::vector<QModelIndex> > QQmlQModelIndexStdVectorList;
DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQModelIndexStdVectorList);
typedef QQmlSequence<QItemSelection> QQmlQItemSelectionRangeList;
DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQItemSelectionRangeList);
+#endif
typedef QQmlSequence<QList<bool> > QQmlBoolList;
DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlBoolList);
typedef QQmlSequence<QList<qreal> > QQmlRealList;
diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h
index da71215bed..886dfaa521 100644
--- a/src/qml/jsruntime/qv4sequenceobject_p.h
+++ b/src/qml/jsruntime/qv4sequenceobject_p.h
@@ -65,7 +65,7 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
-struct SequencePrototype : public QV4::Object
+struct Q_QML_PRIVATE_EXPORT SequencePrototype : public QV4::Object
{
V4_PROTOTYPE(arrayPrototype)
void init();
diff --git a/src/qml/jsruntime/qv4setobject.cpp b/src/qml/jsruntime/qv4setobject.cpp
index 088ecbe30d..1664d1bd71 100644
--- a/src/qml/jsruntime/qv4setobject.cpp
+++ b/src/qml/jsruntime/qv4setobject.cpp
@@ -76,7 +76,7 @@ ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv,
ScopedFunctionObject adder(scope, a->get(ScopedString(scope, scope.engine->newString(QString::fromLatin1("add")))));
if (!adder)
return scope.engine->throwTypeError();
- ScopedObject iter(scope, Runtime::method_getIterator(scope.engine, iterable, true));
+ ScopedObject iter(scope, Runtime::GetIterator::call(scope.engine, iterable, true));
CHECK_EXCEPTION();
if (!iter)
return a.asReturnedValue();
@@ -84,7 +84,7 @@ ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv,
Value *nextValue = scope.alloc(1);
ScopedValue done(scope);
forever {
- done = Runtime::method_iteratorNext(scope.engine, iter, nextValue);
+ done = Runtime::IteratorNext::call(scope.engine, iter, nextValue);
CHECK_EXCEPTION();
if (done->toBoolean())
return a.asReturnedValue();
@@ -92,7 +92,7 @@ ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv,
adder->call(a, nextValue, 1);
if (scope.engine->hasException) {
ScopedValue falsey(scope, Encode(false));
- return Runtime::method_iteratorClose(scope.engine, iter, falsey);
+ return Runtime::IteratorClose::call(scope.engine, iter, falsey);
}
}
}
diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h
index 44cfef9173..616fa9a5a9 100644
--- a/src/qml/jsruntime/qv4stackframe_p.h
+++ b/src/qml/jsruntime/qv4stackframe_p.h
@@ -52,63 +52,13 @@
#include <private/qv4context_p.h>
#include <private/qv4enginebase_p.h>
-#ifndef V4_BOOTSTRAP
+#include <private/qv4calldata_p.h>
#include <private/qv4function_p.h>
-#endif
QT_BEGIN_NAMESPACE
namespace QV4 {
-struct CallData
-{
- enum Offsets {
- Function = 0,
- Context = 1,
- Accumulator = 2,
- This = 3,
- NewTarget = 4,
- Argc = 5,
-
- LastOffset = Argc,
- OffsetCount = LastOffset + 1
- };
-
- Value function;
- Value context;
- Value accumulator;
- Value thisObject;
- Value newTarget;
- Value _argc;
-
- int argc() const {
- Q_ASSERT(_argc.isInteger());
- return _argc.int_32();
- }
-
- void setArgc(int argc) {
- Q_ASSERT(argc >= 0);
- _argc.setInt_32(argc);
- }
-
- inline ReturnedValue argument(int i) const {
- return i < argc() ? args[i].asReturnedValue() : Value::undefinedValue().asReturnedValue();
- }
-
- Value args[1];
-
- static Q_DECL_CONSTEXPR int HeaderSize() { return offsetof(CallData, args) / sizeof(QV4::Value); }
-};
-
-Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value);
-Q_STATIC_ASSERT(offsetof(CallData, function ) == CallData::Function * sizeof(Value));
-Q_STATIC_ASSERT(offsetof(CallData, context ) == CallData::Context * sizeof(Value));
-Q_STATIC_ASSERT(offsetof(CallData, accumulator) == CallData::Accumulator * sizeof(Value));
-Q_STATIC_ASSERT(offsetof(CallData, thisObject ) == CallData::This * sizeof(Value));
-Q_STATIC_ASSERT(offsetof(CallData, newTarget ) == CallData::NewTarget * sizeof(Value));
-Q_STATIC_ASSERT(offsetof(CallData, _argc ) == CallData::Argc * sizeof(Value));
-Q_STATIC_ASSERT(offsetof(CallData, args ) == 6 * sizeof(Value));
-
struct Q_QML_EXPORT CppStackFrame {
EngineBase *engine;
Value *savedStackTop;
@@ -155,7 +105,6 @@ struct Q_QML_EXPORT CppStackFrame {
engine->jsStackTop = savedStackTop;
}
-#ifndef V4_BOOTSTRAP
static uint requiredJSStackFrameSize(uint nRegisters) {
return CallData::HeaderSize() + nRegisters;
}
@@ -168,7 +117,7 @@ struct Q_QML_EXPORT CppStackFrame {
void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope,
const Value &thisObject, const Value &newTarget = Value::undefinedValue()) {
setupJSFrame(stackSpace, function, scope, thisObject, newTarget,
- v4Function->nFormals, v4Function->compiledFunction->nRegisters);
+ v4Function->compiledFunction->nFormals, v4Function->compiledFunction->nRegisters);
}
void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope,
const Value &thisObject, const Value &newTarget, uint nFormals, uint nRegisters)
@@ -198,7 +147,6 @@ struct Q_QML_EXPORT CppStackFrame {
*v = Value::emptyValue().asReturnedValue();
}
}
-#endif
QString source() const;
QString function() const;
diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp
index 68d65f2e24..223f4a4769 100644
--- a/src/qml/jsruntime/qv4string.cpp
+++ b/src/qml/jsruntime/qv4string.cpp
@@ -39,19 +39,15 @@
#include "qv4string_p.h"
#include "qv4value_p.h"
-#ifndef V4_BOOTSTRAP
#include "qv4identifiertable_p.h"
#include "qv4runtime_p.h"
#include "qv4objectproto_p.h"
#include "qv4stringobject_p.h"
-#endif
#include <QtCore/QHash>
#include <QtCore/private/qnumeric_p.h>
using namespace QV4;
-#ifndef V4_BOOTSTRAP
-
void Heap::StringOrSymbol::markObjects(Heap::Base *that, MarkStack *markStack)
{
StringOrSymbol *s = static_cast<StringOrSymbol *>(that);
@@ -254,10 +250,3 @@ qint64 String::virtualGetLength(const Managed *m)
{
return static_cast<const String *>(m)->d()->length();
}
-
-#endif // V4_BOOTSTRAP
-
-uint String::toArrayIndex(const QString &str)
-{
- return QV4::String::toArrayIndex(str.constData(), str.constData() + str.length());
-}
diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h
index fbd4f5f550..52fe09cd72 100644
--- a/src/qml/jsruntime/qv4string_p.h
+++ b/src/qml/jsruntime/qv4string_p.h
@@ -54,6 +54,7 @@
#include "qv4managed_p.h"
#include <QtCore/private/qnumeric_p.h>
#include "qv4enginebase_p.h"
+#include <private/qv4stringtoarrayindex_p.h>
QT_BEGIN_NAMESPACE
@@ -104,7 +105,6 @@ struct Q_QML_PRIVATE_EXPORT StringOrSymbol : Base
struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol {
static void markObjects(Heap::Base *that, MarkStack *markStack);
-#ifndef V4_BOOTSTRAP
const VTable *vtable() const {
return internalClass->vtable;
}
@@ -140,11 +140,9 @@ struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol {
private:
static void append(const String *data, QChar *ch);
-#endif
};
Q_STATIC_ASSERT(std::is_trivial< String >::value);
-#ifndef V4_BOOTSTRAP
struct ComplexString : String {
void init(String *l, String *n);
void init(String *ref, int from, int len);
@@ -162,12 +160,10 @@ inline
int String::length() const {
return text ? text->size : static_cast<const ComplexString *>(this)->len;
}
-#endif
}
struct Q_QML_PRIVATE_EXPORT StringOrSymbol : public Managed {
-#ifndef V4_BOOTSTRAP
V4_MANAGED(StringOrSymbol, Managed)
V4_NEEDS_DESTROY
enum {
@@ -184,11 +180,9 @@ public:
inline QString toQString() const {
return d()->toQString();
}
-#endif
};
struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol {
-#ifndef V4_BOOTSTRAP
V4_MANAGED(String, StringOrSymbol)
Q_MANAGED_TYPE(String)
V4_INTERNALCLASS(String)
@@ -239,43 +233,13 @@ struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol {
protected:
static bool virtualIsEqualTo(Managed *that, Managed *o);
static qint64 virtualGetLength(const Managed *m);
-#endif
-
-public:
- static uint toArrayIndex(const QString &str);
-
-private:
- static inline uint toUInt(const QChar *ch) { return ch->unicode(); }
- static inline uint toUInt(const char *ch) { return static_cast<unsigned char>(*ch); }
-
- template <typename T>
- static inline uint toArrayIndex(const T *ch, const T *end)
- {
- uint i = toUInt(ch) - '0';
- if (i > 9)
- return UINT_MAX;
- ++ch;
- // reject "01", "001", ...
- if (i == 0 && ch != end)
- return UINT_MAX;
-
- while (ch < end) {
- uint x = toUInt(ch) - '0';
- if (x > 9)
- return UINT_MAX;
- if (mul_overflow(i, uint(10), &i) || add_overflow(i, x, &i)) // i = i * 10 + x
- return UINT_MAX;
- ++ch;
- }
- return i;
- }
public:
template <typename T>
static inline uint calculateHashValue(const T *ch, const T* end, uint *subtype)
{
// array indices get their number as hash value
- uint h = toArrayIndex(ch, end);
+ uint h = stringToArrayIndex(ch, end);
if (h != UINT_MAX) {
if (subtype)
*subtype = Heap::StringOrSymbol::StringType_ArrayIndex;
@@ -283,17 +247,16 @@ public:
}
while (ch < end) {
- h = 31 * h + toUInt(ch);
+ h = 31 * h + charToUInt(ch);
++ch;
}
if (subtype)
- *subtype = (toUInt(ch) == '@') ? Heap::StringOrSymbol::StringType_Symbol : Heap::StringOrSymbol::StringType_Regular;
+ *subtype = (charToUInt(ch) == '@') ? Heap::StringOrSymbol::StringType_Symbol : Heap::StringOrSymbol::StringType_Regular;
return h;
}
};
-#ifndef V4_BOOTSTRAP
struct ComplexString : String {
typedef QV4::Heap::ComplexString Data;
QV4::Heap::ComplexString *d_unchecked() const { return static_cast<QV4::Heap::ComplexString *>(m()); }
@@ -332,8 +295,6 @@ inline ReturnedValue value_convert<String>(ExecutionEngine *e, const Value &v)
{
return v.toString(e)->asReturnedValue();
}
-#endif
-
}
diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp
index 6afa6d36d6..9b4a2d575e 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -45,7 +45,7 @@
#include <private/qv4mm_p.h>
#include "qv4scopedvalue_p.h"
#include "qv4symbol_p.h"
-#include "qv4alloca_p.h"
+#include <private/qv4alloca_p.h>
#include "qv4jscall_p.h"
#include "qv4stringiterator_p.h"
#include <QtCore/QDateTime>
@@ -152,13 +152,14 @@ PropertyAttributes StringObject::virtualGetOwnProperty(const Managed *m, Propert
if (attributes != Attr_Invalid)
return attributes;
- const StringObject *s = static_cast<const StringObject *>(m);
- uint slen = s->d()->string->toQString().length();
- uint index = id.asArrayIndex();
- if (index < slen) {
- if (p)
- p->value = s->getIndex(index);
- return Attr_NotConfigurable|Attr_NotWritable;
+ if (id.isArrayIndex()) {
+ const uint index = id.asArrayIndex();
+ const auto s = static_cast<const StringObject *>(m);
+ if (index < uint(s->d()->string->toQString().length())) {
+ if (p)
+ p->value = s->getIndex(index);
+ return Attr_NotConfigurable|Attr_NotWritable;
+ }
}
return Object::virtualGetOwnProperty(m, id, p);
}
diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp
index faf7934c06..7d33167762 100644
--- a/src/qml/jsruntime/qv4typedarray.cpp
+++ b/src/qml/jsruntime/qv4typedarray.cpp
@@ -214,7 +214,7 @@ template <typename T>
ReturnedValue atomicLoad(char *data)
{
typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
- T val = QAtomicOps<T>::load(*mem);
+ T val = QAtomicOps<T>::loadRelaxed(*mem);
return typeToValue(val);
}
@@ -223,7 +223,7 @@ ReturnedValue atomicStore(char *data, Value v)
{
T value = valueToType<T>(v);
typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
- QAtomicOps<T>::store(*mem, value);
+ QAtomicOps<T>::storeRelaxed(*mem, value);
return typeToValue(value);
}
@@ -459,24 +459,23 @@ Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type
ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
- uint index = id.asArrayIndex();
- if (index == UINT_MAX && !id.isCanonicalNumericIndexString())
+ const bool isArrayIndex = id.isArrayIndex();
+ if (!isArrayIndex && !id.isCanonicalNumericIndexString())
return Object::virtualGet(m, id, receiver, hasProperty);
- // fall through, with index == UINT_MAX it'll do the right thing.
Scope scope(static_cast<const Object *>(m)->engine());
Scoped<TypedArray> a(scope, static_cast<const TypedArray *>(m));
if (a->d()->buffer->isDetachedBuffer())
return scope.engine->throwTypeError();
- if (index >= a->length()) {
+ if (!isArrayIndex || id.asArrayIndex() >= a->length()) {
if (hasProperty)
*hasProperty = false;
return Encode::undefined();
}
uint bytesPerElement = a->d()->type->bytesPerElement;
- uint byteOffset = a->d()->byteOffset + index * bytesPerElement;
+ uint byteOffset = a->d()->byteOffset + id.asArrayIndex() * bytesPerElement;
Q_ASSERT(byteOffset + bytesPerElement <= (uint)a->d()->buffer->byteLength());
if (hasProperty)
@@ -486,27 +485,22 @@ ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Val
bool TypedArray::virtualHasProperty(const Managed *m, PropertyKey id)
{
- uint index = id.asArrayIndex();
- if (index == UINT_MAX && !id.isCanonicalNumericIndexString())
+ const bool isArrayIndex = id.isArrayIndex();
+ if (!isArrayIndex && !id.isCanonicalNumericIndexString())
return Object::virtualHasProperty(m, id);
- // fall through, with index == UINT_MAX it'll do the right thing.
const TypedArray *a = static_cast<const TypedArray *>(m);
if (a->d()->buffer->isDetachedBuffer()) {
a->engine()->throwTypeError();
return false;
}
- if (index >= a->length())
- return false;
- return true;
+ return isArrayIndex && id.asArrayIndex() < a->length();
}
PropertyAttributes TypedArray::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
{
- uint index = id.asArrayIndex();
- if (index == UINT_MAX && !id.isCanonicalNumericIndexString())
+ if (!id.isArrayIndex() && !id.isCanonicalNumericIndexString())
return Object::virtualGetOwnProperty(m, id, p);
- // fall through, with index == UINT_MAX it'll do the right thing.
bool hasProperty = false;
ReturnedValue v = virtualGet(m, id, m, &hasProperty);
@@ -517,10 +511,9 @@ PropertyAttributes TypedArray::virtualGetOwnProperty(const Managed *m, PropertyK
bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
- uint index = id.asArrayIndex();
- if (index == UINT_MAX && !id.isCanonicalNumericIndexString())
+ const bool isArrayIndex = id.isArrayIndex();
+ if (!isArrayIndex && !id.isCanonicalNumericIndexString())
return Object::virtualPut(m, id, value, receiver);
- // fall through, with index == UINT_MAX it'll do the right thing.
ExecutionEngine *v4 = static_cast<Object *>(m)->engine();
if (v4->hasException)
@@ -531,6 +524,10 @@ bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Valu
if (a->d()->buffer->isDetachedBuffer())
return scope.engine->throwTypeError();
+ if (!isArrayIndex)
+ return false;
+
+ const uint index = id.asArrayIndex();
if (index >= a->length())
return false;
@@ -547,11 +544,12 @@ bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Valu
bool TypedArray::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs)
{
- uint index = id.asArrayIndex();
- if (index == UINT_MAX && !id.isCanonicalNumericIndexString())
- return Object::virtualDefineOwnProperty(m, id, p, attrs);
- // fall through, with index == UINT_MAX it'll do the right thing.
+ if (!id.isArrayIndex()) {
+ return !id.isCanonicalNumericIndexString()
+ && Object::virtualDefineOwnProperty(m, id, p, attrs);
+ }
+ const uint index = id.asArrayIndex();
TypedArray *a = static_cast<TypedArray *>(m);
if (index >= a->length() || attrs.isAccessor())
return false;
@@ -1595,7 +1593,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_toLocaleString(const Function
R += separator;
v = instance->get(k);
- v = Runtime::method_callElement(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0);
+ v = Runtime::CallElement::call(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0);
s = v->toString(scope.engine);
if (scope.hasException())
return Encode::undefined();
diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp
index cbc153bb86..d29b060b9e 100644
--- a/src/qml/jsruntime/qv4value.cpp
+++ b/src/qml/jsruntime/qv4value.cpp
@@ -36,16 +36,14 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-#include <qv4engine_p.h>
+
#include <qv4runtime_p.h>
-#include <qv4string_p.h>
#include <qv4propertykey_p.h>
-#ifndef V4_BOOTSTRAP
+#include <qv4string_p.h>
#include <qv4symbol_p.h>
#include <qv4object_p.h>
#include <qv4objectproto_p.h>
#include <private/qv4mm_p.h>
-#endif
#include <wtf/MathExtras.h>
@@ -83,12 +81,8 @@ bool Value::toBooleanImpl(Value val)
Heap::Base *b = val.m();
if (!b)
return false;
-#ifdef V4_BOOTSTRAP
- Q_UNIMPLEMENTED();
-#else
if (b->internalClass->vtable->isString)
return static_cast<Heap::String *>(b)->length() > 0;
-#endif
return true;
}
@@ -103,10 +97,6 @@ double Value::toNumberImpl(Value val)
case QV4::Value::Undefined_Type:
return std::numeric_limits<double>::quiet_NaN();
case QV4::Value::Managed_Type:
-#ifdef V4_BOOTSTRAP
- Q_UNIMPLEMENTED();
- Q_FALLTHROUGH();
-#else
if (String *s = val.stringValue())
return RuntimeHelpers::stringToNumber(s->toQString());
if (val.isSymbol()) {
@@ -114,16 +104,15 @@ double Value::toNumberImpl(Value val)
m.engine()->throwTypeError();
return 0;
}
- {
- Q_ASSERT(val.isObject());
- Scope scope(val.objectValue()->engine());
- ScopedValue protectThis(scope, val);
- ScopedValue prim(scope, RuntimeHelpers::toPrimitive(val, NUMBER_HINT));
+ {
+ Q_ASSERT(val.isObject());
+ Scope scope(val.objectValue()->engine());
+ ScopedValue protectThis(scope, val);
+ ScopedValue prim(scope, RuntimeHelpers::toPrimitive(val, NUMBER_HINT));
if (scope.engine->hasException)
return 0;
return prim->toNumber();
}
-#endif
case QV4::Value::Null_Type:
case QV4::Value::Boolean_Type:
case QV4::Value::Integer_Type:
@@ -133,7 +122,6 @@ double Value::toNumberImpl(Value val)
}
}
-#ifndef V4_BOOTSTRAP
QString Value::toQStringNoThrow() const
{
switch (type()) {
@@ -248,7 +236,6 @@ QV4::PropertyKey Value::toPropertyKey(ExecutionEngine *e) const
ScopedStringOrSymbol s(scope, v);
return s->toPropertyKey();
}
-#endif // V4_BOOTSTRAP
bool Value::sameValue(Value other) const {
if (_val == other._val)
@@ -285,7 +272,6 @@ bool Value::sameValueZero(Value other) const {
return false;
}
-#ifndef V4_BOOTSTRAP
Heap::String *Value::toString(ExecutionEngine *e, Value val)
{
return RuntimeHelpers::convertToString(e, val);
@@ -327,4 +313,3 @@ uint Value::asArrayLength(bool *ok) const
}
return idx;
}
-#endif // V4_BOOTSTRAP
diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h
index ce85e48b72..4e901721cb 100644
--- a/src/qml/jsruntime/qv4value_p.h
+++ b/src/qml/jsruntime/qv4value_p.h
@@ -57,8 +57,10 @@
#include "qv4global_p.h"
#include <private/qv4heap_p.h>
#include <private/qv4internalclass_p.h>
+#include <private/qv4staticvalue_p.h>
#include <private/qnumeric_p.h>
+#include <private/qv4calldata_p.h>
QT_BEGIN_NAMESPACE
@@ -68,84 +70,23 @@ namespace Heap {
struct Base;
}
-struct Q_QML_PRIVATE_EXPORT Value
+struct Q_QML_PRIVATE_EXPORT Value : public StaticValue
{
- /*
- We use 8 bytes for a value and a different variant of NaN boxing. A Double
- NaN (actually -qNaN) is indicated by a number that has the top 13 bits set, and for a
- signalling NaN it is the top 14 bits. The other values are usually set to 0 by the
- processor, and are thus free for us to store other data. We keep pointers in there for
- managed objects, and encode the other types using the free space given to use by the unused
- bits for NaN values. This also works for pointers on 64 bit systems, as they all currently
- only have 48 bits of addressable memory. (Note: we do leave the lower 49 bits available for
- pointers.)
-
- We xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will
- get encoded with bits 63-49 all set to 0. We then use bit 48 to distinguish between
- managed/undefined (0), or Null/Int/Bool/Empty (1). So, storing a 49 bit pointer will leave
- the top 15 bits 0, which is exactly the 'natural' representation of pointers. If bit 49 is
- set, bit 48 indicates Empty (0) or integer-convertible (1). Then the 3 bit below that are
- used to encode Null/Int/Bool.
-
- Undefined is encoded as a managed pointer with value 0. This is the same as a nullptr.
-
- Specific bit-sequences:
- 0 = always 0
- 1 = always 1
- x = stored value
- a,b,c,d = specific bit values, see notes
-
- 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 |
- 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value
- ------------------------------------------------------------------------+--------------
- 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined
- 00000000 0000000x xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer)
- a0000000 0000bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf
- dddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double
- 00000000 00000010 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole)
- 00000000 00000010 10000000 00000000 00000000 00000000 00000000 00000000 | Null
- 00000000 00000011 00000000 00000000 00000000 00000000 00000000 0000000x | Bool
- 00000000 00000011 10000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int
-
- Notes:
- - a: xor-ed signbit, always 1 for NaN
- - bc, xor-ed values: 11 = inf, 10 = sNaN, 01 = qNaN, 00 = boxed value
- - d: xor-ed bits, where at least one bit is set, so: (val >> (64-14)) > 0
- - Undefined maps to C++ nullptr, so the "default" initialization is the same for both C++
- and JS
- - Managed has the left 15 bits set to 0, so: (val >> (64-15)) == 0
- - empty, Null, Bool, and Int have the left 14 bits set to 0, and bit 49 set to 1,
- so: (val >> (64-15)) == 1
- - Null, Bool, and Int have bit 48 set, indicating integer-convertible
- - xoring _val with NaNEncodeMask will convert to a double in "natural" representation, where
- any non double results in a NaN
- - on 32bit we can use the fact that addresses are 32bits wide, so the tag part (bits 32 to
- 63) are zero. No need to shift.
- */
-
- quint64 _val;
-
- QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 &rawValueRef() { return _val; }
- QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 rawValue() const { return _val; }
- QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setRawValue(quint64 raw) { _val = raw; }
-
-#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
- static inline int valueOffset() { return 0; }
- static inline int tagOffset() { return 4; }
-#else // !Q_LITTLE_ENDIAN
- static inline int valueOffset() { return 4; }
- static inline int tagOffset() { return 0; }
-#endif
- static inline constexpr quint64 tagValue(quint32 tag, quint32 value) { return quint64(tag) << 32 | value; }
- QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; }
- QML_NEARLY_ALWAYS_INLINE constexpr quint32 value() const { return _val & quint64(~quint32(0)); }
- QML_NEARLY_ALWAYS_INLINE constexpr quint32 tag() const { return _val >> 32; }
- QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTag(quint32 tag) { setTagValue(tag, value()); }
+ using HeapBasePtr = Heap::Base *;
+ using ManagedPtr = Managed *;
+
+ Value() = default;
+ constexpr Value(quint64 val) : StaticValue(val) {}
+
+ static constexpr Value fromStaticValue(StaticValue staticValue)
+ {
+ return {staticValue._val};
+ }
#if QT_POINTER_SIZE == 8
- QML_NEARLY_ALWAYS_INLINE Heap::Base *m() const
+ QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const
{
- Heap::Base *b;
+ HeapBasePtr b;
#ifdef __ia64
// Restore bits 49-47 to bits 63-61, undoing the workaround explained in
// setM below.
@@ -159,7 +100,7 @@ struct Q_QML_PRIVATE_EXPORT Value
#endif
return b;
}
- QML_NEARLY_ALWAYS_INLINE void setM(Heap::Base *b)
+ QML_NEARLY_ALWAYS_INLINE void setM(HeapBasePtr b)
{
memcpy(&_val, &b, 8);
#ifdef __ia64
@@ -171,15 +112,15 @@ struct Q_QML_PRIVATE_EXPORT Value
#endif
}
#elif QT_POINTER_SIZE == 4
- QML_NEARLY_ALWAYS_INLINE Heap::Base *m() const
+ QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const
{
- Q_STATIC_ASSERT(sizeof(Heap::Base*) == sizeof(quint32));
- Heap::Base *b;
+ Q_STATIC_ASSERT(sizeof(HeapBasePtr) == sizeof(quint32));
+ HeapBasePtr b;
quint32 v = value();
memcpy(&b, &v, 4);
return b;
}
- QML_NEARLY_ALWAYS_INLINE void setM(Heap::Base *b)
+ QML_NEARLY_ALWAYS_INLINE void setM(HeapBasePtr b)
{
quint32 v;
memcpy(&v, &b, 4);
@@ -189,190 +130,11 @@ struct Q_QML_PRIVATE_EXPORT Value
# error "unsupported pointer size"
#endif
- QML_NEARLY_ALWAYS_INLINE constexpr int int_32() const
- {
- return int(value());
- }
- QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setInt_32(int i)
- {
- setTagValue(quint32(ValueTypeInternal::Integer), quint32(i));
- }
- QML_NEARLY_ALWAYS_INLINE uint uint_32() const { return value(); }
-
- QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setEmpty()
- {
- setTagValue(quint32(ValueTypeInternal::Empty), 0);
- }
-
- // ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible
- // and use negative numbers here
- enum QuickType {
- QT_ManagedOrUndefined = 0,
- QT_ManagedOrUndefined1 = 1,
- QT_ManagedOrUndefined2 = 2,
- QT_ManagedOrUndefined3 = 3,
- QT_Empty = 4,
- QT_Null = 5,
- QT_Bool = 6,
- QT_Int = 7
- // all other values are doubles
- };
-
- enum Type {
- Undefined_Type = 0,
- Managed_Type = 1,
- Empty_Type = 4,
- Null_Type = 5,
- Boolean_Type = 6,
- Integer_Type = 7,
- Double_Type = 8
- };
-
- inline Type type() const {
- int t = quickType();
- if (t < QT_Empty)
- return _val ? Managed_Type : Undefined_Type;
- if (t > QT_Int)
- return Double_Type;
- return static_cast<Type>(t);
- }
-
- // Shared between 32-bit and 64-bit encoding
- enum {
- Tag_Shift = 32
- };
-
- // Used only by 64-bit encoding
- static const quint64 NaNEncodeMask = 0xfffc000000000000ull;
- enum {
- IsDouble_Shift = 64-14,
- IsManagedOrUndefined_Shift = 64-15,
- IsIntegerConvertible_Shift = 64-15,
- IsIntegerOrBool_Shift = 64-16,
- QuickType_Shift = 64 - 17,
- IsPositiveIntShift = 31
- };
-
- static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49
-
- enum class ValueTypeInternal_64 {
- Empty = Immediate_Mask_64 | 0,
- Null = Immediate_Mask_64 | 0x08000u,
- Boolean = Immediate_Mask_64 | 0x10000u,
- Integer = Immediate_Mask_64 | 0x18000u
- };
-
- // Used only by 32-bit encoding
- enum Masks {
- SilentNaNBit = 0x00040000,
- NotDouble_Mask = 0x7ffa0000,
- };
- static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit;
-
- enum class ValueTypeInternal_32 {
- Empty = Immediate_Mask_32 | 0,
- Null = Immediate_Mask_32 | 0x08000u,
- Boolean = Immediate_Mask_32 | 0x10000u,
- Integer = Immediate_Mask_32 | 0x18000u
- };
-
- enum {
- Managed_Type_Internal = 0
- };
-
- using ValueTypeInternal = ValueTypeInternal_64;
-
- enum {
- NaN_Mask = 0x7ff80000,
- };
-
- inline quint64 quickType() const { return (_val >> QuickType_Shift); }
-
- // used internally in property
- inline bool isEmpty() const { return tag() == quint32(ValueTypeInternal::Empty); }
- inline bool isNull() const { return tag() == quint32(ValueTypeInternal::Null); }
- inline bool isBoolean() const { return tag() == quint32(ValueTypeInternal::Boolean); }
- inline bool isInteger() const { return tag() == quint32(ValueTypeInternal::Integer); }
- inline bool isNullOrUndefined() const { return isNull() || isUndefined(); }
- inline bool isNumber() const { return quickType() >= QT_Int; }
-
- inline bool isUndefined() const { return _val == 0; }
- inline bool isDouble() const { return (_val >> IsDouble_Shift); }
- inline bool isManaged() const { return _val && ((_val >> IsManagedOrUndefined_Shift) == 0); }
- inline bool isManagedOrUndefined() const { return ((_val >> IsManagedOrUndefined_Shift) == 0); }
-
- inline bool isIntOrBool() const {
- return (_val >> IsIntegerOrBool_Shift) == 3;
- }
-
- inline bool integerCompatible() const {
- Q_ASSERT(!isEmpty());
- return (_val >> IsIntegerConvertible_Shift) == 1;
- }
- static inline bool integerCompatible(Value a, Value b) {
- return a.integerCompatible() && b.integerCompatible();
- }
- static inline bool bothDouble(Value a, Value b) {
- return a.isDouble() && b.isDouble();
- }
- inline bool isNaN() const { return (tag() & 0x7ffc0000 ) == 0x00040000; }
-
- inline bool isPositiveInt() const {
-#if QT_POINTER_SIZE == 4
- return isInteger() && int_32() >= 0;
-#else
- return (_val >> IsPositiveIntShift) == (quint64(ValueTypeInternal::Integer) << 1);
-#endif
- }
-
- QML_NEARLY_ALWAYS_INLINE double doubleValue() const {
- Q_ASSERT(isDouble());
- double d;
- Value v = *this;
- v._val ^= NaNEncodeMask;
- memcpy(&d, &v._val, 8);
- return d;
- }
- QML_NEARLY_ALWAYS_INLINE void setDouble(double d) {
- if (qt_is_nan(d))
- d = qt_qnan();
- memcpy(&_val, &d, 8);
- _val ^= NaNEncodeMask;
- Q_ASSERT(isDouble());
- }
inline bool isString() const;
inline bool isStringOrSymbol() const;
inline bool isSymbol() const;
inline bool isObject() const;
inline bool isFunctionObject() const;
- inline bool isInt32() {
- if (tag() == quint32(ValueTypeInternal::Integer))
- return true;
- if (isDouble()) {
- double d = doubleValue();
- if (isInt32(d)) {
- setInt_32(int(d));
- return true;
- }
- }
- return false;
- }
- QML_NEARLY_ALWAYS_INLINE static bool isInt32(double d) {
- int i = int(d);
- return (i == d && !(d == 0 && std::signbit(d)));
- }
- double asDouble() const {
- if (tag() == quint32(ValueTypeInternal::Integer))
- return int_32();
- return doubleValue();
- }
-
- bool booleanValue() const {
- return int_32();
- }
- int integerValue() const {
- return int_32();
- }
QML_NEARLY_ALWAYS_INLINE String *stringValue() const {
if (!isString())
@@ -394,16 +156,16 @@ struct Q_QML_PRIVATE_EXPORT Value
return nullptr;
return reinterpret_cast<Object*>(const_cast<Value *>(this));
}
- QML_NEARLY_ALWAYS_INLINE Managed *managed() const {
+ QML_NEARLY_ALWAYS_INLINE ManagedPtr managed() const {
if (!isManaged())
return nullptr;
return reinterpret_cast<Managed*>(const_cast<Value *>(this));
}
- QML_NEARLY_ALWAYS_INLINE Heap::Base *heapObject() const {
+ QML_NEARLY_ALWAYS_INLINE Value::HeapBasePtr heapObject() const {
return isManagedOrUndefined() ? m() : nullptr;
}
- static inline Value fromHeapObject(Heap::Base *m)
+ static inline Value fromHeapObject(HeapBasePtr m)
{
Value v;
v.setM(m);
@@ -446,12 +208,6 @@ struct Q_QML_PRIVATE_EXPORT Value
static Heap::Object *toObject(ExecutionEngine *e, Value val);
inline bool isPrimitive() const;
- inline bool tryIntegerConversion() {
- bool b = integerCompatible();
- if (b)
- setTagValue(quint32(ValueTypeInternal::Integer), value());
- return b;
- }
template <typename T>
const T *as() const {
@@ -485,13 +241,12 @@ struct Q_QML_PRIVATE_EXPORT Value
return static_cast<const T *>(managed());
}
-#ifndef V4_BOOTSTRAP
uint asArrayLength(bool *ok) const;
-#endif
- ReturnedValue *data_ptr() { return &_val; }
- constexpr ReturnedValue asReturnedValue() const { return _val; }
- static Value fromReturnedValue(ReturnedValue val) { Value v; v._val = val; return v; }
+ static constexpr Value fromReturnedValue(ReturnedValue val)
+ {
+ return fromStaticValue(StaticValue::fromReturnedValue(val));
+ }
// As per ES specs
bool sameValue(Value other) const;
@@ -499,21 +254,45 @@ struct Q_QML_PRIVATE_EXPORT Value
inline void mark(MarkStack *markStack);
- inline static constexpr Value emptyValue() { return { tagValue(quint32(ValueTypeInternal::Empty), 0) }; }
- static inline constexpr Value fromBoolean(bool b) { return { tagValue(quint32(ValueTypeInternal::Boolean), b) }; }
- static inline constexpr Value fromInt32(int i) { return { tagValue(quint32(ValueTypeInternal::Integer), quint32(i)) }; }
- inline static constexpr Value undefinedValue() { return { 0 }; }
- static inline constexpr Value nullValue() { return { tagValue(quint32(ValueTypeInternal::Null), 0) }; }
- static inline Value fromDouble(double d);
- static inline Value fromUInt32(uint i);
-
- static double toInteger(double d);
- static int toInt32(double d);
- static unsigned int toUInt32(double d);
+ static double toInteger(double d) { return StaticValue::toInteger(d); }
+ static int toInt32(double d) { return StaticValue::toInt32(d); }
+ static unsigned int toUInt32(double d) { return StaticValue::toUInt32(d); }
+ inline static constexpr Value emptyValue()
+ {
+ return fromStaticValue(StaticValue::emptyValue());
+ }
+ static inline constexpr Value fromBoolean(bool b)
+ {
+ return fromStaticValue(StaticValue::fromBoolean(b));
+ }
+ static inline constexpr Value fromInt32(int i)
+ {
+ return fromStaticValue(StaticValue::fromInt32(i));
+ }
+ inline static constexpr Value undefinedValue()
+ {
+ return fromStaticValue(StaticValue::undefinedValue());
+ }
+ static inline constexpr Value nullValue()
+ {
+ return fromStaticValue(StaticValue::nullValue());
+ }
+ static inline Value fromDouble(double d)
+ {
+ return fromStaticValue(StaticValue::fromDouble(d));
+ }
+ static inline Value fromUInt32(uint i)
+ {
+ return fromStaticValue(StaticValue::fromUInt32(i));
+ }
Value &operator =(const ScopedValue &v);
- Value &operator=(ReturnedValue v) { _val = v; return *this; }
- Value &operator=(Managed *m) {
+ Value &operator=(ReturnedValue v)
+ {
+ StaticValue::operator=(v);
+ return *this;
+ }
+ Value &operator=(ManagedPtr m) {
if (!m) {
setM(nullptr);
} else {
@@ -521,7 +300,7 @@ struct Q_QML_PRIVATE_EXPORT Value
}
return *this;
}
- Value &operator=(Heap::Base *o) {
+ Value &operator=(HeapBasePtr o) {
setM(o);
return *this;
}
@@ -529,43 +308,88 @@ struct Q_QML_PRIVATE_EXPORT Value
template<typename T>
Value &operator=(const Scoped<T> &t);
};
-Q_STATIC_ASSERT(std::is_trivial< Value >::value);
+Q_STATIC_ASSERT(std::is_trivial<Value>::value);
+Q_STATIC_ASSERT(sizeof(Value) == sizeof(StaticValue));
+
+template<>
+inline StaticValue &StaticValue::operator=<Value>(const Value &value)
+{
+ _val = value._val;
+ return *this;
+}
+
+template<typename Managed>
+inline StaticValue &StaticValue::operator=(const Managed &m)
+{
+ *static_cast<Value *>(this) = m;
+ return *this;
+}
+
+template<>
+inline Value &StaticValue::asValue<Value>()
+{
+ return *static_cast<Value *>(this);
+}
+
+template<>
+inline const Value &StaticValue::asValue<Value>() const
+{
+ return *static_cast<const Value *>(this);
+}
+
+template<>
+inline Value *CallData::argValues<Value>()
+{
+ return static_cast<Value *>(static_cast<StaticValue *>(args));
+}
+
+template<>
+inline const Value *CallData::argValues<Value>() const
+{
+ return static_cast<const Value *>(static_cast<const StaticValue *>(args));
+}
+
+template<typename HeapBase>
+inline Encode::Encode(HeapBase *o)
+{
+ val = Value::fromHeapObject(o).asReturnedValue();
+}
inline void Value::mark(MarkStack *markStack)
{
- Heap::Base *o = heapObject();
+ HeapBasePtr o = heapObject();
if (o)
o->mark(markStack);
}
inline bool Value::isString() const
{
- Heap::Base *b = heapObject();
+ HeapBasePtr b = heapObject();
return b && b->internalClass->vtable->isString;
}
bool Value::isStringOrSymbol() const
{
- Heap::Base *b = heapObject();
+ HeapBasePtr b = heapObject();
return b && b->internalClass->vtable->isStringOrSymbol;
}
bool Value::isSymbol() const
{
- Heap::Base *b = heapObject();
+ HeapBasePtr b = heapObject();
return b && b->internalClass->vtable->isStringOrSymbol && !b->internalClass->vtable->isString;
}
inline bool Value::isObject() const
{
- Heap::Base *b = heapObject();
+ HeapBasePtr b = heapObject();
return b && b->internalClass->vtable->isObject;
}
inline bool Value::isFunctionObject() const
{
- Heap::Base *b = heapObject();
+ HeapBasePtr b = heapObject();
return b && b->internalClass->vtable->isFunctionObject;
}
@@ -595,151 +419,12 @@ inline ReturnedValue Value::convertedToNumber() const
inline
ReturnedValue Heap::Base::asReturnedValue() const
{
- return Value::fromHeapObject(const_cast<Heap::Base *>(this)).asReturnedValue();
-}
-
-inline Value Value::fromDouble(double d)
-{
- Value v;
- v.setDouble(d);
- return v;
-}
-
-inline Value Value::fromUInt32(uint i)
-{
- Value v;
- if (i < INT_MAX) {
- v.setTagValue(quint32(ValueTypeInternal::Integer), i);
- } else {
- v.setDouble(i);
- }
- return v;
-}
-
-struct Double {
- quint64 d;
-
- Double(double dbl) {
- memcpy(&d, &dbl, sizeof(double));
- }
-
- int sign() const {
- return (d >> 63) ? -1 : 1;
- }
-
- bool isDenormal() const {
- return static_cast<int>((d << 1) >> 53) == 0;
- }
-
- int exponent() const {
- return static_cast<int>((d << 1) >> 53) - 1023;
- }
-
- quint64 significant() const {
- quint64 m = (d << 12) >> 12;
- if (!isDenormal())
- m |= (static_cast<quint64>(1) << 52);
- return m;
- }
-
- static int toInt32(double d) {
- int i = static_cast<int>(d);
- if (i == d)
- return i;
- return Double(d).toInt32();
- }
-
- int toInt32() {
- int e = exponent() - 52;
- if (e < 0) {
- if (e <= -53)
- return 0;
- return sign() * static_cast<int>(significant() >> -e);
- } else {
- if (e > 31)
- return 0;
- return sign() * (static_cast<int>(significant()) << e);
- }
- }
-};
-
-inline double Value::toInteger(double d)
-{
- if (std::isnan(d))
- return +0;
- else if (!d || std::isinf(d))
- return d;
- return d >= 0 ? std::floor(d) : std::ceil(d);
-}
-
-inline int Value::toInt32(double value)
-{
- return Double::toInt32(value);
-}
-
-inline unsigned int Value::toUInt32(double d)
-{
- return static_cast<uint>(toInt32(d));
+ return Value::fromHeapObject(const_cast<Value::HeapBasePtr>(this)).asReturnedValue();
}
// For source compat with older code in other modules
using Primitive = Value;
-struct Encode {
- static constexpr ReturnedValue undefined() {
- return Value::undefinedValue().asReturnedValue();
- }
- static constexpr ReturnedValue null() {
- return Value::nullValue().asReturnedValue();
- }
-
- explicit constexpr Encode(bool b)
- : val(Value::fromBoolean(b).asReturnedValue())
- {
- }
- explicit Encode(double d) {
- val = Value::fromDouble(d).asReturnedValue();
- }
- explicit constexpr Encode(int i)
- : val(Value::fromInt32(i).asReturnedValue())
- {
- }
- explicit Encode(uint i) {
- val = Value::fromUInt32(i).asReturnedValue();
- }
- explicit constexpr Encode(ReturnedValue v)
- : val(v)
- {
- }
- constexpr Encode(Value v)
- : val(v.asReturnedValue())
- {
- }
-
- explicit Encode(Heap::Base *o) {
- val = Value::fromHeapObject(o).asReturnedValue();
- }
-
- explicit Encode(Value *o) {
- Q_ASSERT(o);
- val = o->asReturnedValue();
- }
-
- static ReturnedValue smallestNumber(double d) {
- if (Value::isInt32(d))
- return Encode(static_cast<int>(d));
- else
- return Encode(d);
- }
-
- constexpr operator ReturnedValue() const {
- return val;
- }
- quint64 val;
-private:
- explicit Encode(void *);
-};
-
template<typename T>
ReturnedValue value_convert(ExecutionEngine *e, const Value &v);
@@ -796,8 +481,8 @@ inline double Value::toInteger() const
template <size_t o>
struct HeapValue : Value {
static Q_CONSTEXPR size_t offset = o;
- Heap::Base *base() {
- Heap::Base *base = reinterpret_cast<Heap::Base *>(this) - (offset/sizeof(Heap::Base));
+ HeapBasePtr base() {
+ HeapBasePtr base = reinterpret_cast<HeapBasePtr>(this) - (offset/sizeof(Heap::Base));
Q_ASSERT(base->inUse());
return base;
}
@@ -805,7 +490,7 @@ struct HeapValue : Value {
void set(EngineBase *e, const Value &newVal) {
WriteBarrier::write(e, base(), data_ptr(), newVal.asReturnedValue());
}
- void set(EngineBase *e, Heap::Base *b) {
+ void set(EngineBase *e, HeapBasePtr b) {
WriteBarrier::write(e, base(), data_ptr(), b->asReturnedValue());
}
};
@@ -817,8 +502,9 @@ struct ValueArray {
uint alloc;
Value values[1];
- Heap::Base *base() {
- Heap::Base *base = reinterpret_cast<Heap::Base *>(this) - (offset/sizeof(Heap::Base));
+ Value::HeapBasePtr base() {
+ Value::HeapBasePtr base = reinterpret_cast<Value::HeapBasePtr>(this)
+ - (offset/sizeof(Heap::Base));
Q_ASSERT(base->inUse());
return base;
}
@@ -826,7 +512,7 @@ struct ValueArray {
void set(EngineBase *e, uint index, Value v) {
WriteBarrier::write(e, base(), values[index].data_ptr(), v.asReturnedValue());
}
- void set(EngineBase *e, uint index, Heap::Base *b) {
+ void set(EngineBase *e, uint index, Value::HeapBasePtr b) {
WriteBarrier::write(e, base(), values[index].data_ptr(), Value::fromHeapObject(b).asReturnedValue());
}
inline const Value &operator[] (uint index) const {
@@ -855,12 +541,12 @@ struct ValueArray {
const Value *end = v + alloc;
if (alloc > 32*1024) {
// drain from time to time to avoid overflows in the js stack
- Heap::Base **currentBase = markStack->top;
+ Value::HeapBasePtr *currentBase = markStack->top;
while (v < end) {
v->mark(markStack);
++v;
if (markStack->top >= currentBase + 32*1024) {
- Heap::Base **oldBase = markStack->base;
+ Value::HeapBasePtr *oldBase = markStack->base;
markStack->base = currentBase;
markStack->drain();
markStack->base = oldBase;
diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp
index e4d8bcaafc..e117e509ab 100644
--- a/src/qml/jsruntime/qv4variantobject.cpp
+++ b/src/qml/jsruntime/qv4variantobject.cpp
@@ -41,7 +41,6 @@
#include "qv4functionobject_p.h"
#include "qv4objectproto_p.h"
#include <private/qqmlvaluetypewrapper_p.h>
-#include <private/qv8engine_p.h>
#include <private/qv4qobjectwrapper_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index 4ce8d97ee5..ad5a6ebc8c 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -38,11 +38,11 @@
****************************************************************************/
#include "qv4vme_moth_p.h"
-#include "qv4instr_moth_p.h"
#include <QtCore/qjsondocument.h>
#include <QtCore/qjsonobject.h>
+#include <private/qv4instr_moth_p.h>
#include <private/qv4value_p.h>
#include <private/qv4debugging_p.h>
#include <private/qv4function_p.h>
@@ -56,17 +56,20 @@
#include <private/qv4profiling_p.h>
#include <private/qv4jscall_p.h>
#include <private/qv4generatorobject_p.h>
+#include <private/qv4alloca_p.h>
#include <private/qqmljavascriptexpression_p.h>
#include <iostream>
-#include "qv4alloca_p.h"
-
+#if QT_CONFIG(qml_jit)
#include <private/qv4baselinejit_p.h>
+#endif
#include <qtqml_tracepoints_p.h>
#undef COUNT_INSTRUCTIONS
+enum { ShowWhenDeoptimiationHappens = 0 };
+
extern "C" {
// This is the interface to Qt Creator's (new) QML debugger.
@@ -202,7 +205,7 @@ int qt_v4DebuggerHook(const char *json)
return ProtocolVersion; // Version number.
}
- int version = ob.value(QLatin1Literal("version")).toString().toInt();
+ int version = ob.value(QLatin1String("version")).toString().toInt();
if (version != ProtocolVersion) {
return -WrongProtocol;
}
@@ -345,73 +348,9 @@ static struct InstrCount {
#undef CHECK_EXCEPTION
#endif
#define CHECK_EXCEPTION \
- if (engine->hasException) \
+ if (engine->hasException || engine->isInterrupted.loadAcquire()) \
goto handleUnwind
-static inline void traceJumpTakesTruePath(bool truePathTaken, Function *f, int slot)
-{
-#if QT_CONFIG(qml_tracing)
- quint8 *traceInfo = f->traceInfo(slot);
- Q_ASSERT(traceInfo);
- *traceInfo |= truePathTaken ? quint8(ObservedTraceValues::TruePathTaken)
- : quint8(ObservedTraceValues::FalsePathTaken);
-#else
- Q_UNUSED(truePathTaken);
- Q_UNUSED(f);
- Q_UNUSED(slot);
-#endif
-}
-
-static inline void traceValue(ReturnedValue acc, Function *f, int slot)
-{
-#if QT_CONFIG(qml_tracing)
- quint8 *traceInfo = f->traceInfo(slot);
- Q_ASSERT(traceInfo);
- switch (Primitive::fromReturnedValue(acc).type()) {
- case QV4::Value::Integer_Type:
- *traceInfo |= quint8(ObservedTraceValues::Integer);
- break;
- case QV4::Value::Boolean_Type:
- *traceInfo |= quint8(ObservedTraceValues::Boolean);
- break;
- case QV4::Value::Double_Type:
- *traceInfo |= quint8(ObservedTraceValues::Double);
- break;
- default:
- *traceInfo |= quint8(ObservedTraceValues::Other);
- break;
- }
-#else
- Q_UNUSED(acc);
- Q_UNUSED(f);
- Q_UNUSED(slot);
-#endif
-}
-
-static inline void traceDoubleValue(Function *f, int slot)
-{
-#if QT_CONFIG(qml_tracing)
- quint8 *traceInfo = f->traceInfo(slot);
- Q_ASSERT(traceInfo);
- *traceInfo |= quint8(ObservedTraceValues::Double);
-#else
- Q_UNUSED(f);
- Q_UNUSED(slot);
-#endif
-}
-
-static inline void traceOtherValue(Function *f, int slot)
-{
-#if QT_CONFIG(qml_tracing)
- quint8 *traceInfo = f->traceInfo(slot);
- Q_ASSERT(traceInfo);
- *traceInfo |= quint8(ObservedTraceValues::Other);
-#else
- Q_UNUSED(f);
- Q_UNUSED(slot);
-#endif
-}
-
static inline Heap::CallContext *getScope(QV4::Value *stack, int level)
{
Heap::ExecutionContext *scope = static_cast<ExecutionContext &>(stack[CallData::Context]).d();
@@ -425,7 +364,7 @@ static inline Heap::CallContext *getScope(QV4::Value *stack, int level)
static inline const QV4::Value &constant(Function *function, int index)
{
- return function->compilationUnit->constants[index];
+ return function->compilationUnit->constants[index].asValue<QV4::Value>();
}
static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs)
@@ -491,7 +430,7 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine)
Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling
QV4::Debugging::Debugger *debugger = engine->debugger();
-#ifdef V4_ENABLE_JIT
+#if QT_CONFIG(qml_jit)
if (debugger == nullptr) {
if (function->jittedCode == nullptr) {
if (engine->canJIT(function))
@@ -499,16 +438,20 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine)
else
++function->interpreterCallCount;
}
- if (function->jittedCode != nullptr)
- return function->jittedCode(frame, engine);
}
-#endif // V4_ENABLE_JIT
+#endif // QT_CONFIG(qml_jit)
// interpreter
if (debugger)
debugger->enteringFunction();
- ReturnedValue result = interpret(frame, engine, function->codeData);
+ ReturnedValue result;
+ if (function->jittedCode != nullptr && debugger == nullptr) {
+ result = function->jittedCode(frame, engine);
+ } else {
+ // interpreter
+ result = interpret(frame, engine, function->codeData);
+ }
if (debugger)
debugger->leavingFunction(result);
@@ -519,15 +462,10 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine)
QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, const char *code)
{
QV4::Function *function = frame->v4Function;
- QV4::Value &accumulator = frame->jsFrame->accumulator;
+ QV4::Value &accumulator = frame->jsFrame->accumulator.asValue<Value>();
QV4::ReturnedValue acc = accumulator.asReturnedValue();
Value *stack = reinterpret_cast<Value *>(frame->jsFrame);
- if (function->tracingEnabled()) {
- for (int i = 0; i < int(function->nFormals); ++i)
- traceValue(frame->jsFrame->argument(i), function, i);
- }
-
MOTH_JUMP_TABLE;
for (;;) {
@@ -586,7 +524,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m());
Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext);
acc = cc->locals[index].asReturnedValue();
- traceValue(acc, function, traceSlot);
MOTH_END_INSTR(LoadLocal)
MOTH_BEGIN_INSTR(StoreLocal)
@@ -599,7 +536,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(LoadScopedLocal)
auto cc = getScope(stack, scope);
acc = cc->locals[index].asReturnedValue();
- traceValue(acc, function, traceSlot);
MOTH_END_INSTR(LoadScopedLocal)
MOTH_BEGIN_INSTR(StoreScopedLocal)
@@ -613,88 +549,73 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_END_INSTR(LoadRuntimeString)
MOTH_BEGIN_INSTR(MoveRegExp)
- STACK_VALUE(destReg) = Runtime::method_regexpLiteral(engine, regExpId);
+ STACK_VALUE(destReg) = Runtime::RegexpLiteral::call(engine, regExpId);
MOTH_END_INSTR(MoveRegExp)
MOTH_BEGIN_INSTR(LoadClosure)
- acc = Runtime::method_closure(engine, value);
+ acc = Runtime::Closure::call(engine, value);
MOTH_END_INSTR(LoadClosure)
MOTH_BEGIN_INSTR(LoadName)
STORE_IP();
- acc = Runtime::method_loadName(engine, name);
+ acc = Runtime::LoadName::call(engine, name);
CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
MOTH_END_INSTR(LoadName)
MOTH_BEGIN_INSTR(LoadGlobalLookup)
STORE_IP();
- QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
+ QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index;
acc = l->globalGetter(l, engine);
CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
MOTH_END_INSTR(LoadGlobalLookup)
MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup)
STORE_IP();
- QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
+ QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index;
acc = l->qmlContextPropertyGetter(l, engine, nullptr);
CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
MOTH_END_INSTR(LoadQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(StoreNameStrict)
STORE_IP();
STORE_ACC();
- Runtime::method_storeNameStrict(engine, name, accumulator);
+ Runtime::StoreNameStrict::call(engine, name, accumulator);
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreNameStrict)
MOTH_BEGIN_INSTR(StoreNameSloppy)
STORE_IP();
STORE_ACC();
- Runtime::method_storeNameSloppy(engine, name, accumulator);
+ Runtime::StoreNameSloppy::call(engine, name, accumulator);
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreNameSloppy)
MOTH_BEGIN_INSTR(LoadElement)
STORE_IP();
STORE_ACC();
-#if QT_CONFIG(qml_tracing)
- acc = Runtime::method_loadElement_traced(engine, STACK_VALUE(base), accumulator, function->traceInfo(traceSlot));
- traceValue(acc, function, traceSlot);
-#else
- Q_UNUSED(traceSlot);
- acc = Runtime::method_loadElement(engine, STACK_VALUE(base), accumulator);
-#endif
+ acc = Runtime::LoadElement::call(engine, STACK_VALUE(base), accumulator);
CHECK_EXCEPTION;
MOTH_END_INSTR(LoadElement)
MOTH_BEGIN_INSTR(StoreElement)
STORE_IP();
STORE_ACC();
-#if QT_CONFIG(qml_tracing)
- Runtime::method_storeElement_traced(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator, function->traceInfo(traceSlot));
-#else
- Q_UNUSED(traceSlot);
- Runtime::method_storeElement(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator);
-#endif
+ Runtime::StoreElement::call(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator);
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreElement)
MOTH_BEGIN_INSTR(LoadProperty)
STORE_IP();
STORE_ACC();
- acc = Runtime::method_loadProperty(engine, accumulator, name);
+ acc = Runtime::LoadProperty::call(engine, accumulator, name);
CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
MOTH_END_INSTR(LoadProperty)
MOTH_BEGIN_INSTR(GetLookup)
STORE_IP();
STORE_ACC();
- QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
+ QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index;
if (accumulator.isNullOrUndefined()) {
QString message = QStringLiteral("Cannot read property '%1' of %2")
@@ -706,20 +627,19 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
acc = l->getter(l, engine, accumulator);
CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
MOTH_END_INSTR(GetLookup)
MOTH_BEGIN_INSTR(StoreProperty)
STORE_IP();
STORE_ACC();
- Runtime::method_storeProperty(engine, STACK_VALUE(base), name, accumulator);
+ Runtime::StoreProperty::call(engine, STACK_VALUE(base), name, accumulator);
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreProperty)
MOTH_BEGIN_INSTR(SetLookup)
STORE_IP();
STORE_ACC();
- QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
+ QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index;
if (!l->setter(l, engine, STACK_VALUE(base), accumulator) && function->isStrict())
engine->throwTypeError();
CHECK_EXCEPTION;
@@ -727,14 +647,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(LoadSuperProperty)
STORE_IP();
- acc = Runtime::method_loadSuperProperty(engine, STACK_VALUE(property));
+ acc = Runtime::LoadSuperProperty::call(engine, STACK_VALUE(property));
CHECK_EXCEPTION;
MOTH_END_INSTR(LoadSuperProperty)
MOTH_BEGIN_INSTR(StoreSuperProperty)
STORE_IP();
STORE_ACC();
- Runtime::method_storeSuperProperty(engine, STACK_VALUE(property), accumulator);
+ Runtime::StoreSuperProperty::call(engine, STACK_VALUE(property), accumulator);
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreSuperProperty)
@@ -765,7 +685,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(IteratorNextForYieldStar)
STORE_ACC();
- acc = Runtime::method_iteratorNextForYieldStar(engine, accumulator, STACK_VALUE(iterator), &STACK_VALUE(object));
+ acc = Runtime::IteratorNextForYieldStar::call(engine, accumulator, STACK_VALUE(iterator), &STACK_VALUE(object));
CHECK_EXCEPTION;
MOTH_END_INSTR(IteratorNextForYieldStar)
@@ -779,7 +699,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
Value undef = Value::undefinedValue();
acc = static_cast<const FunctionObject &>(func).call(&undef, stack + argv, argc);
CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallValue)
MOTH_BEGIN_INSTR(CallWithReceiver)
@@ -791,19 +710,17 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
}
acc = static_cast<const FunctionObject &>(func).call(stack + thisObject, stack + argv, argc);
CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallWithReceiver)
MOTH_BEGIN_INSTR(CallProperty)
STORE_IP();
- acc = Runtime::method_callProperty(engine, stack + base, name, stack + argv, argc);
+ acc = Runtime::CallProperty::call(engine, stack[base], name, stack + argv, argc);
CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallProperty)
MOTH_BEGIN_INSTR(CallPropertyLookup)
STORE_IP();
- Lookup *l = function->compilationUnit->runtimeLookups + lookupIndex;
+ Lookup *l = function->executableCompilationUnit()->runtimeLookups + lookupIndex;
if (stack[base].isNullOrUndefined()) {
QString message = QStringLiteral("Cannot call method '%1' of %2")
@@ -826,49 +743,42 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc);
CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallPropertyLookup)
MOTH_BEGIN_INSTR(CallElement)
STORE_IP();
- acc = Runtime::method_callElement(engine, stack + base, STACK_VALUE(index), stack + argv, argc);
+ acc = Runtime::CallElement::call(engine, stack[base], STACK_VALUE(index), stack + argv, argc);
CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallElement)
MOTH_BEGIN_INSTR(CallName)
STORE_IP();
- acc = Runtime::method_callName(engine, name, stack + argv, argc);
+ acc = Runtime::CallName::call(engine, name, stack + argv, argc);
CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallName)
MOTH_BEGIN_INSTR(CallPossiblyDirectEval)
STORE_IP();
- acc = Runtime::method_callPossiblyDirectEval(engine, stack + argv, argc);
+ acc = Runtime::CallPossiblyDirectEval::call(engine, stack + argv, argc);
CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallPossiblyDirectEval)
MOTH_BEGIN_INSTR(CallGlobalLookup)
STORE_IP();
- acc = Runtime::method_callGlobalLookup(engine, index, stack + argv, argc);
+ acc = Runtime::CallGlobalLookup::call(engine, index, stack + argv, argc);
CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallGlobalLookup)
MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup)
STORE_IP();
- acc = Runtime::method_callQmlContextPropertyLookup(engine, index, stack + argv, argc);
+ acc = Runtime::CallQmlContextPropertyLookup::call(engine, index, stack + argv, argc);
CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(CallWithSpread)
STORE_IP();
- acc = Runtime::method_callWithSpread(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc);
+ acc = Runtime::CallWithSpread::call(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc);
CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallWithSpread)
MOTH_BEGIN_INSTR(TailCall)
@@ -877,21 +787,21 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
*engine->jsAlloca(1) = Primitive::fromInt32(argv);
*engine->jsAlloca(1) = STACK_VALUE(thisObject);
*engine->jsAlloca(1) = STACK_VALUE(func);
- return Runtime::method_tailCall(frame, engine);
+ return Runtime::TailCall::call(frame, engine);
CHECK_EXCEPTION;
MOTH_END_INSTR(TailCall)
MOTH_BEGIN_INSTR(Construct)
STORE_IP();
STORE_ACC();
- acc = Runtime::method_construct(engine, STACK_VALUE(func), ACC, stack + argv, argc);
+ acc = Runtime::Construct::call(engine, STACK_VALUE(func), ACC, stack + argv, argc);
CHECK_EXCEPTION;
MOTH_END_INSTR(Construct)
MOTH_BEGIN_INSTR(ConstructWithSpread)
STORE_IP();
STORE_ACC();
- acc = Runtime::method_constructWithSpread(engine, STACK_VALUE(func), ACC, stack + argv, argc);
+ acc = Runtime::ConstructWithSpread::call(engine, STACK_VALUE(func), ACC, stack + argv, argc);
CHECK_EXCEPTION;
MOTH_END_INSTR(ConstructWithSpread)
@@ -918,7 +828,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(DeadTemporalZoneCheck)
if (ACC.isEmpty()) {
STORE_IP();
- Runtime::method_throwReferenceError(engine, name);
+ Runtime::ThrowReferenceError::call(engine, name);
goto handleUnwind;
}
MOTH_END_INSTR(DeadTemporalZoneCheck)
@@ -926,7 +836,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(ThrowException)
STORE_IP();
STORE_ACC();
- Runtime::method_throwException(engine, accumulator);
+ Runtime::ThrowException::call(engine, accumulator);
goto handleUnwind;
MOTH_END_INSTR(ThrowException)
@@ -944,40 +854,36 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_END_INSTR(SetException)
MOTH_BEGIN_INSTR(PushCatchContext)
- ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
- STACK_VALUE(CallData::Context) = Runtime::method_createCatchContext(c, index, name);
+ Runtime::PushCatchContext::call(engine, index, name);
MOTH_END_INSTR(PushCatchContext)
MOTH_BEGIN_INSTR(CreateCallContext)
- stack[CallData::Context] = ExecutionContext::newCallContext(frame);
+ Runtime::PushCallContext::call(frame);
MOTH_END_INSTR(CreateCallContext)
MOTH_BEGIN_INSTR(PushWithContext)
STORE_IP();
STORE_ACC();
- auto ctx = Runtime::method_createWithContext(engine, stack);
+ acc = Runtime::PushWithContext::call(engine, stack[CallData::Accumulator]);
CHECK_EXCEPTION;
- STACK_VALUE(CallData::Context) = ctx;
MOTH_END_INSTR(PushWithContext)
MOTH_BEGIN_INSTR(PushBlockContext)
STORE_ACC();
- ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
- STACK_VALUE(CallData::Context) = Runtime::method_createBlockContext(c, index);
+ Runtime::PushBlockContext::call(engine, index);
MOTH_END_INSTR(PushBlockContext)
MOTH_BEGIN_INSTR(CloneBlockContext)
STORE_ACC();
- ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
- STACK_VALUE(CallData::Context) = Runtime::method_cloneBlockContext(c);
+ Runtime::CloneBlockContext::call(engine);
MOTH_END_INSTR(CloneBlockContext)
MOTH_BEGIN_INSTR(PushScriptContext)
- STACK_VALUE(CallData::Context) = Runtime::method_createScriptContext(engine, index);
+ Runtime::PushScriptContext::call(engine, index);
MOTH_END_INSTR(PushScriptContext)
MOTH_BEGIN_INSTR(PopScriptContext)
- STACK_VALUE(CallData::Context) = Runtime::method_popScriptContext(engine);
+ Runtime::PopScriptContext::call(engine);
MOTH_END_INSTR(PopScriptContext)
MOTH_BEGIN_INSTR(PopContext)
@@ -988,14 +894,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(GetIterator)
STORE_IP();
STORE_ACC();
- acc = Runtime::method_getIterator(engine, accumulator, iterator);
+ acc = Runtime::GetIterator::call(engine, accumulator, iterator);
CHECK_EXCEPTION;
MOTH_END_INSTR(GetIterator)
MOTH_BEGIN_INSTR(IteratorNext)
STORE_IP();
STORE_ACC();
- acc = Runtime::method_iteratorNext(engine, accumulator, &STACK_VALUE(value));
+ acc = Runtime::IteratorNext::call(engine, accumulator, &STACK_VALUE(value));
STACK_VALUE(done) = acc;
CHECK_EXCEPTION;
MOTH_END_INSTR(IteratorNext)
@@ -1003,98 +909,74 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(IteratorClose)
STORE_IP();
STORE_ACC();
- acc = Runtime::method_iteratorClose(engine, accumulator, STACK_VALUE(done));
+ acc = Runtime::IteratorClose::call(engine, accumulator, STACK_VALUE(done));
CHECK_EXCEPTION;
MOTH_END_INSTR(IteratorClose)
MOTH_BEGIN_INSTR(DestructureRestElement)
STORE_IP();
STORE_ACC();
- acc = Runtime::method_destructureRestElement(engine, ACC);
+ acc = Runtime::DestructureRestElement::call(engine, ACC);
CHECK_EXCEPTION;
MOTH_END_INSTR(DestructureRestElement)
MOTH_BEGIN_INSTR(DeleteProperty)
- if (!Runtime::method_deleteProperty(engine, STACK_VALUE(base), STACK_VALUE(index))) {
- if (function->isStrict()) {
- STORE_IP();
- engine->throwTypeError();
- goto handleUnwind;
- }
- acc = Encode(false);
- } else {
- acc = Encode(true);
- }
+ acc = Runtime::DeleteProperty::call(engine, function, STACK_VALUE(base), STACK_VALUE(index));
+ CHECK_EXCEPTION;
MOTH_END_INSTR(DeleteProperty)
MOTH_BEGIN_INSTR(DeleteName)
- if (!Runtime::method_deleteName(engine, name)) {
- if (function->isStrict()) {
- STORE_IP();
- QString n = function->compilationUnit->runtimeStrings[name]->toQString();
- engine->throwSyntaxError(QStringLiteral("Can't delete property %1").arg(n));
- goto handleUnwind;
- }
- acc = Encode(false);
- } else {
- acc = Encode(true);
- }
+ acc = Runtime::DeleteName::call(engine, function, name);
+ CHECK_EXCEPTION;
MOTH_END_INSTR(DeleteName)
MOTH_BEGIN_INSTR(TypeofName)
- acc = Runtime::method_typeofName(engine, name);
+ acc = Runtime::TypeofName::call(engine, name);
MOTH_END_INSTR(TypeofName)
MOTH_BEGIN_INSTR(TypeofValue)
STORE_ACC();
- acc = Runtime::method_typeofValue(engine, accumulator);
+ acc = Runtime::TypeofValue::call(engine, accumulator);
MOTH_END_INSTR(TypeofValue)
MOTH_BEGIN_INSTR(DeclareVar)
- Runtime::method_declareVar(engine, isDeletable, varName);
+ Runtime::DeclareVar::call(engine, isDeletable, varName);
MOTH_END_INSTR(DeclareVar)
MOTH_BEGIN_INSTR(DefineArray)
QV4::Value *arguments = stack + args;
- acc = Runtime::method_arrayLiteral(engine, arguments, argc);
+ acc = Runtime::ArrayLiteral::call(engine, arguments, argc);
MOTH_END_INSTR(DefineArray)
MOTH_BEGIN_INSTR(DefineObjectLiteral)
QV4::Value *arguments = stack + args;
- acc = Runtime::method_objectLiteral(engine, internalClassId, arguments, argc);
+ acc = Runtime::ObjectLiteral::call(engine, internalClassId, arguments, argc);
MOTH_END_INSTR(DefineObjectLiteral)
MOTH_BEGIN_INSTR(CreateClass)
- acc = Runtime::method_createClass(engine, classIndex, STACK_VALUE(heritage), stack + computedNames);
+ acc = Runtime::CreateClass::call(engine, classIndex, STACK_VALUE(heritage), stack + computedNames);
MOTH_END_INSTR(CreateClass)
MOTH_BEGIN_INSTR(CreateMappedArgumentsObject)
- acc = Runtime::method_createMappedArgumentsObject(engine);
+ acc = Runtime::CreateMappedArgumentsObject::call(engine);
MOTH_END_INSTR(CreateMappedArgumentsObject)
MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject)
- acc = Runtime::method_createUnmappedArgumentsObject(engine);
+ acc = Runtime::CreateUnmappedArgumentsObject::call(engine);
MOTH_END_INSTR(CreateUnmappedArgumentsObject)
MOTH_BEGIN_INSTR(CreateRestParameter)
- acc = Runtime::method_createRestParameter(engine, argIndex);
+ acc = Runtime::CreateRestParameter::call(engine, argIndex);
MOTH_END_INSTR(CreateRestParameter)
MOTH_BEGIN_INSTR(ConvertThisToObject)
- Value *t = &stack[CallData::This];
- if (!t->isObject()) {
- if (t->isNullOrUndefined()) {
- *t = engine->globalObject->asReturnedValue();
- } else {
- STORE_ACC();
- *t = t->toObject(engine)->asReturnedValue();
- CHECK_EXCEPTION;
- }
- }
+ STORE_ACC();
+ stack[CallData::This] = Runtime::ConvertThisToObject::call(engine, stack[CallData::This]);
+ CHECK_EXCEPTION;
MOTH_END_INSTR(ConvertThisToObject)
MOTH_BEGIN_INSTR(LoadSuperConstructor)
- acc = Runtime::method_loadSuperConstructor(engine, stack[CallData::Function]);
+ acc = Runtime::LoadSuperConstructor::call(engine, stack[CallData::Function]);
CHECK_EXCEPTION;
MOTH_END_INSTR(LoadSuperConstructor)
@@ -1114,7 +996,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
takeJump = ACC.int_32();
else
takeJump = ACC.toBoolean();
- traceJumpTakesTruePath(takeJump, function, traceSlot);
if (takeJump)
code += offset;
MOTH_END_INSTR(JumpTrue)
@@ -1125,7 +1006,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
takeJump = !ACC.int_32();
else
takeJump = !ACC.toBoolean();
- traceJumpTakesTruePath(!takeJump, function, traceSlot);
if (takeJump)
code += offset;
MOTH_END_INSTR(JumpFalse)
@@ -1140,6 +1020,10 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
code += offset;
MOTH_END_INSTR(JumpNotUndefined)
+ MOTH_BEGIN_INSTR(CheckException)
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(CheckException)
+
MOTH_BEGIN_INSTR(CmpEqNull)
acc = Encode(ACC.isNullOrUndefined());
MOTH_END_INSTR(CmpEqNull)
@@ -1176,7 +1060,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
acc = Encode(left.int_32() == ACC.int_32());
} else {
STORE_ACC();
- acc = Encode(bool(Runtime::method_compareEqual(left, accumulator)));
+ acc = Encode(bool(Runtime::CompareEqual::call(left, accumulator)));
CHECK_EXCEPTION;
}
MOTH_END_INSTR(CmpEq)
@@ -1187,7 +1071,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
acc = Encode(bool(left.int_32() != ACC.int_32()));
} else {
STORE_ACC();
- acc = Encode(bool(!Runtime::method_compareEqual(left, accumulator)));
+ acc = Encode(bool(!Runtime::CompareEqual::call(left, accumulator)));
CHECK_EXCEPTION;
}
MOTH_END_INSTR(CmpNe)
@@ -1200,7 +1084,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
acc = Encode(left.asDouble() > ACC.asDouble());
} else {
STORE_ACC();
- acc = Encode(bool(Runtime::method_compareGreaterThan(left, accumulator)));
+ acc = Encode(bool(Runtime::CompareGreaterThan::call(left, accumulator)));
CHECK_EXCEPTION;
}
MOTH_END_INSTR(CmpGt)
@@ -1213,7 +1097,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
acc = Encode(left.asDouble() >= ACC.asDouble());
} else {
STORE_ACC();
- acc = Encode(bool(Runtime::method_compareGreaterEqual(left, accumulator)));
+ acc = Encode(bool(Runtime::CompareGreaterEqual::call(left, accumulator)));
CHECK_EXCEPTION;
}
MOTH_END_INSTR(CmpGe)
@@ -1226,7 +1110,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
acc = Encode(left.asDouble() < ACC.asDouble());
} else {
STORE_ACC();
- acc = Encode(bool(Runtime::method_compareLessThan(left, accumulator)));
+ acc = Encode(bool(Runtime::CompareLessThan::call(left, accumulator)));
CHECK_EXCEPTION;
}
MOTH_END_INSTR(CmpLt)
@@ -1239,7 +1123,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
acc = Encode(left.asDouble() <= ACC.asDouble());
} else {
STORE_ACC();
- acc = Encode(bool(Runtime::method_compareLessEqual(left, accumulator)));
+ acc = Encode(bool(Runtime::CompareLessEqual::call(left, accumulator)));
CHECK_EXCEPTION;
}
MOTH_END_INSTR(CmpLe)
@@ -1249,7 +1133,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
acc = Encode(true);
} else {
STORE_ACC();
- acc = Encode(bool(RuntimeHelpers::strictEqual(STACK_VALUE(lhs), accumulator)));
+ acc = Runtime::StrictEqual::call(STACK_VALUE(lhs), accumulator);
CHECK_EXCEPTION;
}
MOTH_END_INSTR(CmpStrictEqual)
@@ -1257,7 +1141,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(CmpStrictNotEqual)
if (STACK_VALUE(lhs).rawValue() != ACC.rawValue() || ACC.isNaN()) {
STORE_ACC();
- acc = Encode(!RuntimeHelpers::strictEqual(STACK_VALUE(lhs), accumulator));
+ acc = Runtime::StrictNotEqual::call(STACK_VALUE(lhs), accumulator);
CHECK_EXCEPTION;
} else {
acc = Encode(false);
@@ -1266,13 +1150,13 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(CmpIn)
STORE_ACC();
- acc = Runtime::method_in(engine, STACK_VALUE(lhs), accumulator);
+ acc = Runtime::In::call(engine, STACK_VALUE(lhs), accumulator);
CHECK_EXCEPTION;
MOTH_END_INSTR(CmpIn)
MOTH_BEGIN_INSTR(CmpInstanceOf)
STORE_ACC();
- acc = Runtime::method_instanceof(engine, STACK_VALUE(lhs), ACC);
+ acc = Runtime::Instanceof::call(engine, STACK_VALUE(lhs), ACC);
CHECK_EXCEPTION;
MOTH_END_INSTR(CmpInstanceOf)
@@ -1296,17 +1180,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
int a = ACC.int_32();
if (a == 0 || a == std::numeric_limits<int>::min()) {
acc = Encode(-static_cast<double>(a));
- traceDoubleValue(function, traceSlot);
} else {
- acc = sub_int32(0, ACC.int_32(), function->traceInfo(traceSlot));
+ acc = sub_int32(0, ACC.int_32());
}
} else if (ACC.isDouble()) {
acc ^= (1ull << 63); // simply flip sign bit
- traceDoubleValue(function, traceSlot);
} else {
acc = Encode(-ACC.toNumberImpl());
CHECK_EXCEPTION;
- traceOtherValue(function, traceSlot);
}
MOTH_END_INSTR(UMinus)
@@ -1317,57 +1198,49 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(Increment)
if (Q_LIKELY(ACC.integerCompatible())) {
- acc = add_int32(ACC.int_32(), 1, function->traceInfo(traceSlot));
+ acc = add_int32(ACC.int_32(), 1);
} else if (ACC.isDouble()) {
acc = QV4::Encode(ACC.doubleValue() + 1.);
- traceDoubleValue(function, traceSlot);
} else {
acc = Encode(ACC.toNumberImpl() + 1.);
CHECK_EXCEPTION;
- traceDoubleValue(function, traceSlot);
}
MOTH_END_INSTR(Increment)
MOTH_BEGIN_INSTR(Decrement)
if (Q_LIKELY(ACC.integerCompatible())) {
- acc = sub_int32(ACC.int_32(), 1, function->traceInfo(traceSlot));
+ acc = sub_int32(ACC.int_32(), 1);
} else if (ACC.isDouble()) {
acc = QV4::Encode(ACC.doubleValue() - 1.);
- traceDoubleValue(function, traceSlot);
} else {
acc = Encode(ACC.toNumberImpl() - 1.);
CHECK_EXCEPTION;
- traceDoubleValue(function, traceSlot);
}
MOTH_END_INSTR(Decrement)
MOTH_BEGIN_INSTR(Add)
const Value left = STACK_VALUE(lhs);
if (Q_LIKELY(Value::integerCompatible(left, ACC))) {
- acc = add_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot));
+ acc = add_int32(left.int_32(), ACC.int_32());
} else if (left.isNumber() && ACC.isNumber()) {
acc = Encode(left.asDouble() + ACC.asDouble());
- traceDoubleValue(function, traceSlot);
} else {
STORE_ACC();
- acc = Runtime::method_add(engine, left, accumulator);
+ acc = Runtime::Add::call(engine, left, accumulator);
CHECK_EXCEPTION;
- traceOtherValue(function, traceSlot);
}
MOTH_END_INSTR(Add)
MOTH_BEGIN_INSTR(Sub)
const Value left = STACK_VALUE(lhs);
if (Q_LIKELY(Value::integerCompatible(left, ACC))) {
- acc = sub_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot));
+ acc = sub_int32(left.int_32(), ACC.int_32());
} else if (left.isNumber() && ACC.isNumber()) {
acc = Encode(left.asDouble() - ACC.asDouble());
- traceDoubleValue(function, traceSlot);
} else {
STORE_ACC();
- acc = Runtime::method_sub(left, accumulator);
+ acc = Runtime::Sub::call(left, accumulator);
CHECK_EXCEPTION;
- traceOtherValue(function, traceSlot);
}
MOTH_END_INSTR(Sub)
@@ -1376,7 +1249,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
double base = left.toNumber();
double exp = ACC.toNumber();
if (qIsInf(exp) && (base == 1 || base == -1))
- acc = Encode(qSNaN());
+ acc = Encode(qQNaN());
else
acc = Encode(pow(base,exp));
MOTH_END_INSTR(Exp)
@@ -1384,29 +1257,26 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(Mul)
const Value left = STACK_VALUE(lhs);
if (Q_LIKELY(Value::integerCompatible(left, ACC))) {
- acc = mul_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot));
+ acc = mul_int32(left.int_32(), ACC.int_32());
} else if (left.isNumber() && ACC.isNumber()) {
acc = Encode(left.asDouble() * ACC.asDouble());
- traceDoubleValue(function, traceSlot);
} else {
STORE_ACC();
- acc = Runtime::method_mul(left, accumulator);
+ acc = Runtime::Mul::call(left, accumulator);
CHECK_EXCEPTION;
- traceOtherValue(function, traceSlot);
}
MOTH_END_INSTR(Mul)
MOTH_BEGIN_INSTR(Div)
STORE_ACC();
- acc = Runtime::method_div(STACK_VALUE(lhs), accumulator);
+ acc = Runtime::Div::call(STACK_VALUE(lhs), accumulator);
CHECK_EXCEPTION;
MOTH_END_INSTR(Div)
MOTH_BEGIN_INSTR(Mod)
STORE_ACC();
- acc = Runtime::method_mod(STACK_VALUE(lhs), accumulator);
+ acc = Runtime::Mod::call(STACK_VALUE(lhs), accumulator);
CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
MOTH_END_INSTR(Mod)
MOTH_BEGIN_INSTR(BitAnd)
@@ -1493,7 +1363,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_END_INSTR(ThrowOnNullOrUndefined)
MOTH_BEGIN_INSTR(GetTemplateObject)
- acc = RuntimeHelpers::getTemplateObject(function, index);
+ acc = Runtime::GetTemplateObject::call(function, index);
MOTH_END_INSTR(GetTemplateObject)
MOTH_BEGIN_INSTR(Debug)
@@ -1504,7 +1374,10 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_END_INSTR(Debug)
handleUnwind:
- Q_ASSERT(engine->hasException || frame->unwindLevel);
+ // We do start the exception handler in case of isInterrupted. The exception handler will
+ // immediately abort, due to the same isInterrupted. We don't skip the exception handler
+ // because the current behavior is easier to implement in the JIT.
+ Q_ASSERT(engine->hasException || engine->isInterrupted.loadAcquire() || frame->unwindLevel);
if (!frame->unwindHandler) {
acc = Encode::undefined();
return acc;
diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h
index 8a76e60f20..b3944f5454 100644
--- a/src/qml/jsruntime/qv4vme_moth_p.h
+++ b/src/qml/jsruntime/qv4vme_moth_p.h
@@ -52,6 +52,7 @@
//
#include <private/qv4global_p.h>
+#include <private/qv4staticvalue_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/qml/jsruntime/qv4vtable_p.h b/src/qml/jsruntime/qv4vtable_p.h
index aff1ae82d7..9dda104cd1 100644
--- a/src/qml/jsruntime/qv4vtable_p.h
+++ b/src/qml/jsruntime/qv4vtable_p.h
@@ -58,7 +58,7 @@ namespace QV4 {
struct Lookup;
-struct OwnPropertyKeyIterator {
+struct Q_QML_PRIVATE_EXPORT OwnPropertyKeyIterator {
virtual ~OwnPropertyKeyIterator() = 0;
virtual PropertyKey next(const Object *o, Property *p = nullptr, PropertyAttributes *attrs = nullptr) = 0;
};
@@ -90,8 +90,8 @@ struct VTable
typedef bool (*ResolveLookupSetter)(Object *, ExecutionEngine *, Lookup *, const Value &);
const VTable * const parent;
- quint32 inlinePropertyOffset : 16;
- quint32 nInlineProperties : 16;
+ quint16 inlinePropertyOffset;
+ quint16 nInlineProperties;
quint8 isExecutionContext;
quint8 isString;
quint8 isObject;
diff --git a/src/qml/memory/memory.pri b/src/qml/memory/memory.pri
index 7956e4a9a1..0bc8e90d27 100644
--- a/src/qml/memory/memory.pri
+++ b/src/qml/memory/memory.pri
@@ -1,15 +1,11 @@
INCLUDEPATH += $$PWD
INCLUDEPATH += $$OUT_PWD
-!qmldevtools_build {
SOURCES += \
$$PWD/qv4mm.cpp \
HEADERS += \
$$PWD/qv4mm_p.h \
$$PWD/qv4mmdefs_p.h \
- $$PWD/qv4writebarrier_p.h
-}
-
-HEADERS += \
+ $$PWD/qv4writebarrier_p.h \
$$PWD/qv4heap_p.h
diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h
index 07af5a6f7a..d7cfa193e6 100644
--- a/src/qml/memory/qv4heap_p.h
+++ b/src/qml/memory/qv4heap_p.h
@@ -234,7 +234,7 @@ struct QQmlQPointer {
}
T *data() const {
- return d == nullptr || d->strongref.load() == 0 ? nullptr : qObject;
+ return d == nullptr || d->strongref.loadRelaxed() == 0 ? nullptr : qObject;
}
operator T*() const { return data(); }
inline T* operator->() const { return data(); }
@@ -247,7 +247,7 @@ struct QQmlQPointer {
}
bool isNull() const Q_DECL_NOTHROW
{
- return d == nullptr || qObject == nullptr || d->strongref.load() == 0;
+ return d == nullptr || qObject == nullptr || d->strongref.loadRelaxed() == 0;
}
private:
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp
index cac68bdcaf..c7f52eac6b 100644
--- a/src/qml/memory/qv4mm.cpp
+++ b/src/qml/memory/qv4mm.cpp
@@ -46,6 +46,7 @@
#include <QtCore/qalgorithms.h>
#include <QtCore/private/qnumeric_p.h>
#include <QtCore/qloggingcategory.h>
+#include <private/qv4alloca_p.h>
#include <qqmlengine.h>
#include "PageReservation.h"
#include "PageAllocation.h"
@@ -59,10 +60,10 @@
#include <iostream>
#include <cstdlib>
#include <algorithm>
-#include "qv4alloca_p.h"
#include "qv4profiling_p.h"
#include "qv4mapobject_p.h"
#include "qv4setobject_p.h"
+#include "qv4writebarrier_p.h"
//#define MM_STATS
@@ -111,7 +112,11 @@ enum {
struct MemorySegment {
enum {
+#ifdef Q_OS_RTEMS
+ NumChunks = sizeof(quint64),
+#else
NumChunks = 8*sizeof(quint64),
+#endif
SegmentSize = NumChunks*Chunk::ChunkSize,
};
@@ -846,7 +851,7 @@ MarkStack::MarkStack(ExecutionEngine *engine)
{
base = (Heap::Base **)engine->gcStack->base();
top = base;
- limit = base + ExecutionEngine::GCStackLimit/sizeof(Heap::Base)*3/4;
+ limit = base + engine->maxGCStackSize()/sizeof(Heap::Base)*3/4;
}
void MarkStack::drain()
@@ -999,12 +1004,12 @@ bool MemoryManager::shouldRunGC() const
return false;
}
-size_t dumpBins(BlockAllocator *b, bool printOutput = true)
+static size_t dumpBins(BlockAllocator *b, const char *title)
{
const QLoggingCategory &stats = lcGcAllocatorStats();
size_t totalSlotMem = 0;
- if (printOutput)
- qDebug(stats) << "Slot map:";
+ if (title)
+ qDebug(stats) << "Slot map for" << title << "allocator:";
for (uint i = 0; i < BlockAllocator::NumBins; ++i) {
uint nEntries = 0;
HeapItem *h = b->freeBins[i];
@@ -1013,7 +1018,7 @@ size_t dumpBins(BlockAllocator *b, bool printOutput = true)
totalSlotMem += h->freeData.availableSlots;
h = h->freeData.next;
}
- if (printOutput)
+ if (title)
qDebug(stats) << " number of entries in slot" << i << ":" << nEntries;
}
SDUMP() << " large slot map";
@@ -1023,7 +1028,7 @@ size_t dumpBins(BlockAllocator *b, bool printOutput = true)
h = h->freeData.next;
}
- if (printOutput)
+ if (title)
qDebug(stats) << " total mem in bins" << totalSlotMem*Chunk::SlotSize;
return totalSlotMem*Chunk::SlotSize;
}
@@ -1064,7 +1069,8 @@ void MemoryManager::runGC()
size_t oldChunks = blockAllocator.chunks.size();
qDebug(stats) << "Allocated" << totalMem << "bytes in" << oldChunks << "chunks";
qDebug(stats) << "Fragmented memory before GC" << (totalMem - usedBefore);
- dumpBins(&blockAllocator);
+ dumpBins(&blockAllocator, "Block");
+ dumpBins(&icAllocator, "InternalClass");
QElapsedTimer t;
t.start();
@@ -1082,7 +1088,8 @@ void MemoryManager::runGC()
qDebug(stats) << " new unmanaged heap:" << unmanagedHeapSize;
qDebug(stats) << " unmanaged heap limit:" << unmanagedHeapSizeGCLimit;
}
- size_t memInBins = dumpBins(&blockAllocator);
+ size_t memInBins = dumpBins(&blockAllocator, "Block")
+ + dumpBins(&icAllocator, "InternalClasss");
qDebug(stats) << "Marked object in" << markTime << "us.";
qDebug(stats) << " " << markStackSize << "objects marked";
qDebug(stats) << "Sweeped object in" << sweepTime << "us.";
@@ -1104,7 +1111,8 @@ void MemoryManager::runGC()
qDebug(stats) << "Used memory after GC:" << usedAfter;
qDebug(stats) << "Freed up bytes :" << (usedBefore - usedAfter);
qDebug(stats) << "Freed up chunks :" << (oldChunks - blockAllocator.chunks.size());
- size_t lost = blockAllocator.allocatedMem() - memInBins - usedAfter;
+ size_t lost = blockAllocator.allocatedMem() + icAllocator.allocatedMem()
+ - memInBins - usedAfter;
if (lost)
qDebug(stats) << "!!!!!!!!!!!!!!!!!!!!! LOST MEM:" << lost << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
if (largeItemsBefore || largeItemsAfter) {
@@ -1126,7 +1134,9 @@ void MemoryManager::runGC()
if (aggressiveGC) {
// ensure we don't 'loose' any memory
Q_ASSERT(blockAllocator.allocatedMem()
- == blockAllocator.usedMem() + dumpBins(&blockAllocator, false));
+ == blockAllocator.usedMem() + dumpBins(&blockAllocator, nullptr));
+ Q_ASSERT(icAllocator.allocatedMem()
+ == icAllocator.usedMem() + dumpBins(&icAllocator, nullptr));
}
usedSlotsAfterLastFullSweep = blockAllocator.usedSlotsAfterLastSweep + icAllocator.usedSlotsAfterLastSweep;
diff --git a/src/qml/memory/qv4writebarrier_p.h b/src/qml/memory/qv4writebarrier_p.h
index 8b04aa6cb1..c65cfc0269 100644
--- a/src/qml/memory/qv4writebarrier_p.h
+++ b/src/qml/memory/qv4writebarrier_p.h
@@ -51,7 +51,6 @@
//
#include <private/qv4global_p.h>
-#include <private/qv4enginebase_p.h>
QT_BEGIN_NAMESPACE
@@ -60,6 +59,7 @@ QT_BEGIN_NAMESPACE
#define WRITEBARRIER(x) (1/WRITEBARRIER_##x == 1)
namespace QV4 {
+struct EngineBase;
namespace WriteBarrier {
diff --git a/src/qml/parser/parser.pri b/src/qml/parser/parser.pri
index 2c0175c94b..e15730f5d1 100644
--- a/src/qml/parser/parser.pri
+++ b/src/qml/parser/parser.pri
@@ -4,11 +4,9 @@ HEADERS += \
$$PWD/qqmljsastvisitor_p.h \
$$PWD/qqmljsengine_p.h \
$$PWD/qqmljslexer_p.h \
- $$PWD/qqmljsmemorypool_p.h \
$$PWD/qqmljsglobal_p.h \
$$PWD/qqmljskeywords_p.h \
$$PWD/qqmljsengine_p.h \
- $$PWD/qqmljsglobal_p.h \
$$PWD/qqmljssourcelocation_p.h
SOURCES += \
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g
index 4ad9057ced..8ac7633ae0 100644
--- a/src/qml/parser/qqmljs.g
+++ b/src/qml/parser/qqmljs.g
@@ -58,6 +58,7 @@
%token T_MINUS "-" T_MINUS_EQ "-=" T_MINUS_MINUS "--"
%token T_NEW "new" T_NOT "!" T_NOT_EQ "!="
%token T_NOT_EQ_EQ "!==" T_NUMERIC_LITERAL "numeric literal" T_OR "|"
+%token T_VERSION_NUMBER "version number"
%token T_OR_EQ "|=" T_OR_OR "||" T_PLUS "+"
%token T_PLUS_EQ "+=" T_PLUS_PLUS "++" T_QUESTION "?"
%token T_RBRACE "}" T_RBRACKET "]" T_REMAINDER "%"
@@ -245,6 +246,7 @@
#include <private/qqmljsgrammar_p.h>
#include <private/qqmljsast_p.h>
#include <private/qqmljsengine_p.h>
+#include <private/qqmljsdiagnosticmessage_p.h>
#include <QtCore/qlist.h>
#include <QtCore/qstring.h>
@@ -297,6 +299,9 @@ public:
AST::ExportsList *ExportsList;
AST::ExportClause *ExportClause;
AST::ExportDeclaration *ExportDeclaration;
+ AST::TypeAnnotation *TypeAnnotation;
+ AST::TypeArgumentList *TypeArgumentList;
+ AST::Type *Type;
AST::UiProgram *UiProgram;
AST::UiHeaderItemList *UiHeaderItemList;
@@ -314,6 +319,7 @@ public:
AST::UiArrayMemberList *UiArrayMemberList;
AST::UiQualifiedId *UiQualifiedId;
AST::UiEnumMemberList *UiEnumMemberList;
+ AST::UiVersionSpecifier *UiVersionSpecifier;
};
public:
@@ -365,7 +371,7 @@ public:
inline DiagnosticMessage diagnosticMessage() const
{
for (const DiagnosticMessage &d : diagnostic_messages) {
- if (d.kind != DiagnosticMessage::Warning)
+ if (d.type != QtWarningMsg)
return d;
}
@@ -376,10 +382,10 @@ public:
{ return diagnosticMessage().message; }
inline int errorLineNumber() const
- { return diagnosticMessage().loc.startLine; }
+ { return diagnosticMessage().line; }
inline int errorColumnNumber() const
- { return diagnosticMessage().loc.startColumn; }
+ { return diagnosticMessage().column; }
protected:
bool parse(int startToken);
@@ -403,13 +409,26 @@ protected:
void pushToken(int token);
int lookaheadToken(Lexer *lexer);
+ static DiagnosticMessage compileError(const AST::SourceLocation &location,
+ const QString &message, QtMsgType kind = QtCriticalMsg)
+ {
+ DiagnosticMessage error;
+ error.line = location.startLine;
+ error.column = location.startColumn;
+ error.message = message;
+ error.type = kind;
+ return error;
+ }
+
void syntaxError(const AST::SourceLocation &location, const char *message) {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location, QLatin1String(message)));
+ diagnostic_messages.append(compileError(location, QLatin1String(message)));
}
void syntaxError(const AST::SourceLocation &location, const QString &message) {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location, message));
+ diagnostic_messages.append(compileError(location, message));
}
+ bool ensureNoFunctionTypeAnnotations(AST::TypeAnnotation *returnTypeAnnotation, AST::FormalParameterList *formals);
+
protected:
Engine *driver;
MemoryPool *pool;
@@ -580,6 +599,21 @@ int Parser::lookaheadToken(Lexer *lexer)
return yytoken;
}
+bool Parser::ensureNoFunctionTypeAnnotations(AST::TypeAnnotation *returnValueAnnotation, AST::FormalParameterList *formals)
+{
+ for (auto formal = formals; formal; formal = formal->next) {
+ if (formal->element && formal->element->typeAnnotation) {
+ syntaxError(formal->element->typeAnnotation->firstSourceLocation(), "Type annotations are not permitted in function parameters in JavaScript functions");
+ return false;
+ }
+ }
+ if (returnValueAnnotation) {
+ syntaxError(returnValueAnnotation->firstSourceLocation(), "Type annotations are not permitted for the return value of JavaScript functions");
+ return false;
+ }
+ return true;
+}
+
//#define PARSER_DEBUG
bool Parser::parse(int startToken)
@@ -769,8 +803,10 @@ UiHeaderItemList: UiHeaderItemList UiImport;
PragmaId: JsIdentifier;
-UiPragma: T_PRAGMA PragmaId T_AUTOMATIC_SEMICOLON;
-UiPragma: T_PRAGMA PragmaId T_SEMICOLON;
+Semicolon: T_AUTOMATIC_SEMICOLON;
+Semicolon: T_SEMICOLON;
+
+UiPragma: T_PRAGMA PragmaId Semicolon;
/.
case $rule_number: {
AST::UiPragma *pragma = new (pool) AST::UiPragma(stringRef(2));
@@ -782,28 +818,52 @@ UiPragma: T_PRAGMA PragmaId T_SEMICOLON;
ImportId: MemberExpression;
-UiImport: UiImportHead T_AUTOMATIC_SEMICOLON;
-UiImport: UiImportHead T_SEMICOLON;
+UiImport: UiImportHead Semicolon;
/.
case $rule_number: {
sym(1).UiImport->semicolonToken = loc(2);
} break;
./
-UiImport: UiImportHead T_NUMERIC_LITERAL T_AUTOMATIC_SEMICOLON;
-UiImport: UiImportHead T_NUMERIC_LITERAL T_SEMICOLON;
+UiVersionSpecifier: T_VERSION_NUMBER T_DOT T_VERSION_NUMBER;
/.
case $rule_number: {
- sym(1).UiImport->versionToken = loc(2);
+ auto version = new (pool) AST::UiVersionSpecifier(sym(1).dval, sym(3).dval);
+ version->majorToken = loc(1);
+ version->minorToken = loc(3);
+ sym(1).UiVersionSpecifier = version;
+ } break;
+./
+
+
+UiVersionSpecifier: T_VERSION_NUMBER;
+/.
+ case $rule_number: {
+ auto version = new (pool) AST::UiVersionSpecifier(sym(1).dval, 0);
+ version->majorToken = loc(1);
+ sym(1).UiVersionSpecifier = version;
+ } break;
+./
+
+UiImport: UiImportHead UiVersionSpecifier Semicolon;
+/.
+ case $rule_number: {
+ auto versionToken = loc(2);
+ auto version = sym(2).UiVersionSpecifier;
+ sym(1).UiImport->version = version;
+ if (version->minorToken.isValid()) {
+ versionToken.length += version->minorToken.length + (version->minorToken.offset - versionToken.offset - versionToken.length);
+ }
+ sym(1).UiImport->versionToken = versionToken;
sym(1).UiImport->semicolonToken = loc(3);
} break;
./
-UiImport: UiImportHead T_NUMERIC_LITERAL T_AS QmlIdentifier T_AUTOMATIC_SEMICOLON;
-UiImport: UiImportHead T_NUMERIC_LITERAL T_AS QmlIdentifier T_SEMICOLON;
+UiImport: UiImportHead UiVersionSpecifier T_AS QmlIdentifier Semicolon;
/.
case $rule_number: {
sym(1).UiImport->versionToken = loc(2);
+ sym(1).UiImport->version = sym(2).UiVersionSpecifier;
sym(1).UiImport->asToken = loc(3);
sym(1).UiImport->importIdToken = loc(4);
sym(1).UiImport->importId = stringRef(4);
@@ -811,8 +871,7 @@ UiImport: UiImportHead T_NUMERIC_LITERAL T_AS QmlIdentifier T_SEMICOLON;
} break;
./
-UiImport: UiImportHead T_AS QmlIdentifier T_AUTOMATIC_SEMICOLON;
-UiImport: UiImportHead T_AS QmlIdentifier T_SEMICOLON;
+UiImport: UiImportHead T_AS QmlIdentifier Semicolon;
/.
case $rule_number: {
sym(1).UiImport->asToken = loc(2);
@@ -840,7 +899,7 @@ UiImportHead: T_IMPORT ImportId;
if (node) {
node->importToken = loc(1);
} else {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
+ diagnostic_messages.append(compileError(loc(1),
QLatin1String("Expected a qualified name id or a string literal")));
return false; // ### remove me
@@ -1045,6 +1104,17 @@ UiParameterListOpt: UiParameterList;
} break;
./
+UiParameterList: QmlIdentifier T_COLON UiPropertyType;
+/.
+ case $rule_number: {
+ AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(3).UiQualifiedId->finish(), stringRef(1));
+ node->identifierToken = loc(1);
+ node->colonToken = loc(2);
+ node->propertyTypeToken = loc(3);
+ sym(1).Node = node;
+ } break;
+./
+
UiParameterList: UiPropertyType QmlIdentifier;
/.
case $rule_number: {
@@ -1055,6 +1125,18 @@ UiParameterList: UiPropertyType QmlIdentifier;
} break;
./
+UiParameterList: UiParameterList T_COMMA QmlIdentifier T_COLON UiPropertyType;
+/.
+ case $rule_number: {
+ AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, sym(5).UiQualifiedId->finish(), stringRef(3));
+ node->propertyTypeToken = loc(5);
+ node->commaToken = loc(2);
+ node->identifierToken = loc(3);
+ node->colonToken = loc(4);
+ sym(1).Node = node;
+ } break;
+./
+
UiParameterList: UiParameterList T_COMMA UiPropertyType QmlIdentifier;
/.
case $rule_number: {
@@ -1066,8 +1148,7 @@ UiParameterList: UiParameterList T_COMMA UiPropertyType QmlIdentifier;
} break;
./
-UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_AUTOMATIC_SEMICOLON;
-UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_SEMICOLON;
+UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN Semicolon;
/.
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2));
@@ -1081,8 +1162,7 @@ UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_SEM
} break;
./
-UiObjectMember: T_SIGNAL T_IDENTIFIER T_AUTOMATIC_SEMICOLON;
-UiObjectMember: T_SIGNAL T_IDENTIFIER T_SEMICOLON;
+UiObjectMember: T_SIGNAL T_IDENTIFIER Semicolon;
/.
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2));
@@ -1095,8 +1175,7 @@ UiObjectMember: T_SIGNAL T_IDENTIFIER T_SEMICOLON;
} break;
./
-UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_AUTOMATIC_SEMICOLON;
-UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_SEMICOLON;
+UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier Semicolon;
/.
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6));
@@ -1110,8 +1189,7 @@ UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T
} break;
./
-UiObjectMember: T_READONLY T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_AUTOMATIC_SEMICOLON;
-UiObjectMember: T_READONLY T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_SEMICOLON;
+UiObjectMember: T_READONLY T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier Semicolon;
/.
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(5).UiQualifiedId->finish(), stringRef(7));
@@ -1127,8 +1205,7 @@ UiObjectMember: T_READONLY T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlI
} break;
./
-UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_AUTOMATIC_SEMICOLON;
-UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_SEMICOLON;
+UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier Semicolon;
/.
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3));
@@ -1140,8 +1217,7 @@ UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_SEMICOLON;
} break;
./
-UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_AUTOMATIC_SEMICOLON;
-UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_SEMICOLON;
+UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier Semicolon;
/.
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4));
@@ -1155,8 +1231,7 @@ UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_SEMICOLON;
} break;
./
-UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_AUTOMATIC_SEMICOLON;
-UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_SEMICOLON;
+UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier Semicolon;
/.
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(5).UiQualifiedId->finish(), stringRef(7));
@@ -1171,8 +1246,13 @@ UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlId
sym(1).Node = node;
} break;
./
+OptionalSemicolon: | Semicolon;
+/.
+/* we need OptionalSemicolon because UiScriptStatement might already parse the last semicolon
+ and then we would miss a semicolon (see tests/auto/quick/qquickvisualdatamodel/data/objectlist.qml)*/
+ ./
-UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement;
+UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement OptionalSemicolon;
/.
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3), sym(5).Statement);
@@ -1184,7 +1264,7 @@ UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatemen
} break;
./
-UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement;
+UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement OptionalSemicolon;
/.
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4), sym(6).Statement);
@@ -1198,7 +1278,7 @@ UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScr
} break;
./
-UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement;
+UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement OptionalSemicolon;
/.
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4), sym(6).Statement);
@@ -1212,7 +1292,7 @@ UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScri
} break;
./
-UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET;
+UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET Semicolon;
/.
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6));
@@ -1238,7 +1318,7 @@ UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T
} break;
./
-UiObjectMember: T_READONLY T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET;
+UiObjectMember: T_READONLY T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET Semicolon;
/.
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(5).UiQualifiedId->finish(), stringRef(7));
@@ -1266,7 +1346,7 @@ UiObjectMember: T_READONLY T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlI
} break;
./
-UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer;
+UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer Semicolon;
/.
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3));
@@ -1289,7 +1369,7 @@ UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatem
} break;
./
-UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer;
+UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer Semicolon;
/.
case $rule_number: {
AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4));
@@ -1314,13 +1394,21 @@ UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON Expre
} break;
./
-UiObjectMember: FunctionDeclaration;
+UiObjectMember: FunctionDeclarationWithTypes;
/.
case $rule_number: {
sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node);
} break;
./
+UiObjectMember: GeneratorExpression;
+/.
+ case $rule_number: {
+ auto node = new (pool) AST::UiSourceElement(sym(1).Node);
+ sym(1).Node = node;
+ } break;
+./
+
UiObjectMember: VariableStatement;
/.
case $rule_number: {
@@ -1332,8 +1420,8 @@ UiQualifiedId: MemberExpression;
/.
case $rule_number: {
if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken,
- QLatin1String("Ignored annotation")));
+ diagnostic_messages.append(compileError(mem->lbracketToken,
+ QLatin1String("Ignored annotation"), QtWarningMsg));
sym(1).Expression = mem->base;
}
@@ -1343,7 +1431,7 @@ UiQualifiedId: MemberExpression;
} else {
sym(1).UiQualifiedId = 0;
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
+ diagnostic_messages.append(compileError(loc(1),
QLatin1String("Expected a qualified name id")));
return false; // ### recover
@@ -1430,6 +1518,63 @@ IdentifierReference: JsIdentifier;
BindingIdentifier: IdentifierReference;
--------------------------------------------------------------------------------------------------------
+-- Types
+--------------------------------------------------------------------------------------------------------
+
+TypeArguments: Type;
+/.
+ case $rule_number: {
+ sym(1).TypeArgumentList = new (pool) AST::TypeArgumentList(sym(1).Type);
+ } break;
+./
+
+TypeArguments: TypeArguments T_COMMA Type;
+/.
+ case $rule_number: {
+ sym(1).TypeArgumentList = new (pool) AST::TypeArgumentList(sym(1).TypeArgumentList, sym(3).Type);
+ } break;
+./
+
+Type: UiQualifiedId T_LT TypeArguments T_GT;
+/.
+ case $rule_number: {
+ sym(1).Type = new (pool) AST::Type(sym(1).UiQualifiedId, sym(3).TypeArgumentList->finish());
+ } break;
+./
+
+Type: T_RESERVED_WORD;
+/.
+ case $rule_number: {
+ AST::UiQualifiedId *id = new (pool) AST::UiQualifiedId(stringRef(1));
+ id->identifierToken = loc(1);
+ sym(1).Type = new (pool) AST::Type(id->finish());
+ } break;
+./
+
+Type: UiQualifiedId;
+/.
+ case $rule_number: {
+ sym(1).Type = new (pool) AST::Type(sym(1).UiQualifiedId);
+ } break;
+./
+
+TypeAnnotation: T_COLON Type;
+/.
+ case $rule_number: {
+ sym(1).TypeAnnotation = new (pool) AST::TypeAnnotation(sym(2).Type);
+ sym(1).TypeAnnotation->colonToken = loc(1);
+ } break;
+./
+
+TypeAnnotationOpt: TypeAnnotation;
+TypeAnnotationOpt: ;
+/.
+ case $rule_number: {
+ sym(1).TypeAnnotation = nullptr;
+ } break;
+./
+
+--------------------------------------------------------------------------------------------------------
-- Expressions
--------------------------------------------------------------------------------------------------------
@@ -1590,7 +1735,7 @@ RegularExpressionLiteral: T_DIVIDE_EQ;
scan_regexp: {
bool rx = lexer->scanRegExp(prefix);
if (!rx) {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage()));
+ diagnostic_messages.append(compileError(location(lexer), lexer->errorMessage()));
return false;
}
@@ -2759,8 +2904,7 @@ StatementListItem: Statement;
} break;
./
-StatementListItem: ExpressionStatementLookahead T_FORCE_DECLARATION Declaration T_AUTOMATIC_SEMICOLON;
-StatementListItem: ExpressionStatementLookahead T_FORCE_DECLARATION Declaration T_SEMICOLON;
+StatementListItem: ExpressionStatementLookahead T_FORCE_DECLARATION Declaration Semicolon;
/.
case $rule_number: {
sym(1).Node = new (pool) AST::StatementList(sym(3).FunctionDeclaration);
@@ -2810,14 +2954,20 @@ VarDeclaration: Var VariableDeclarationList;
VarDeclaration_In: Var VariableDeclarationList_In;
/.
case $rule_number: {
- AST::VariableStatement *node = new (pool) AST::VariableStatement(sym(2).VariableDeclarationList->finish(sym(1).scope));
+ AST::VariableDeclarationList *declarations = sym(2).VariableDeclarationList->finish(sym(1).scope);
+ for (auto it = declarations; it; it = it->next) {
+ if (it->declaration && it->declaration->typeAnnotation) {
+ syntaxError(it->declaration->typeAnnotation->firstSourceLocation(), "Type annotations are not permitted in variable declarations");
+ return false;
+ }
+ }
+ AST::VariableStatement *node = new (pool) AST::VariableStatement(declarations);
node->declarationKindToken = loc(1);
sym(1).Node = node;
} break;
./
-VariableStatement: VarDeclaration_In T_AUTOMATIC_SEMICOLON;
-VariableStatement: VarDeclaration_In T_SEMICOLON;
+VariableStatement: VarDeclaration_In Semicolon;
BindingList: LexicalBinding_In;
/. case $rule_number: Q_FALLTHROUGH(); ./
@@ -2847,22 +2997,22 @@ VariableDeclarationList_In: VariableDeclarationList_In T_COMMA VariableDeclarati
} break;
./
-LexicalBinding: BindingIdentifier InitializerOpt;
+LexicalBinding: BindingIdentifier TypeAnnotationOpt InitializerOpt;
/. case $rule_number: Q_FALLTHROUGH(); ./
-LexicalBinding_In: BindingIdentifier InitializerOpt_In;
+LexicalBinding_In: BindingIdentifier TypeAnnotationOpt InitializerOpt_In;
/. case $rule_number: Q_FALLTHROUGH(); ./
-VariableDeclaration: BindingIdentifier InitializerOpt;
+VariableDeclaration: BindingIdentifier TypeAnnotationOpt InitializerOpt;
/. case $rule_number: Q_FALLTHROUGH(); ./
-VariableDeclaration_In: BindingIdentifier InitializerOpt_In;
+VariableDeclaration_In: BindingIdentifier TypeAnnotationOpt InitializerOpt_In;
/.
case $rule_number: {
- auto *node = new (pool) AST::PatternElement(stringRef(1), sym(2).Expression);
+ auto *node = new (pool) AST::PatternElement(stringRef(1), sym(2).TypeAnnotation, sym(3).Expression);
node->identifierToken = loc(1);
sym(1).Node = node;
// if initializer is an anonymous function expression, we need to assign identifierref as it's name
- if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression))
+ if (auto *f = asAnonymousFunctionDefinition(sym(3).Expression))
f->name = stringRef(1);
- if (auto *c = asAnonymousClassDefinition(sym(2).Expression))
+ if (auto *c = asAnonymousClassDefinition(sym(3).Expression))
c->name = stringRef(1);
} break;
./
@@ -3012,15 +3162,15 @@ BindingProperty: PropertyName T_COLON BindingPattern InitializerOpt_In;
} break;
./
-BindingElement: BindingIdentifier InitializerOpt_In;
+BindingElement: BindingIdentifier TypeAnnotationOpt InitializerOpt_In;
/.
case $rule_number: {
- AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(1), sym(2).Expression);
+ AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(1), sym(2).TypeAnnotation, sym(3).Expression);
node->identifierToken = loc(1);
// if initializer is an anonymous function expression, we need to assign identifierref as it's name
- if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression))
+ if (auto *f = asAnonymousFunctionDefinition(sym(3).Expression))
f->name = stringRef(1);
- if (auto *c = asAnonymousClassDefinition(sym(2).Expression))
+ if (auto *c = asAnonymousClassDefinition(sym(3).Expression))
c->name = stringRef(1);
sym(1).Node = node;
} break;
@@ -3037,7 +3187,7 @@ BindingElement: BindingPattern InitializerOpt_In;
BindingRestElement: T_ELLIPSIS BindingIdentifier;
/.
case $rule_number: {
- AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(2), nullptr, AST::PatternElement::RestElement);
+ AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(2), /*type annotation*/nullptr, nullptr, AST::PatternElement::RestElement);
node->identifierToken = loc(2);
sym(1).Node = node;
} break;
@@ -3087,8 +3237,7 @@ ExpressionStatementLookahead: ;
} break;
./
-ExpressionStatement: Expression_In T_AUTOMATIC_SEMICOLON;
-ExpressionStatement: Expression_In T_SEMICOLON;
+ExpressionStatement: Expression_In Semicolon;
/.
case $rule_number: {
AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression);
@@ -3121,9 +3270,8 @@ IfStatement: T_IF T_LPAREN Expression_In T_RPAREN Statement;
./
-IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression_In T_RPAREN T_AUTOMATIC_SEMICOLON;
IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression_In T_RPAREN T_COMPATIBILITY_SEMICOLON; -- for JSC/V8 compatibility
-IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression_In T_RPAREN T_SEMICOLON;
+IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression_In T_RPAREN Semicolon;
/.
case $rule_number: {
AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression);
@@ -3227,12 +3375,16 @@ IterationStatement: T_FOR T_LPAREN ForDeclaration InOrOf Expression_In T_RPAREN
} break;
./
-ForDeclaration: LetOrConst BindingIdentifier;
+ForDeclaration: LetOrConst BindingIdentifier TypeAnnotationOpt;
/. case $rule_number: Q_FALLTHROUGH(); ./
-ForDeclaration: Var BindingIdentifier;
+ForDeclaration: Var BindingIdentifier TypeAnnotationOpt;
/.
case $rule_number: {
- auto *node = new (pool) AST::PatternElement(stringRef(2), nullptr);
+ if (auto typeAnnotation = sym(3).TypeAnnotation) {
+ syntaxError(typeAnnotation->firstSourceLocation(), "Type annotations are not permitted in variable declarations");
+ return false;
+ }
+ auto *node = new (pool) AST::PatternElement(stringRef(2), sym(3).TypeAnnotation, nullptr);
node->identifierToken = loc(2);
node->scope = sym(1).scope;
node->isForDeclaration = true;
@@ -3252,8 +3404,7 @@ ForDeclaration: Var BindingPattern;
} break;
./
-ContinueStatement: T_CONTINUE T_AUTOMATIC_SEMICOLON;
-ContinueStatement: T_CONTINUE T_SEMICOLON;
+ContinueStatement: T_CONTINUE Semicolon;
/.
case $rule_number: {
AST::ContinueStatement *node = new (pool) AST::ContinueStatement();
@@ -3263,8 +3414,7 @@ ContinueStatement: T_CONTINUE T_SEMICOLON;
} break;
./
-ContinueStatement: T_CONTINUE IdentifierReference T_AUTOMATIC_SEMICOLON;
-ContinueStatement: T_CONTINUE IdentifierReference T_SEMICOLON;
+ContinueStatement: T_CONTINUE IdentifierReference Semicolon;
/.
case $rule_number: {
AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2));
@@ -3275,8 +3425,7 @@ ContinueStatement: T_CONTINUE IdentifierReference T_SEMICOLON;
} break;
./
-BreakStatement: T_BREAK T_AUTOMATIC_SEMICOLON;
-BreakStatement: T_BREAK T_SEMICOLON;
+BreakStatement: T_BREAK Semicolon;
/.
case $rule_number: {
AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef());
@@ -3286,8 +3435,7 @@ BreakStatement: T_BREAK T_SEMICOLON;
} break;
./
-BreakStatement: T_BREAK IdentifierReference T_AUTOMATIC_SEMICOLON;
-BreakStatement: T_BREAK IdentifierReference T_SEMICOLON;
+BreakStatement: T_BREAK IdentifierReference Semicolon;
/.
case $rule_number: {
AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2));
@@ -3298,8 +3446,7 @@ BreakStatement: T_BREAK IdentifierReference T_SEMICOLON;
} break;
./
-ReturnStatement: T_RETURN ExpressionOpt_In T_AUTOMATIC_SEMICOLON;
-ReturnStatement: T_RETURN ExpressionOpt_In T_SEMICOLON;
+ReturnStatement: T_RETURN ExpressionOpt_In Semicolon;
/.
case $rule_number: {
if (!functionNestingLevel) {
@@ -3423,8 +3570,7 @@ LabelledItem: ExpressionStatementLookahead T_FORCE_DECLARATION FunctionDeclarati
} break;
./
-ThrowStatement: T_THROW Expression_In T_AUTOMATIC_SEMICOLON;
-ThrowStatement: T_THROW Expression_In T_SEMICOLON;
+ThrowStatement: T_THROW Expression_In Semicolon;
/.
case $rule_number: {
AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression);
@@ -3501,8 +3647,7 @@ CatchParameter: BindingPattern;
} break;
./
-DebuggerStatement: T_DEBUGGER T_AUTOMATIC_SEMICOLON; -- automatic semicolon
-DebuggerStatement: T_DEBUGGER T_SEMICOLON;
+DebuggerStatement: T_DEBUGGER Semicolon;
/.
case $rule_number: {
AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement();
@@ -3519,59 +3664,85 @@ DebuggerStatement: T_DEBUGGER T_SEMICOLON;
-- otherwise conflict.
Function: T_FUNCTION %prec REDUCE_HERE;
-FunctionDeclaration: Function BindingIdentifier T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
+FunctionDeclaration: Function BindingIdentifier T_LPAREN FormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace;
/.
case $rule_number: {
- AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList);
+ if (!ensureNoFunctionTypeAnnotations(sym(6).TypeAnnotation, sym(4).FormalParameterList))
+ return false;
+ AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(8).StatementList,
+ /*type annotation*/nullptr);
node->functionToken = loc(1);
node->identifierToken = loc(2);
node->lparenToken = loc(3);
node->rparenToken = loc(5);
- node->lbraceToken = loc(6);
- node->rbraceToken = loc(8);
+ node->lbraceToken = loc(7);
+ node->rbraceToken = loc(9);
sym(1).Node = node;
} break;
./
+FunctionDeclarationWithTypes: Function BindingIdentifier T_LPAREN FormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace;
+/.
+ case $rule_number: {
+ AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(8).StatementList,
+ sym(6).TypeAnnotation);
+ node->functionToken = loc(1);
+ node->identifierToken = loc(2);
+ node->lparenToken = loc(3);
+ node->rparenToken = loc(5);
+ node->lbraceToken = loc(7);
+ node->rbraceToken = loc(9);
+ sym(1).Node = node;
+ } break;
+./
FunctionDeclaration_Default: FunctionDeclaration;
-FunctionDeclaration_Default: Function T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
+FunctionDeclaration_Default: Function T_LPAREN FormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace;
/.
case $rule_number: {
- AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(QStringRef(), sym(3).FormalParameterList, sym(6).StatementList);
+ if (!ensureNoFunctionTypeAnnotations(sym(5).TypeAnnotation, sym(3).FormalParameterList))
+ return false;
+ AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(QStringRef(), sym(3).FormalParameterList, sym(7).StatementList,
+ /*type annotation*/nullptr);
node->functionToken = loc(1);
node->lparenToken = loc(2);
node->rparenToken = loc(4);
- node->lbraceToken = loc(5);
- node->rbraceToken = loc(7);
+ node->lbraceToken = loc(6);
+ node->rbraceToken = loc(8);
sym(1).Node = node;
} break;
./
-FunctionExpression: T_FUNCTION BindingIdentifier T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
+FunctionExpression: T_FUNCTION BindingIdentifier T_LPAREN FormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace;
/.
case $rule_number: {
- AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList);
+ if (!ensureNoFunctionTypeAnnotations(sym(6).TypeAnnotation, sym(4).FormalParameterList))
+ return false;
+ AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(8).StatementList,
+ /*type annotation*/nullptr);
node->functionToken = loc(1);
if (! stringRef(2).isNull())
node->identifierToken = loc(2);
node->lparenToken = loc(3);
node->rparenToken = loc(5);
- node->lbraceToken = loc(6);
- node->rbraceToken = loc(8);
+ node->lbraceToken = loc(7);
+ node->rbraceToken = loc(9);
sym(1).Node = node;
} break;
./
-FunctionExpression: T_FUNCTION T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
+FunctionExpression: T_FUNCTION T_LPAREN FormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace;
/.
case $rule_number: {
- AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(6).StatementList);
+ if (!ensureNoFunctionTypeAnnotations(sym(5).TypeAnnotation, sym(3).FormalParameterList))
+ return false;
+ AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(7).StatementList,
+ /*type annotation*/nullptr);
node->functionToken = loc(1);
node->lparenToken = loc(2);
node->rparenToken = loc(4);
- node->lbraceToken = loc(5);
- node->rbraceToken = loc(7);
+ node->lbraceToken = loc(6);
+ node->rbraceToken = loc(8);
sym(1).Node = node;
} break;
./
@@ -3681,7 +3852,7 @@ ArrowFunction_In: ArrowParameters T_ARROW ConciseBodyLookahead T_FORCE_BLOCK Fun
ArrowParameters: BindingIdentifier;
/.
case $rule_number: {
- AST::PatternElement *e = new (pool) AST::PatternElement(stringRef(1), nullptr, AST::PatternElement::Binding);
+ AST::PatternElement *e = new (pool) AST::PatternElement(stringRef(1), /*type annotation*/nullptr, nullptr, AST::PatternElement::Binding);
e->identifierToken = loc(1);
sym(1).FormalParameterList = (new (pool) AST::FormalParameterList(nullptr, e))->finish(pool);
} break;
@@ -3715,30 +3886,34 @@ ConciseBodyLookahead: ;
} break;
./
-MethodDefinition: PropertyName T_LPAREN StrictFormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
+MethodDefinition: PropertyName T_LPAREN StrictFormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace;
/.
case $rule_number: {
- AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(1), sym(3).FormalParameterList, sym(6).StatementList);
+ if (!ensureNoFunctionTypeAnnotations(sym(5).TypeAnnotation, sym(3).FormalParameterList))
+ return false;
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(1), sym(3).FormalParameterList, sym(7).StatementList);
f->functionToken = sym(1).PropertyName->firstSourceLocation();
f->lparenToken = loc(2);
f->rparenToken = loc(4);
- f->lbraceToken = loc(5);
- f->rbraceToken = loc(7);
+ f->lbraceToken = loc(6);
+ f->rbraceToken = loc(8);
AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, f, AST::PatternProperty::Method);
node->colonToken = loc(2);
sym(1).Node = node;
} break;
./
-MethodDefinition: T_STAR PropertyName GeneratorLParen StrictFormalParameters T_RPAREN FunctionLBrace GeneratorBody GeneratorRBrace;
+MethodDefinition: T_STAR PropertyName GeneratorLParen StrictFormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace GeneratorBody GeneratorRBrace;
/.
case $rule_number: {
- AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList);
+ if (!ensureNoFunctionTypeAnnotations(sym(6).TypeAnnotation, sym(4).FormalParameterList))
+ return false;
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(8).StatementList);
f->functionToken = sym(2).PropertyName->firstSourceLocation();
f->lparenToken = loc(3);
f->rparenToken = loc(5);
- f->lbraceToken = loc(6);
- f->rbraceToken = loc(8);
+ f->lbraceToken = loc(7);
+ f->rbraceToken = loc(9);
f->isGenerator = true;
AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Method);
node->colonToken = loc(2);
@@ -3747,30 +3922,34 @@ MethodDefinition: T_STAR PropertyName GeneratorLParen StrictFormalParameters T_R
./
-MethodDefinition: T_GET PropertyName T_LPAREN T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
+MethodDefinition: T_GET PropertyName T_LPAREN T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace;
/.
case $rule_number: {
- AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), nullptr, sym(6).StatementList);
+ if (!ensureNoFunctionTypeAnnotations(sym(5).TypeAnnotation, /*formals*/nullptr))
+ return false;
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), nullptr, sym(7).StatementList);
f->functionToken = sym(2).PropertyName->firstSourceLocation();
f->lparenToken = loc(3);
f->rparenToken = loc(4);
- f->lbraceToken = loc(5);
- f->rbraceToken = loc(7);
+ f->lbraceToken = loc(6);
+ f->rbraceToken = loc(8);
AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Getter);
node->colonToken = loc(2);
sym(1).Node = node;
} break;
./
-MethodDefinition: T_SET PropertyName T_LPAREN PropertySetParameterList T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
+MethodDefinition: T_SET PropertyName T_LPAREN PropertySetParameterList T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace;
/.
case $rule_number: {
- AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList);
+ if (!ensureNoFunctionTypeAnnotations(sym(6).TypeAnnotation, sym(4).FormalParameterList))
+ return false;
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(8).StatementList);
f->functionToken = sym(2).PropertyName->firstSourceLocation();
f->lparenToken = loc(3);
f->rparenToken = loc(5);
- f->lbraceToken = loc(6);
- f->rbraceToken = loc(8);
+ f->lbraceToken = loc(7);
+ f->rbraceToken = loc(9);
AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Setter);
node->colonToken = loc(2);
sym(1).Node = node;
@@ -4077,13 +4256,10 @@ ModuleItemList: ModuleItemList ModuleItem;
} break;
./
-ModuleItem: ImportDeclaration T_AUTOMATIC_SEMICOLON;
-/. case $rule_number: Q_FALLTHROUGH(); ./
-ModuleItem: ImportDeclaration T_SEMICOLON;
-/. case $rule_number: Q_FALLTHROUGH(); ./
-ModuleItem: ExportDeclaration T_AUTOMATIC_SEMICOLON;
+
+ModuleItem: ImportDeclaration Semicolon;
/. case $rule_number: Q_FALLTHROUGH(); ./
-ModuleItem: ExportDeclaration T_SEMICOLON;
+ModuleItem: ExportDeclaration Semicolon;
/.
case $rule_number: {
sym(1).StatementList = new (pool) AST::StatementList(sym(1).Node);
@@ -4420,7 +4596,7 @@ ExportSpecifier: IdentifierName T_AS IdentifierName;
yylloc.length = 0;
//const QString msg = QCoreApplication::translate("QQmlParser", "Missing `;'");
- //diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, yylloc, msg));
+ //diagnostic_messages.append(compileError(yyloc, msg, QtWarningMsg));
first_token = &token_buffer[0];
last_token = &token_buffer[1];
@@ -4457,7 +4633,7 @@ ExportSpecifier: IdentifierName T_AS IdentifierName;
msg = QCoreApplication::translate("QQmlParser", "Syntax error");
else
msg = QCoreApplication::translate("QQmlParser", "Unexpected token `%1'").arg(QLatin1String(spell[token]));
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
+ diagnostic_messages.append(compileError(token_buffer[0].loc, msg));
action = errorState;
goto _Lcheck_token;
@@ -4488,7 +4664,7 @@ ExportSpecifier: IdentifierName T_AS IdentifierName;
qDebug() << "Parse error, trying to recover (2).";
#endif
const QString msg = QCoreApplication::translate("QQmlParser", "Expected token `%1'").arg(QLatin1String(spell[*tk]));
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
+ diagnostic_messages.append(compileError(token_buffer[0].loc, msg));
pushToken(*tk);
goto _Lcheck_token;
@@ -4503,7 +4679,7 @@ ExportSpecifier: IdentifierName T_AS IdentifierName;
int a = t_action(errorState, tk);
if (a > 0 && t_action(a, yytoken)) {
const QString msg = QCoreApplication::translate("QQmlParser", "Expected token `%1'").arg(QLatin1String(spell[tk]));
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
+ diagnostic_messages.append(compileError(token_buffer[0].loc, msg));
pushToken(tk);
goto _Lcheck_token;
@@ -4511,7 +4687,7 @@ ExportSpecifier: IdentifierName T_AS IdentifierName;
}
const QString msg = QCoreApplication::translate("QQmlParser", "Syntax error");
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
+ diagnostic_messages.append(compileError(token_buffer[0].loc, msg));
}
return false;
diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp
index 2b1a999b38..b63b2191b9 100644
--- a/src/qml/parser/qqmljsast.cpp
+++ b/src/qml/parser/qqmljsast.cpp
@@ -131,7 +131,7 @@ FormalParameterList *ExpressionNode::reparseAsFormalParameterList(MemoryPool *po
}
AST::PatternElement *binding = nullptr;
if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(expr)) {
- binding = new (pool) AST::PatternElement(idExpr->name, rhs);
+ binding = new (pool) AST::PatternElement(idExpr->name, /*type annotation*/nullptr, rhs);
binding->identifierToken = idExpr->identifierToken;
} else if (AST::Pattern *p = expr->patternCast()) {
SourceLocation loc;
@@ -960,6 +960,7 @@ void FunctionDeclaration::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
accept(formals, visitor);
+ accept(typeAnnotation, visitor);
accept(body, visitor);
}
@@ -970,6 +971,7 @@ void FunctionExpression::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
accept(formals, visitor);
+ accept(typeAnnotation, visitor);
accept(body, visitor);
}
@@ -981,9 +983,9 @@ FunctionExpression *FunctionExpression::asFunctionDefinition()
return this;
}
-QStringList FormalParameterList::formals() const
+BoundNames FormalParameterList::formals() const
{
- QStringList formals;
+ BoundNames formals;
int i = 0;
for (const FormalParameterList *it = this; it; it = it->next) {
if (it->element) {
@@ -991,18 +993,18 @@ QStringList FormalParameterList::formals() const
int duplicateIndex = formals.indexOf(name);
if (duplicateIndex >= 0) {
// change the name of the earlier argument to enforce the lookup semantics from the spec
- formals[duplicateIndex] += QLatin1String("#") + QString::number(i);
+ formals[duplicateIndex].id += QLatin1String("#") + QString::number(i);
}
- formals += name;
+ formals += {name, it->element->typeAnnotation};
}
++i;
}
return formals;
}
-QStringList FormalParameterList::boundNames() const
+BoundNames FormalParameterList::boundNames() const
{
- QStringList names;
+ BoundNames names;
for (const FormalParameterList *it = this; it; it = it->next) {
if (it->element)
it->element->boundNames(&names);
@@ -1270,6 +1272,35 @@ void UiQualifiedId::accept0(Visitor *visitor)
visitor->endVisit(this);
}
+void Type::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(typeId, visitor);
+ accept(typeArguments, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void TypeArgumentList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ for (TypeArgumentList *it = this; it; it = it->next)
+ accept(it->typeId, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void TypeAnnotation::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(type, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
void UiImport::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
@@ -1340,13 +1371,14 @@ void PatternElement::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
accept(bindingTarget, visitor);
+ accept(typeAnnotation, visitor);
accept(initializer, visitor);
}
visitor->endVisit(this);
}
-void PatternElement::boundNames(QStringList *names)
+void PatternElement::boundNames(BoundNames *names)
{
if (bindingTarget) {
if (PatternElementList *e = elementList())
@@ -1354,7 +1386,7 @@ void PatternElement::boundNames(QStringList *names)
else if (PatternPropertyList *p = propertyList())
p->boundNames(names);
} else {
- names->append(bindingIdentifier.toString());
+ names->append({bindingIdentifier.toString(), typeAnnotation});
}
}
@@ -1371,7 +1403,7 @@ void PatternElementList::accept0(Visitor *visitor)
}
}
-void PatternElementList::boundNames(QStringList *names)
+void PatternElementList::boundNames(BoundNames *names)
{
for (PatternElementList *it = this; it; it = it->next) {
if (it->element)
@@ -1384,13 +1416,14 @@ void PatternProperty::accept0(Visitor *visitor)
if (visitor->visit(this)) {
accept(name, visitor);
accept(bindingTarget, visitor);
+ accept(typeAnnotation, visitor);
accept(initializer, visitor);
}
visitor->endVisit(this);
}
-void PatternProperty::boundNames(QStringList *names)
+void PatternProperty::boundNames(BoundNames *names)
{
PatternElement::boundNames(names);
}
@@ -1406,7 +1439,7 @@ void PatternPropertyList::accept0(Visitor *visitor)
}
}
-void PatternPropertyList::boundNames(QStringList *names)
+void PatternPropertyList::boundNames(BoundNames *names)
{
for (PatternPropertyList *it = this; it; it = it->next)
it->property->boundNames(names);
@@ -1475,6 +1508,37 @@ LeftHandSideExpression *LeftHandSideExpression::leftHandSideExpressionCast()
return this;
}
+void UiVersionSpecifier::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+ visitor->endVisit(this);
+}
+
+QString Type::toString() const
+{
+ QString result;
+ toString(&result);
+ return result;
+}
+
+void Type::toString(QString *out) const
+{
+ for (QQmlJS::AST::UiQualifiedId *it = typeId; it; it = it->next) {
+ out->append(it->name);
+
+ if (it->next)
+ out->append(QLatin1Char('.'));
+ }
+
+ if (typeArguments) {
+ out->append(QLatin1Char('<'));
+ if (auto subType = static_cast<TypeArgumentList*>(typeArguments)->typeId)
+ subType->toString(out);
+ out->append(QLatin1Char('>'));
+ };
+}
+
} } // namespace QQmlJS::AST
QT_END_NAMESPACE
diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h
index b81553776d..39194068bf 100644
--- a/src/qml/parser/qqmljsast_p.h
+++ b/src/qml/parser/qqmljsast_p.h
@@ -53,7 +53,8 @@
#include "qqmljsastvisitor_p.h"
#include "qqmljsglobal_p.h"
-#include "qqmljsmemorypool_p.h"
+
+#include <private/qqmljsmemorypool_p.h>
#include <QtCore/qstring.h>
@@ -233,7 +234,9 @@ public:
Kind_PatternElementList,
Kind_PatternProperty,
Kind_PatternPropertyList,
-
+ Kind_Type,
+ Kind_TypeArgumentList,
+ Kind_TypeAnnotation,
Kind_UiArrayBinding,
Kind_UiImport,
@@ -251,7 +254,8 @@ public:
Kind_UiSourceElement,
Kind_UiHeaderItemList,
Kind_UiEnumDeclaration,
- Kind_UiEnumMemberList
+ Kind_UiEnumMemberList,
+ Kind_UiVersionSpecifier
};
inline Node() {}
@@ -303,6 +307,131 @@ public:
int kind = Kind_Undefined;
};
+
+class QML_PARSER_EXPORT UiQualifiedId: public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(UiQualifiedId)
+
+ UiQualifiedId(const QStringRef &name)
+ : next(this), name(name)
+ { kind = K; }
+
+ UiQualifiedId(UiQualifiedId *previous, const QStringRef &name)
+ : name(name)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ UiQualifiedId *finish()
+ {
+ UiQualifiedId *head = next;
+ next = nullptr;
+ return head;
+ }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return identifierToken; }
+
+ SourceLocation lastSourceLocation() const override
+ { return next ? next->lastSourceLocation() : identifierToken; }
+
+// attributes
+ UiQualifiedId *next;
+ QStringRef name;
+ SourceLocation identifierToken;
+};
+
+class QML_PARSER_EXPORT Type: public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(Type)
+
+ Type(UiQualifiedId *typeId, Node *typeArguments = nullptr)
+ : typeId(typeId)
+ , typeArguments(typeArguments)
+ { kind = K; }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return typeId->firstSourceLocation(); }
+
+ SourceLocation lastSourceLocation() const override
+ { return typeArguments ? typeArguments->lastSourceLocation() : typeId->lastSourceLocation(); }
+
+ QString toString() const;
+ void toString(QString *out) const;
+
+// attributes
+ UiQualifiedId *typeId;
+ Node *typeArguments; // TypeArgumentList
+};
+
+
+class QML_PARSER_EXPORT TypeArgumentList: public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(TypeArgumentList)
+
+ TypeArgumentList(Type *typeId)
+ : typeId(typeId)
+ , next(nullptr)
+ { kind = K; }
+
+ TypeArgumentList(TypeArgumentList *previous, Type *typeId)
+ : typeId(typeId)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return typeId->firstSourceLocation(); }
+
+ SourceLocation lastSourceLocation() const override
+ { return next ? next->lastSourceLocation() : typeId->lastSourceLocation(); }
+
+ inline TypeArgumentList *finish()
+ {
+ TypeArgumentList *front = next;
+ next = nullptr;
+ return front;
+ }
+
+// attributes
+ Type *typeId;
+ TypeArgumentList *next;
+};
+
+class QML_PARSER_EXPORT TypeAnnotation: public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(TypeAnnotation)
+
+ TypeAnnotation(Type *type)
+ : type(type)
+ { kind = K; }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return colonToken; }
+
+ SourceLocation lastSourceLocation() const override
+ { return type->lastSourceLocation(); }
+
+// attributes
+ Type *type;
+ SourceLocation colonToken;
+};
class QML_PARSER_EXPORT ExpressionNode: public Node
{
public:
@@ -492,7 +621,30 @@ public:
SourceLocation literalToken;
};
-class QML_PARSER_EXPORT StringLiteral: public LeftHandSideExpression
+class QML_PARSER_EXPORT UiVersionSpecifier : public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(UiVersionSpecifier)
+
+ UiVersionSpecifier(int majorum, int minorum) : majorVersion(majorum), minorVersion(minorum) { kind = K; }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override { return majorToken; }
+
+ SourceLocation lastSourceLocation() const override
+ {
+ return minorToken.isValid() ? minorToken : majorToken;
+ }
+
+ // attributes:
+ int majorVersion;
+ int minorVersion;
+ SourceLocation majorToken;
+ SourceLocation minorToken;
+};
+
+class QML_PARSER_EXPORT StringLiteral : public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(StringLiteral)
@@ -681,6 +833,34 @@ public:
SourceLocation propertyNameToken;
};
+struct QML_PARSER_EXPORT BoundName
+{
+ QString id;
+ TypeAnnotation *typeAnnotation = nullptr;
+ BoundName(const QString &id, TypeAnnotation *typeAnnotation)
+ : id(id), typeAnnotation(typeAnnotation)
+ {}
+ BoundName() = default;
+ QString typeName() const { return typeAnnotation ? typeAnnotation->type->toString() : QString(); }
+};
+
+struct BoundNames : public QVector<BoundName>
+{
+ int indexOf(const QString &name, int from = 0) const
+ {
+ auto found = std::find_if(constBegin() + from, constEnd(),
+ [name](const BoundName &it) { return it.id == name; });
+ if (found == constEnd())
+ return -1;
+ return found - constBegin();
+ }
+
+ bool contains(const QString &name) const
+ {
+ return indexOf(name) != -1;
+ }
+};
+
class QML_PARSER_EXPORT PatternElement : public Node
{
public:
@@ -705,8 +885,9 @@ public:
: initializer(i), type(t)
{ kind = K; }
- PatternElement(const QStringRef &n, ExpressionNode *i = nullptr, Type t = Binding)
+ PatternElement(const QStringRef &n, TypeAnnotation *typeAnnotation = nullptr, ExpressionNode *i = nullptr, Type t = Binding)
: bindingIdentifier(n), initializer(i), type(t)
+ , typeAnnotation(typeAnnotation)
{
Q_ASSERT(t >= RestElement);
kind = K;
@@ -726,7 +907,7 @@ public:
{ return identifierToken.isValid() ? identifierToken : (bindingTarget ? bindingTarget->firstSourceLocation() : initializer->firstSourceLocation()); }
SourceLocation lastSourceLocation() const override
- { return initializer ? initializer->lastSourceLocation() : (bindingTarget ? bindingTarget->lastSourceLocation() : identifierToken); }
+ { return initializer ? initializer->lastSourceLocation() : (bindingTarget ? bindingTarget->lastSourceLocation() : (typeAnnotation ? typeAnnotation->lastSourceLocation() : identifierToken)); }
ExpressionNode *destructuringTarget() const { return bindingTarget; }
Pattern *destructuringPattern() const { return bindingTarget ? bindingTarget->patternCast() : nullptr; }
@@ -736,7 +917,7 @@ public:
bool isVariableDeclaration() const { return scope != VariableScope::NoScope; }
bool isLexicallyScoped() const { return scope == VariableScope::Let || scope == VariableScope::Const; }
- virtual void boundNames(QStringList *names);
+ virtual void boundNames(BoundNames *names);
// attributes
SourceLocation identifierToken;
@@ -744,6 +925,7 @@ public:
ExpressionNode *bindingTarget = nullptr;
ExpressionNode *initializer = nullptr;
Type type = Literal;
+ TypeAnnotation *typeAnnotation = nullptr;
// when used in a VariableDeclarationList
VariableScope scope = VariableScope::NoScope;
bool isForDeclaration = false;
@@ -773,7 +955,7 @@ public:
void accept0(Visitor *visitor) override;
- void boundNames(QStringList *names);
+ void boundNames(BoundNames *names);
SourceLocation firstSourceLocation() const override
{ return elision ? elision->firstSourceLocation() : element->firstSourceLocation(); }
@@ -796,7 +978,7 @@ public:
{ kind = K; }
PatternProperty(PropertyName *name, const QStringRef &n, ExpressionNode *i = nullptr)
- : PatternElement(n, i), name(name)
+ : PatternElement(n, /*type annotation*/nullptr, i), name(name)
{ kind = K; }
PatternProperty(PropertyName *name, Pattern *pattern, ExpressionNode *i = nullptr)
@@ -813,7 +995,7 @@ public:
return loc.isValid() ? loc : name->lastSourceLocation();
}
- void boundNames(QStringList *names) override;
+ void boundNames(BoundNames *names) override;
bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) override;
// attributes
@@ -841,7 +1023,7 @@ public:
void accept0(Visitor *visitor) override;
- void boundNames(QStringList *names);
+ void boundNames(BoundNames *names);
inline PatternPropertyList *finish ()
{
@@ -2131,8 +2313,9 @@ class QML_PARSER_EXPORT FunctionExpression: public ExpressionNode
public:
QQMLJS_DECLARE_AST_NODE(FunctionExpression)
- FunctionExpression(const QStringRef &n, FormalParameterList *f, StatementList *b):
- name (n), formals (f), body (b)
+ FunctionExpression(const QStringRef &n, FormalParameterList *f, StatementList *b, TypeAnnotation *typeAnnotation = nullptr):
+ name (n), formals (f), body (b),
+ typeAnnotation(typeAnnotation)
{ kind = K; }
void accept0(Visitor *visitor) override;
@@ -2151,6 +2334,7 @@ public:
bool isGenerator = false;
FormalParameterList *formals;
StatementList *body;
+ TypeAnnotation *typeAnnotation;
SourceLocation functionToken;
SourceLocation identifierToken;
SourceLocation lparenToken;
@@ -2164,8 +2348,8 @@ class QML_PARSER_EXPORT FunctionDeclaration: public FunctionExpression
public:
QQMLJS_DECLARE_AST_NODE(FunctionDeclaration)
- FunctionDeclaration(const QStringRef &n, FormalParameterList *f, StatementList *b):
- FunctionExpression(n, f, b)
+ FunctionDeclaration(const QStringRef &n, FormalParameterList *f, StatementList *b, TypeAnnotation *typeAnnotation = nullptr):
+ FunctionExpression(n, f, b, typeAnnotation)
{ kind = K; }
void accept0(Visitor *visitor) override;
@@ -2235,9 +2419,9 @@ public:
return false;
}
- QStringList formals() const;
+ BoundNames formals() const;
- QStringList boundNames() const;
+ BoundNames boundNames() const;
void accept0(Visitor *visitor) override;
@@ -2786,44 +2970,6 @@ public:
SourceLocation semicolonToken;
};
-class QML_PARSER_EXPORT UiQualifiedId: public Node
-{
-public:
- QQMLJS_DECLARE_AST_NODE(UiQualifiedId)
-
- UiQualifiedId(const QStringRef &name)
- : next(this), name(name)
- { kind = K; }
-
- UiQualifiedId(UiQualifiedId *previous, const QStringRef &name)
- : name(name)
- {
- kind = K;
- next = previous->next;
- previous->next = this;
- }
-
- UiQualifiedId *finish()
- {
- UiQualifiedId *head = next;
- next = nullptr;
- return head;
- }
-
- void accept0(Visitor *visitor) override;
-
- SourceLocation firstSourceLocation() const override
- { return identifierToken; }
-
- SourceLocation lastSourceLocation() const override
- { return next ? next->lastSourceLocation() : identifierToken; }
-
-// attributes
- UiQualifiedId *next;
- QStringRef name;
- SourceLocation identifierToken;
-};
-
class QML_PARSER_EXPORT UiImport: public Node
{
public:
@@ -2855,6 +3001,7 @@ public:
SourceLocation asToken;
SourceLocation importIdToken;
SourceLocation semicolonToken;
+ UiVersionSpecifier *version = nullptr;
};
class QML_PARSER_EXPORT UiObjectMember: public Node
@@ -3090,10 +3237,10 @@ public:
void accept0(Visitor *) override;
SourceLocation firstSourceLocation() const override
- { return propertyTypeToken; }
+ { return colonToken.isValid() ? identifierToken : propertyTypeToken; }
SourceLocation lastSourceLocation() const override
- { return next ? next->lastSourceLocation() : identifierToken; }
+ { return next ? next->lastSourceLocation() : (colonToken.isValid() ? propertyTypeToken : identifierToken); }
inline UiParameterList *finish ()
{
@@ -3109,6 +3256,7 @@ public:
SourceLocation commaToken;
SourceLocation propertyTypeToken;
SourceLocation identifierToken;
+ SourceLocation colonToken;
};
class QML_PARSER_EXPORT UiPublicMember: public UiObjectMember
@@ -3203,7 +3351,7 @@ public:
SourceLocation firstSourceLocation() const override
{
- if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement))
+ if (FunctionExpression *funDecl = sourceElement->asFunctionDefinition())
return funDecl->firstSourceLocation();
else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement))
return varStmt->firstSourceLocation();
@@ -3213,7 +3361,7 @@ public:
SourceLocation lastSourceLocation() const override
{
- if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement))
+ if (FunctionExpression *funDecl = sourceElement->asFunctionDefinition())
return funDecl->lastSourceLocation();
else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement))
return varStmt->lastSourceLocation();
diff --git a/src/qml/parser/qqmljsastfwd_p.h b/src/qml/parser/qqmljsastfwd_p.h
index e9caa918d5..05226fd043 100644
--- a/src/qml/parser/qqmljsastfwd_p.h
+++ b/src/qml/parser/qqmljsastfwd_p.h
@@ -158,6 +158,9 @@ class NestedExpression;
class ClassExpression;
class ClassDeclaration;
class ClassElementList;
+class TypeArgumentList;
+class Type;
+class TypeAnnotation;
// ui elements
class UiProgram;
@@ -178,8 +181,10 @@ class UiQualifiedId;
class UiHeaderItemList;
class UiEnumDeclaration;
class UiEnumMemberList;
+class UiVersionSpecifier;
-} } // namespace AST
+} // namespace AST
+} // namespace QQmlJS
QT_END_NAMESPACE
diff --git a/src/qml/parser/qqmljsastvisitor_p.h b/src/qml/parser/qqmljsastvisitor_p.h
index 9115449a46..7146cd00ac 100644
--- a/src/qml/parser/qqmljsastvisitor_p.h
+++ b/src/qml/parser/qqmljsastvisitor_p.h
@@ -111,6 +111,7 @@ public:
virtual bool visit(UiQualifiedId *) { return true; }
virtual bool visit(UiEnumDeclaration *) { return true; }
virtual bool visit(UiEnumMemberList *) { return true; }
+ virtual bool visit(UiVersionSpecifier *) { return true; }
virtual void endVisit(UiProgram *) {}
virtual void endVisit(UiImport *) {}
@@ -129,6 +130,7 @@ public:
virtual void endVisit(UiQualifiedId *) {}
virtual void endVisit(UiEnumDeclaration *) {}
virtual void endVisit(UiEnumMemberList *) { }
+ virtual void endVisit(UiVersionSpecifier *) {}
// QQmlJS
virtual bool visit(ThisExpression *) { return true; }
@@ -401,6 +403,15 @@ public:
virtual bool visit(DebuggerStatement *) { return true; }
virtual void endVisit(DebuggerStatement *) {}
+ virtual bool visit(Type *) { return true; }
+ virtual void endVisit(Type *) {}
+
+ virtual bool visit(TypeArgumentList *) { return true; }
+ virtual void endVisit(TypeArgumentList *) {}
+
+ virtual bool visit(TypeAnnotation *) { return true; }
+ virtual void endVisit(TypeAnnotation *) {}
+
virtual void throwRecursionDepthError() = 0;
quint16 recursionDepth() const { return m_recursionDepth; }
diff --git a/src/qml/parser/qqmljsengine_p.h b/src/qml/parser/qqmljsengine_p.h
index b7f7da9478..8a3e2db6a1 100644
--- a/src/qml/parser/qqmljsengine_p.h
+++ b/src/qml/parser/qqmljsengine_p.h
@@ -52,9 +52,10 @@
//
#include "qqmljsglobal_p.h"
-#include "qqmljsmemorypool_p.h"
#include "qqmljssourcelocation_p.h"
+#include <private/qqmljsmemorypool_p.h>
+
#include <QtCore/qstring.h>
#include <QtCore/qset.h>
@@ -91,28 +92,6 @@ public:
}
};
-
-class QML_PARSER_EXPORT DiagnosticMessage
-{
-public:
- enum Kind { Hint, Warning, Error };
-
- DiagnosticMessage() {}
-
- DiagnosticMessage(Kind kind, const AST::SourceLocation &loc, const QString &message)
- : kind(kind), loc(loc), message(message) {}
-
- bool isWarning() const
- { return kind == Warning; }
-
- bool isError() const
- { return kind == Error; }
-
- Kind kind = Error;
- AST::SourceLocation loc;
- QString message;
-};
-
class QML_PARSER_EXPORT Engine
{
Lexer *_lexer;
diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp
index c53b13f64d..1e0ac72bd1 100644
--- a/src/qml/parser/qqmljslexer.cpp
+++ b/src/qml/parser/qqmljslexer.cpp
@@ -39,12 +39,15 @@
#include "qqmljslexer_p.h"
#include "qqmljsengine_p.h"
-#include "qqmljsmemorypool_p.h"
#include "qqmljskeywords_p.h"
+#include <private/qqmljsdiagnosticmessage_p.h>
+#include <private/qqmljsmemorypool_p.h>
+
#include <QtCore/qcoreapplication.h>
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qdebug.h>
+#include <QtCore/QScopedValueRollback>
QT_BEGIN_NAMESPACE
Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
@@ -269,16 +272,29 @@ int Lexer::lex()
++_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:
@@ -618,6 +634,8 @@ again:
return T_DIVIDE_;
case '.':
+ if (_importState == ImportState::SawImport)
+ return T_DOT;
if (isDecimalDigit(_char.unicode()))
return scanNumber(ch);
if (_char == QLatin1Char('.')) {
@@ -728,7 +746,10 @@ again:
case '7':
case '8':
case '9':
- return scanNumber(ch);
+ if (_importState == ImportState::SawImport)
+ return scanVersionNumber(ch);
+ else
+ return scanNumber(ch);
default: {
uint c = ch.unicode();
@@ -1146,6 +1167,26 @@ int Lexer::scanNumber(QChar ch)
return T_NUMERIC_LITERAL;
}
+int Lexer::scanVersionNumber(QChar ch)
+{
+ if (ch == QLatin1Char('0')) {
+ _tokenValue = 0;
+ return T_VERSION_NUMBER;
+ }
+
+ int acc = 0;
+ acc += ch.digitValue();
+
+ while (_char.isDigit()) {
+ acc *= 10;
+ acc += _char.digitValue();
+ scanChar(); // consume the digit
+ }
+
+ _tokenValue = acc;
+ return T_VERSION_NUMBER;
+}
+
bool Lexer::scanRegExp(RegExpBodyPrefix prefix)
{
_tokenText.resize(0);
@@ -1402,6 +1443,13 @@ static inline bool isUriToken(int token)
bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
{
+ auto setError = [error, this](QString message) {
+ error->message = std::move(message);
+ error->line = tokenStartLine();
+ error->column = tokenStartColumn();
+ };
+
+ QScopedValueRollback<bool> directivesGuard(_handlingDirectives, true);
Q_ASSERT(!_qmlMode);
lex(); // fetch the first token
@@ -1422,9 +1470,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
if (! (directiveName == QLatin1String("pragma") ||
directiveName == QLatin1String("import"))) {
- error->message = QCoreApplication::translate("QQmlParser", "Syntax error");
- error->loc.startLine = tokenStartLine();
- error->loc.startColumn = tokenStartColumn();
+ setError(QCoreApplication::translate("QQmlParser", "Syntax error"));
return false; // not a valid directive name
}
@@ -1432,9 +1478,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
if (directiveName == QLatin1String("pragma")) {
// .pragma library
if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("library"))) {
- error->message = QCoreApplication::translate("QQmlParser", "Syntax error");
- error->loc.startLine = tokenStartLine();
- error->loc.startColumn = tokenStartColumn();
+ setError(QCoreApplication::translate("QQmlParser", "Syntax error"));
return false; // expected `library
}
@@ -1456,20 +1500,15 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
pathOrUri = tokenText();
if (!pathOrUri.endsWith(QLatin1String("js"))) {
- error->message = QCoreApplication::translate("QQmlParser","Imported file must be a script");
- error->loc.startLine = tokenStartLine();
- error->loc.startColumn = tokenStartColumn();
+ setError(QCoreApplication::translate("QQmlParser","Imported file must be a script"));
return false;
}
} else if (_tokenKind == T_IDENTIFIER) {
- // .import T_IDENTIFIER (. T_IDENTIFIER)* T_NUMERIC_LITERAL as T_IDENTIFIER
-
+ // .import T_IDENTIFIER (. T_IDENTIFIER)* T_VERSION_NUMBER . T_VERSION_NUMBER as T_IDENTIFIER
while (true) {
if (!isUriToken(_tokenKind)) {
- error->message = QCoreApplication::translate("QQmlParser","Invalid module URI");
- error->loc.startLine = tokenStartLine();
- error->loc.startColumn = tokenStartColumn();
+ setError(QCoreApplication::translate("QQmlParser","Invalid module URI"));
return false;
}
@@ -1477,9 +1516,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
lex();
if (tokenStartLine() != lineNumber) {
- error->message = QCoreApplication::translate("QQmlParser","Invalid module URI");
- error->loc.startLine = tokenStartLine();
- error->loc.startColumn = tokenStartColumn();
+ setError(QCoreApplication::translate("QQmlParser","Invalid module URI"));
return false;
}
if (_tokenKind != QQmlJSGrammar::T_DOT)
@@ -1489,21 +1526,30 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
lex();
if (tokenStartLine() != lineNumber) {
- error->message = QCoreApplication::translate("QQmlParser","Invalid module URI");
- error->loc.startLine = tokenStartLine();
- error->loc.startColumn = tokenStartColumn();
+ setError(QCoreApplication::translate("QQmlParser","Invalid module URI"));
return false;
}
}
- if (_tokenKind != T_NUMERIC_LITERAL) {
- error->message = QCoreApplication::translate("QQmlParser","Module import requires a version");
- error->loc.startLine = tokenStartLine();
- error->loc.startColumn = tokenStartColumn();
+ if (_tokenKind != T_VERSION_NUMBER) {
+ setError(QCoreApplication::translate("QQmlParser","Module import requires a version"));
return false; // expected the module version number
}
version = tokenText();
+ lex();
+ if (_tokenKind != T_DOT) {
+ setError(QCoreApplication::translate( "QQmlParser", "Module import requires a minor version (missing dot)"));
+ return false; // expected the module version number
+ }
+ version += QLatin1Char('.');
+
+ lex();
+ if (_tokenKind != T_VERSION_NUMBER) {
+ setError(QCoreApplication::translate( "QQmlParser", "Module import requires a minor version (missing number)"));
+ return false; // expected the module version number
+ }
+ version += tokenText();
}
//
@@ -1511,34 +1557,27 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
//
if (! (lex() == T_AS && tokenStartLine() == lineNumber)) {
if (fileImport)
- error->message = QCoreApplication::translate("QQmlParser", "File import requires a qualifier");
+ setError(QCoreApplication::translate("QQmlParser", "File import requires a qualifier"));
else
- error->message = QCoreApplication::translate("QQmlParser", "Module import requires a qualifier");
+ setError(QCoreApplication::translate("QQmlParser", "Module import requires a qualifier"));
if (tokenStartLine() != lineNumber) {
- error->loc.startLine = lineNumber;
- error->loc.startColumn = column;
- } else {
- error->loc.startLine = tokenStartLine();
- error->loc.startColumn = tokenStartColumn();
+ error->line = lineNumber;
+ error->line = column;
}
return false; // expected `as'
}
if (lex() != T_IDENTIFIER || tokenStartLine() != lineNumber) {
if (fileImport)
- error->message = QCoreApplication::translate("QQmlParser", "File import requires a qualifier");
+ setError(QCoreApplication::translate("QQmlParser", "File import requires a qualifier"));
else
- error->message = QCoreApplication::translate("QQmlParser", "Module import requires a qualifier");
- error->loc.startLine = tokenStartLine();
- error->loc.startColumn = tokenStartColumn();
+ setError(QCoreApplication::translate("QQmlParser", "Module import requires a qualifier"));
return false; // expected module name
}
const QString module = tokenText();
if (!module.at(0).isUpper()) {
- error->message = QCoreApplication::translate("QQmlParser","Invalid import qualifier");
- error->loc.startLine = tokenStartLine();
- error->loc.startColumn = tokenStartColumn();
+ setError(QCoreApplication::translate("QQmlParser","Invalid import qualifier"));
return false;
}
@@ -1549,9 +1588,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
}
if (tokenStartLine() != lineNumber) {
- error->message = QCoreApplication::translate("QQmlParser", "Syntax error");
- error->loc.startLine = tokenStartLine();
- error->loc.startColumn = tokenStartColumn();
+ setError(QCoreApplication::translate("QQmlParser", "Syntax error"));
return false; // the directives cannot span over multiple lines
}
diff --git a/src/qml/parser/qqmljslexer_p.h b/src/qml/parser/qqmljslexer_p.h
index 51152bfd6e..e2ee4ae351 100644
--- a/src/qml/parser/qqmljslexer_p.h
+++ b/src/qml/parser/qqmljslexer_p.h
@@ -62,7 +62,7 @@ QT_BEGIN_NAMESPACE
namespace QQmlJS {
class Engine;
-class DiagnosticMessage;
+struct DiagnosticMessage;
class Directives;
class QML_PARSER_EXPORT Lexer: public QQmlJSGrammar
@@ -124,6 +124,11 @@ public:
StaticIsKeyword = 0x4
};
+ enum class ImportState {
+ SawImport,
+ NoQmlImport
+ };
+
public:
Lexer(Engine *engine);
@@ -188,6 +193,7 @@ private:
inline void scanChar();
int scanToken();
int scanNumber(QChar ch);
+ int scanVersionNumber(QChar ch);
enum ScanStringMode {
SingleQuote = '\'',
DoubleQuote = '"',
@@ -242,6 +248,7 @@ private:
int _tokenLength;
int _tokenLine;
int _tokenColumn;
+ ImportState _importState = ImportState::NoQmlImport;
bool _validTokenText;
bool _prohibitAutomaticSemicolon;
@@ -253,6 +260,7 @@ private:
bool _skipLinefeed = false;
int _generatorLevel = 0;
bool _staticIsKeyword = false;
+ bool _handlingDirectives = false;
};
} // end of namespace QQmlJS
diff --git a/src/qml/qml.pro b/src/qml/qml.pro
index df3085a28e..3c889244f4 100644
--- a/src/qml/qml.pro
+++ b/src/qml/qml.pro
@@ -19,31 +19,6 @@ gcc:isEqual(QT_ARCH, "mips"): QMAKE_CXXFLAGS += -fno-reorder-blocks
DEFINES += QT_NO_FOREACH
-!build_pass {
- # Create a header containing a hash that describes this library. For a
- # released version of Qt, we'll use the .tag file that is updated by git
- # archive with the commit hash. For unreleased versions, we'll ask git
- # describe. Note that it won't update unless qmake is run again, even if
- # the commit change also changed something in this library.
- tagFile = $$PWD/../../.tag
- tag =
- exists($$tagFile) {
- tag = $$cat($$tagFile, singleline)
- QMAKE_INTERNAL_INCLUDED_FILES += $$tagFile
- }
- !equals(tag, "$${LITERAL_DOLLAR}Format:%H$${LITERAL_DOLLAR}") {
- QML_COMPILE_HASH = $$tag
- } else:exists($$PWD/../../.git) {
- commit = $$system(git rev-parse HEAD)
- QML_COMPILE_HASH = $$commit
- }
- compile_hash_contents = \
- "// Generated file, DO NOT EDIT" \
- "$${LITERAL_HASH}define QML_COMPILE_HASH \"$$QML_COMPILE_HASH\"" \
- "$${LITERAL_HASH}define QML_COMPILE_HASH_LENGTH $$str_size($$QML_COMPILE_HASH)"
- write_file("$$OUT_PWD/qml_compile_hash_p.h", compile_hash_contents)|error()
-}
-
exists("qqml_enable_gcov") {
QMAKE_CXXFLAGS = -fprofile-arcs -ftest-coverage -fno-elide-constructors
LIBS_PRIVATE += -lgcov
@@ -64,16 +39,21 @@ greaterThan(QT_CLANG_MAJOR_VERSION, 3)|greaterThan(QT_CLANG_MINOR_VERSION, 3)| \
WERROR += -Wno-error=unused-const-variable
HEADERS += qtqmlglobal.h \
- qtqmlglobal_p.h
+ qtqmlglobal_p.h \
+ qtqmlcompilerglobal.h \
+ qtqmlcompilerglobal_p.h
#modules
+include(common/common.pri)
include(util/util.pri)
include(memory/memory.pri)
include(parser/parser.pri)
include(compiler/compiler.pri)
include(jsapi/jsapi.pri)
include(jsruntime/jsruntime.pri)
-include(jit/jit.pri)
+qtConfig(qml-jit) {
+ include(jit/jit.pri)
+}
include(qml/qml.pri)
include(debugger/debugger.pri)
include(qmldirparser/qmldirparser.pri)
@@ -83,6 +63,7 @@ qtConfig(qml-animation) {
include(types/types.pri)
include(../3rdparty/masm/masm-defs.pri)
include(../3rdparty/masm/masm.pri)
+include(../3rdparty/llvm/llvm.pri)
MODULE_PLUGIN_TYPES = \
qmltooling
diff --git a/src/qml/qml/ftw/ftw.pri b/src/qml/qml/ftw/ftw.pri
index ade05a596b..eadba394b4 100644
--- a/src/qml/qml/ftw/ftw.pri
+++ b/src/qml/qml/ftw/ftw.pri
@@ -3,6 +3,7 @@ HEADERS += \
$$PWD/qintrusivelist_p.h \
$$PWD/qpodvector_p.h \
$$PWD/qhashedstring_p.h \
+ $$PWD/qprimefornumbits_p.h \
$$PWD/qqmlrefcount_p.h \
$$PWD/qfieldlist_p.h \
$$PWD/qqmlthread_p.h \
@@ -11,12 +12,14 @@ HEADERS += \
$$PWD/qrecyclepool_p.h \
$$PWD/qflagpointer_p.h \
$$PWD/qlazilyallocated_p.h \
- $$PWD/qqmlnullablevalue_p.h
+ $$PWD/qqmlnullablevalue_p.h \
+ $$PWD/qstringhash_p.h \
+ $$PWD/qlinkedstringhash_p.h
SOURCES += \
$$PWD/qintrusivelist.cpp \
$$PWD/qhashedstring.cpp \
- $$PWD/qqmlthread.cpp \
+ $$PWD/qqmlthread.cpp
# mirrors logic in $$QT_SOURCE_TREE/config.tests/unix/clock-gettime/clock-gettime.pri
# clock_gettime() is implemented in librt on these systems
diff --git a/src/qml/qml/ftw/qflagpointer_p.h b/src/qml/qml/ftw/qflagpointer_p.h
index 71b41cd30b..a10e57aeca 100644
--- a/src/qml/qml/ftw/qflagpointer_p.h
+++ b/src/qml/qml/ftw/qflagpointer_p.h
@@ -55,6 +55,17 @@
QT_BEGIN_NAMESPACE
+namespace QtPrivate {
+template <typename T> struct QFlagPointerAlignment
+{
+ enum : size_t { Value = Q_ALIGNOF(T) };
+};
+template <> struct QFlagPointerAlignment<void>
+{
+ enum : size_t { Value = ~size_t(0) };
+};
+}
+
template<typename T>
class QFlagPointer {
public:
@@ -133,6 +144,7 @@ template<typename T>
QFlagPointer<T>::QFlagPointer(T *v)
: ptr_value(quintptr(v))
{
+ Q_STATIC_ASSERT_X(Q_ALIGNOF(T) >= 4, "Type T does not have sufficient alignment");
Q_ASSERT((ptr_value & FlagsMask) == 0);
}
@@ -247,6 +259,8 @@ template<typename T, typename T2>
QBiPointer<T, T2>::QBiPointer(T *v)
: ptr_value(quintptr(v))
{
+ Q_STATIC_ASSERT_X(QtPrivate::QFlagPointerAlignment<T>::Value >= 4,
+ "Type T does not have sufficient alignment");
Q_ASSERT((quintptr(v) & FlagsMask) == 0);
}
@@ -254,6 +268,8 @@ template<typename T, typename T2>
QBiPointer<T, T2>::QBiPointer(T2 *v)
: ptr_value(quintptr(v) | Flag2Bit)
{
+ Q_STATIC_ASSERT_X(QtPrivate::QFlagPointerAlignment<T2>::Value >= 4,
+ "Type T2 does not have sufficient alignment");
Q_ASSERT((quintptr(v) & FlagsMask) == 0);
}
diff --git a/src/qml/qml/ftw/qhashedstring.cpp b/src/qml/qml/ftw/qhashedstring.cpp
index 117670dbfc..7a8fdd0a14 100644
--- a/src/qml/qml/ftw/qhashedstring.cpp
+++ b/src/qml/qml/ftw/qhashedstring.cpp
@@ -39,82 +39,7 @@
#include "qhashedstring_p.h"
-
-
-/*
- A QHash has initially around pow(2, MinNumBits) buckets. For
- example, if MinNumBits is 4, it has 17 buckets.
-*/
-const int MinNumBits = 4;
-
-/*
- The prime_deltas array is a table of selected prime values, even
- though it doesn't look like one. The primes we are using are 1,
- 2, 5, 11, 17, 37, 67, 131, 257, ..., i.e. primes in the immediate
- surrounding of a power of two.
-
- The primeForNumBits() function returns the prime associated to a
- power of two. For example, primeForNumBits(8) returns 257.
-*/
-
-static const uchar prime_deltas[] = {
- 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3,
- 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0
-};
-
-static inline int primeForNumBits(int numBits)
-{
- return (1 << numBits) + prime_deltas[numBits];
-}
-
-void QStringHashData::rehashToSize(int size)
-{
- short bits = qMax(MinNumBits, (int)numBits);
- while (primeForNumBits(bits) < size) bits++;
-
- if (bits > numBits)
- rehashToBits(bits);
-}
-
-void QStringHashData::rehashToBits(short bits)
-{
- numBits = qMax(MinNumBits, (int)bits);
-
- int nb = primeForNumBits(numBits);
- if (nb == numBuckets && buckets)
- return;
-
-#ifdef QSTRINGHASH_LINK_DEBUG
- if (linkCount)
- qFatal("QStringHash: Illegal attempt to rehash a linked hash.");
-#endif
-
- QStringHashNode **newBuckets = new QStringHashNode *[nb];
- ::memset(newBuckets, 0, sizeof(QStringHashNode *) * nb);
-
- // Preserve the existing order within buckets so that items with the
- // same key will retain the same find/findNext order
- for (int i = 0; i < numBuckets; ++i) {
- QStringHashNode *bucket = buckets[i];
- if (bucket)
- rehashNode(newBuckets, nb, bucket);
- }
-
- delete [] buckets;
- buckets = newBuckets;
- numBuckets = nb;
-}
-
-void QStringHashData::rehashNode(QStringHashNode **newBuckets, int nb, QStringHashNode *node)
-{
- QStringHashNode *next = node->next.data();
- if (next)
- rehashNode(newBuckets, nb, next);
-
- int bucket = node->hash % nb;
- node->next = newBuckets[bucket];
- newBuckets[bucket] = node;
-}
+QT_BEGIN_NAMESPACE
// Copy of QString's qMemCompare
bool QHashedString::compare(const QChar *lhs, const QChar *rhs, int length)
@@ -228,3 +153,4 @@ QString QHashedCStringRef::toUtf16() const
return rv;
}
+QT_END_NAMESPACE
diff --git a/src/qml/qml/ftw/qhashedstring_p.h b/src/qml/qml/ftw/qhashedstring_p.h
index 2d6c25bdd3..b9f3f81219 100644
--- a/src/qml/qml/ftw/qhashedstring_p.h
+++ b/src/qml/qml/ftw/qhashedstring_p.h
@@ -63,9 +63,6 @@
QT_BEGIN_NAMESPACE
-// Enable this to debug hash linking assumptions.
-// #define QSTRINGHASH_LINK_DEBUG
-
class QHashedStringRef;
class Q_QML_PRIVATE_EXPORT QHashedString : public QString
{
@@ -174,854 +171,6 @@ private:
mutable quint32 m_hash = 0;
};
-class QStringHashData;
-class Q_AUTOTEST_EXPORT QStringHashNode
-{
-public:
- QStringHashNode()
- : ckey(nullptr)
- {
- }
-
- QStringHashNode(const QHashedString &key)
- : length(key.length()), hash(key.hash()), symbolId(0)
- {
- strData = const_cast<QHashedString &>(key).data_ptr();
- setQString(true);
- strData->ref.ref();
- }
-
- QStringHashNode(const QHashedCStringRef &key)
- : length(key.length()), hash(key.hash()), symbolId(0), ckey(key.constData())
- {
- }
-
- QStringHashNode(const QStringHashNode &o)
- : length(o.length), hash(o.hash), symbolId(o.symbolId), ckey(o.ckey)
- {
- setQString(o.isQString());
- if (isQString()) { strData->ref.ref(); }
- }
-
- ~QStringHashNode()
- {
- if (isQString()) { if (!strData->ref.deref()) free(strData); }
- }
-
- QFlagPointer<QStringHashNode> next;
-
- qint32 length = 0;
- quint32 hash = 0;
- quint32 symbolId = 0;
-
- union {
- const char *ckey;
- QStringData *strData;
- };
-
- inline QHashedString key() const
- {
- if (isQString())
- return QHashedString(QString((QChar *)strData->data(), length), hash);
-
- return QHashedString(QString::fromLatin1(ckey, length), hash);
- }
-
- bool isQString() const { return next.flag(); }
- void setQString(bool v) { if (v) next.setFlag(); else next.clearFlag(); }
-
- inline char *cStrData() const { return (char *)ckey; }
- inline quint16 *utf16Data() const { return (quint16 *)strData->data(); }
-
- inline bool equals(const QV4::Value &string) const {
- QString s = string.toQStringNoThrow();
- if (isQString()) {
- QStringDataPtr dd;
- dd.ptr = strData;
- strData->ref.ref();
- return QString(dd) == s;
- } else {
- return QLatin1String(cStrData(), length) == s;
- }
- }
-
- inline bool equals(const QV4::String *string) const {
- if (length != string->d()->length() || hash != string->hashValue())
- return false;
- if (isQString()) {
- QStringDataPtr dd;
- dd.ptr = strData;
- strData->ref.ref();
- return QString(dd) == string->toQString();
- } else {
- return QLatin1String(cStrData(), length) == string->toQString();
- }
- }
-
- inline bool equals(const QHashedStringRef &string) const {
- return length == string.length() &&
- hash == string.hash() &&
- (isQString()?QHashedString::compare(string.constData(), (const QChar *)utf16Data(), length):
- QHashedString::compare(string.constData(), cStrData(), length));
- }
-
- inline bool equals(const QHashedCStringRef &string) const {
- return length == string.length() &&
- hash == string.hash() &&
- (isQString()?QHashedString::compare((const QChar *)utf16Data(), string.constData(), length):
- QHashedString::compare(string.constData(), cStrData(), length));
- }
-};
-
-class Q_AUTOTEST_EXPORT QStringHashData
-{
-public:
- QStringHashData() {}
-
- QStringHashNode **buckets = nullptr;
- int numBuckets = 0;
- int size = 0;
- short numBits = 0;
-#ifdef QSTRINGHASH_LINK_DEBUG
- int linkCount = 0;
-#endif
-
- struct IteratorData {
- IteratorData() {}
- QStringHashNode *n = nullptr;
- void *p = nullptr;
- };
- void rehashToBits(short);
- void rehashToSize(int);
- void rehashNode(QStringHashNode **newBuckets, int nb, QStringHashNode *node);
-
-private:
- QStringHashData(const QStringHashData &);
- QStringHashData &operator=(const QStringHashData &);
-};
-
-// For a supplied key type, in what form do we need to keep a hashed version?
-template<typename T>
-struct HashedForm {};
-
-template<> struct HashedForm<QString> { typedef QHashedString Type; };
-template<> struct HashedForm<QStringRef> { typedef QHashedStringRef Type; };
-template<> struct HashedForm<QHashedString> { typedef const QHashedString &Type; };
-template<> struct HashedForm<QV4::String *> { typedef const QV4::String *Type; };
-template<> struct HashedForm<const QV4::String *> { typedef const QV4::String *Type; };
-template<> struct HashedForm<QHashedStringRef> { typedef const QHashedStringRef &Type; };
-template<> struct HashedForm<QLatin1String> { typedef QHashedCStringRef Type; };
-template<> struct HashedForm<QHashedCStringRef> { typedef const QHashedCStringRef &Type; };
-
-class QStringHashBase
-{
-public:
- static HashedForm<QString>::Type hashedString(const QString &s) { return QHashedString(s);}
- static HashedForm<QStringRef>::Type hashedString(const QStringRef &s) { return QHashedStringRef(s.constData(), s.size());}
- static HashedForm<QHashedString>::Type hashedString(const QHashedString &s) { return s; }
- static HashedForm<QV4::String *>::Type hashedString(QV4::String *s) { return s; }
- static HashedForm<const QV4::String *>::Type hashedString(const QV4::String *s) { return s; }
- static HashedForm<QHashedStringRef>::Type hashedString(const QHashedStringRef &s) { return s; }
-
- static HashedForm<QLatin1String>::Type hashedString(const QLatin1String &s) { return QHashedCStringRef(s.data(), s.size()); }
- static HashedForm<QHashedCStringRef>::Type hashedString(const QHashedCStringRef &s) { return s; }
-
- static const QString &toQString(const QString &s) { return s; }
- static const QString &toQString(const QHashedString &s) { return s; }
- static QString toQString(const QV4::String *s) { return s->toQString(); }
- static QString toQString(const QHashedStringRef &s) { return s.toString(); }
-
- static QString toQString(const QLatin1String &s) { return QString(s); }
- static QString toQString(const QHashedCStringRef &s) { return s.toUtf16(); }
-
- static inline quint32 hashOf(const QHashedStringRef &s) { return s.hash(); }
- static inline quint32 hashOf(QV4::String *s) { return s->hashValue(); }
- static inline quint32 hashOf(const QV4::String *s) { return s->hashValue(); }
-
- template<typename K>
- static inline quint32 hashOf(const K &key) { return hashedString(key).hash(); }
-};
-
-template<class T>
-class QStringHash : public QStringHashBase
-{
-public:
- typedef QHashedString key_type;
- typedef T mapped_type;
-
- struct Node : public QStringHashNode {
- Node(const QHashedString &key, const T &value) : QStringHashNode(key), value(value) {}
- Node(const QHashedCStringRef &key, const T &value) : QStringHashNode(key), value(value) {}
- Node(const Node &o) : QStringHashNode(o), value(o.value) {}
- Node() {}
- T value;
- };
- struct NewedNode : public Node {
- NewedNode(const QHashedString &key, const T &value) : Node(key, value), nextNewed(nullptr) {}
- NewedNode(const QHashedCStringRef &key, const T &value) : Node(key, value), nextNewed(nullptr) {}
- NewedNode(const Node &o) : Node(o), nextNewed(nullptr) {}
- NewedNode *nextNewed;
- };
- struct ReservedNodePool
- {
- ReservedNodePool() : nodes(nullptr) {}
- ~ReservedNodePool() { delete [] nodes; }
- int count = 0;
- int used = 0;
- Node *nodes;
- };
-
- QStringHashData data;
- NewedNode *newedNodes;
- ReservedNodePool *nodePool;
- const QStringHash<T> *link;
-
- template<typename K>
- inline Node *findNode(const K &) const;
-
- inline Node *createNode(const Node &o);
-
- template<typename K>
- inline Node *createNode(const K &, const T &);
-
- inline Node *insertNode(Node *, quint32);
-
- inline void initializeNode(Node *, const QHashedString &key);
- inline void initializeNode(Node *, const QHashedCStringRef &key);
-
- template<typename K>
- inline Node *takeNode(const K &key, const T &value);
-
- inline Node *takeNode(const Node &o);
-
- inline void copy(const QStringHash<T> &);
-
- void copyNode(const QStringHashNode *otherNode);
-
- inline QStringHashData::IteratorData iterateFirst() const;
- static inline QStringHashData::IteratorData iterateNext(const QStringHashData::IteratorData &);
-
-public:
- inline QStringHash();
- inline QStringHash(const QStringHash &);
- inline ~QStringHash();
-
- QStringHash &operator=(const QStringHash<T> &);
-
- void copyAndReserve(const QStringHash<T> &other, int additionalReserve);
- void linkAndReserve(const QStringHash<T> &other, int additionalReserve);
-
- inline bool isEmpty() const;
- inline void clear();
- inline int count() const;
-
- inline int numBuckets() const;
- inline bool isLinked() const;
-
- class ConstIterator {
- public:
- inline ConstIterator();
- inline ConstIterator(const QStringHashData::IteratorData &);
-
- inline ConstIterator &operator++();
-
- inline bool operator==(const ConstIterator &o) const;
- inline bool operator!=(const ConstIterator &o) const;
-
- template<typename K>
- inline bool equals(const K &) const;
-
- inline QHashedString key() const;
- inline const T &value() const;
- inline const T &operator*() const;
-
- inline Node *node() const;
- private:
- QStringHashData::IteratorData d;
- };
-
- template<typename K>
- inline void insert(const K &, const T &);
-
- inline void insert(const ConstIterator &);
-
- template<typename K>
- inline T *value(const K &) const;
-
- inline T *value(const QV4::String *string) const;
- inline T *value(const ConstIterator &) const;
-
- template<typename K>
- inline bool contains(const K &) const;
-
- template<typename K>
- inline T &operator[](const K &);
-
- inline ConstIterator begin() const;
- inline ConstIterator end() const;
-
- inline ConstIterator iterator(Node *n) const;
-
- template<typename K>
- inline ConstIterator find(const K &) const;
-
- inline void reserve(int);
-};
-
-template<class T>
-QStringHash<T>::QStringHash()
-: newedNodes(nullptr), nodePool(nullptr), link(nullptr)
-{
-}
-
-template<class T>
-QStringHash<T>::QStringHash(const QStringHash<T> &other)
-: newedNodes(nullptr), nodePool(nullptr), link(nullptr)
-{
- data.numBits = other.data.numBits;
- data.size = other.data.size;
- reserve(other.count());
- copy(other);
-}
-
-template<class T>
-QStringHash<T> &QStringHash<T>::operator=(const QStringHash<T> &other)
-{
- if (&other == this)
- return *this;
-
- clear();
-
- data.numBits = other.data.numBits;
- data.size = other.data.size;
- reserve(other.count());
- copy(other);
-
- return *this;
-}
-
-template<class T>
-void QStringHash<T>::copyAndReserve(const QStringHash<T> &other, int additionalReserve)
-{
- clear();
- data.numBits = other.data.numBits;
- reserve(other.count() + additionalReserve);
- copy(other);
-}
-
-template<class T>
-void QStringHash<T>::linkAndReserve(const QStringHash<T> &other, int additionalReserve)
-{
- clear();
-
- if (other.count()) {
- data.size = other.data.size;
- data.rehashToSize(other.count() + additionalReserve);
-
- if (data.numBuckets == other.data.numBuckets) {
- nodePool = new ReservedNodePool;
- nodePool->count = additionalReserve;
- nodePool->used = 0;
- nodePool->nodes = new Node[additionalReserve];
-
-#ifdef QSTRINGHASH_LINK_DEBUG
- data.linkCount++;
- const_cast<QStringHash<T>&>(other).data.linkCount++;
-#endif
-
- for (int ii = 0; ii < data.numBuckets; ++ii)
- data.buckets[ii] = (Node *)other.data.buckets[ii];
-
- link = &other;
- return;
- }
-
- data.size = 0;
- }
-
- data.numBits = other.data.numBits;
- reserve(other.count() + additionalReserve);
- copy(other);
-}
-
-template<class T>
-QStringHash<T>::~QStringHash()
-{
- clear();
-}
-
-template<class T>
-void QStringHash<T>::clear()
-{
-#ifdef QSTRINGHASH_LINK_DEBUG
- if (link) {
- data.linkCount--;
- const_cast<QStringHash<T> *>(link)->data.linkCount--;
- }
-
- if (data.linkCount)
- qFatal("QStringHash: Illegal attempt to clear a linked hash.");
-#endif
-
- // Delete the individually allocated nodes
- NewedNode *n = newedNodes;
- while (n) {
- NewedNode *c = n;
- n = c->nextNewed;
- delete c;
- }
- // Delete the pool allocated nodes
- if (nodePool) delete nodePool;
- delete [] data.buckets;
-
- data.buckets = nullptr;
- data.numBuckets = 0;
- data.numBits = 0;
- data.size = 0;
-
- newedNodes = nullptr;
- nodePool = nullptr;
- link = nullptr;
-}
-
-template<class T>
-bool QStringHash<T>::isEmpty() const
-{
- return data.size== 0;
-}
-
-template<class T>
-int QStringHash<T>::count() const
-{
- return data.size;
-}
-
-template<class T>
-int QStringHash<T>::numBuckets() const
-{
- return data.numBuckets;
-}
-
-template<class T>
-bool QStringHash<T>::isLinked() const
-{
- return link != 0;
-}
-
-template<class T>
-void QStringHash<T>::initializeNode(Node *node, const QHashedString &key)
-{
- node->length = key.length();
- node->hash = key.hash();
- node->strData = const_cast<QHashedString &>(key).data_ptr();
- node->strData->ref.ref();
- node->setQString(true);
-}
-
-template<class T>
-void QStringHash<T>::initializeNode(Node *node, const QHashedCStringRef &key)
-{
- node->length = key.length();
- node->hash = key.hash();
- node->ckey = key.constData();
-}
-
-template<class T>
-template<class K>
-typename QStringHash<T>::Node *QStringHash<T>::takeNode(const K &key, const T &value)
-{
- if (nodePool && nodePool->used != nodePool->count) {
- Node *rv = nodePool->nodes + nodePool->used++;
- initializeNode(rv, hashedString(key));
- rv->value = value;
- return rv;
- } else {
- NewedNode *rv = new NewedNode(hashedString(key), value);
- rv->nextNewed = newedNodes;
- newedNodes = rv;
- return rv;
- }
-}
-
-template<class T>
-typename QStringHash<T>::Node *QStringHash<T>::takeNode(const Node &o)
-{
- if (nodePool && nodePool->used != nodePool->count) {
- Node *rv = nodePool->nodes + nodePool->used++;
- rv->length = o.length;
- rv->hash = o.hash;
- if (o.isQString()) {
- rv->strData = o.strData;
- rv->strData->ref.ref();
- rv->setQString(true);
- } else {
- rv->ckey = o.ckey;
- }
- rv->symbolId = o.symbolId;
- rv->value = o.value;
- return rv;
- } else {
- NewedNode *rv = new NewedNode(o);
- rv->nextNewed = newedNodes;
- newedNodes = rv;
- return rv;
- }
-}
-
-template<class T>
-void QStringHash<T>::copyNode(const QStringHashNode *otherNode)
-{
- // Copy the predecessor before the successor
- QStringHashNode *next = otherNode->next.data();
- if (next)
- copyNode(next);
-
- Node *mynode = takeNode(*(const Node *)otherNode);
- int bucket = mynode->hash % data.numBuckets;
- mynode->next = data.buckets[bucket];
- data.buckets[bucket] = mynode;
-}
-
-template<class T>
-void QStringHash<T>::copy(const QStringHash<T> &other)
-{
- Q_ASSERT(data.size == 0);
-
- data.size = other.data.size;
-
- // Ensure buckets array is created
- data.rehashToBits(data.numBits);
-
- // Preserve the existing order within buckets
- for (int i = 0; i < other.data.numBuckets; ++i) {
- QStringHashNode *bucket = other.data.buckets[i];
- if (bucket)
- copyNode(bucket);
- }
-}
-
-template<class T>
-QStringHashData::IteratorData
-QStringHash<T>::iterateNext(const QStringHashData::IteratorData &d)
-{
- QStringHash<T> *This = (QStringHash<T> *)d.p;
- Node *node = (Node *)d.n;
-
- if (This->nodePool && node >= This->nodePool->nodes &&
- node < (This->nodePool->nodes + This->nodePool->used)) {
- node--;
- if (node < This->nodePool->nodes)
- node = nullptr;
- } else {
- NewedNode *nn = (NewedNode *)node;
- node = nn->nextNewed;
-
- if (node == nullptr && This->nodePool && This->nodePool->used)
- node = This->nodePool->nodes + This->nodePool->used - 1;
- }
-
- if (node == nullptr && This->link)
- return This->link->iterateFirst();
-
- QStringHashData::IteratorData rv;
- rv.n = node;
- rv.p = d.p;
- return rv;
-}
-
-template<class T>
-QStringHashData::IteratorData QStringHash<T>::iterateFirst() const
-{
- Node *n = nullptr;
- if (newedNodes)
- n = newedNodes;
- else if (nodePool && nodePool->used)
- n = nodePool->nodes + nodePool->used - 1;
-
- if (n == nullptr && link)
- return link->iterateFirst();
-
- QStringHashData::IteratorData rv;
- rv.n = n;
- rv.p = const_cast<QStringHash<T> *>(this);
- return rv;
-}
-
-template<class T>
-typename QStringHash<T>::ConstIterator QStringHash<T>::iterator(Node *n) const
-{
- if (!n)
- return ConstIterator();
-
- const QStringHash<T> *container = this;
-
- if (link) {
- // This node could be in the linked hash
- if ((n >= nodePool->nodes) && (n < (nodePool->nodes + nodePool->used))) {
- // The node is in this hash
- } else if ((n >= link->nodePool->nodes) && (n < (link->nodePool->nodes + link->nodePool->used))) {
- // The node is in the linked hash
- container = link;
- } else {
- const NewedNode *ln = link->newedNodes;
- while (ln) {
- if (ln == n) {
- // This node is in the linked hash's newed list
- container = link;
- break;
- }
- ln = ln->nextNewed;
- }
- }
- }
-
- QStringHashData::IteratorData rv;
- rv.n = n;
- rv.p = const_cast<QStringHash<T> *>(container);
- return ConstIterator(rv);
-}
-
-template<class T>
-typename QStringHash<T>::Node *QStringHash<T>::createNode(const Node &o)
-{
- Node *n = takeNode(o);
- return insertNode(n, n->hash);
-}
-
-template<class T>
-template<class K>
-typename QStringHash<T>::Node *QStringHash<T>::createNode(const K &key, const T &value)
-{
- Node *n = takeNode(key, value);
- return insertNode(n, hashOf(key));
-}
-
-template<class T>
-typename QStringHash<T>::Node *QStringHash<T>::insertNode(Node *n, quint32 hash)
-{
- if (data.size >= data.numBuckets)
- data.rehashToBits(data.numBits + 1);
-
- int bucket = hash % data.numBuckets;
- n->next = data.buckets[bucket];
- data.buckets[bucket] = n;
-
- data.size++;
-
- return n;
-}
-
-template<class T>
-template<class K>
-void QStringHash<T>::insert(const K &key, const T &value)
-{
- // If this is a linked hash, we can't rely on owning the node, so we always
- // create a new one.
- Node *n = link?nullptr:findNode(key);
- if (n) n->value = value;
- else createNode(key, value);
-}
-
-template<class T>
-void QStringHash<T>::insert(const ConstIterator &iter)
-{
- insert(iter.key(), iter.value());
-}
-
-template<class T>
-template<class K>
-typename QStringHash<T>::Node *QStringHash<T>::findNode(const K &key) const
-{
- QStringHashNode *node = data.numBuckets?data.buckets[hashOf(key) % data.numBuckets]:nullptr;
-
- typename HashedForm<K>::Type hashedKey(hashedString(key));
- while (node && !node->equals(hashedKey))
- node = (*node->next);
-
- return (Node *)node;
-}
-
-template<class T>
-template<class K>
-T *QStringHash<T>::value(const K &key) const
-{
- Node *n = findNode(key);
- return n?&n->value:nullptr;
-}
-
-template<class T>
-T *QStringHash<T>::value(const ConstIterator &iter) const
-{
- Node *n = iter.node();
- return value(n->key());
-}
-
-template<class T>
-T *QStringHash<T>::value(const QV4::String *string) const
-{
- Node *n = findNode(string);
- return n?&n->value:nullptr;
-}
-
-template<class T>
-template<class K>
-bool QStringHash<T>::contains(const K &key) const
-{
- return nullptr != value(key);
-}
-
-template<class T>
-template<class K>
-T &QStringHash<T>::operator[](const K &key)
-{
- Node *n = findNode(key);
- if (n) return n->value;
- else return createNode(key, T())->value;
-}
-
-template<class T>
-void QStringHash<T>::reserve(int n)
-{
- if (nodePool || 0 == n)
- return;
-
- nodePool = new ReservedNodePool;
- nodePool->count = n;
- nodePool->used = 0;
- nodePool->nodes = new Node[n];
-
- data.rehashToSize(n);
-}
-
-template<class T>
-QStringHash<T>::ConstIterator::ConstIterator()
-{
-}
-
-template<class T>
-QStringHash<T>::ConstIterator::ConstIterator(const QStringHashData::IteratorData &d)
-: d(d)
-{
-}
-
-template<class T>
-typename QStringHash<T>::ConstIterator &QStringHash<T>::ConstIterator::operator++()
-{
- d = QStringHash<T>::iterateNext(d);
- return *this;
-}
-
-template<class T>
-bool QStringHash<T>::ConstIterator::operator==(const ConstIterator &o) const
-{
- return d.n == o.d.n;
-}
-
-template<class T>
-bool QStringHash<T>::ConstIterator::operator!=(const ConstIterator &o) const
-{
- return d.n != o.d.n;
-}
-
-template<class T>
-template<typename K>
-bool QStringHash<T>::ConstIterator::equals(const K &key) const
-{
- return d.n->equals(key);
-}
-
-template<class T>
-QHashedString QStringHash<T>::ConstIterator::key() const
-{
- Node *n = (Node *)d.n;
- return n->key();
-}
-template<class T>
-const T &QStringHash<T>::ConstIterator::value() const
-{
- Node *n = (Node *)d.n;
- return n->value;
-}
-
-template<class T>
-const T &QStringHash<T>::ConstIterator::operator*() const
-{
- Node *n = (Node *)d.n;
- return n->value;
-}
-
-template<class T>
-typename QStringHash<T>::Node *QStringHash<T>::ConstIterator::node() const
-{
- Node *n = (Node *)d.n;
- return n;
-}
-
-template<class T>
-typename QStringHash<T>::ConstIterator QStringHash<T>::begin() const
-{
- return ConstIterator(iterateFirst());
-}
-
-template<class T>
-typename QStringHash<T>::ConstIterator QStringHash<T>::end() const
-{
- return ConstIterator();
-}
-
-template<class T>
-template<class K>
-typename QStringHash<T>::ConstIterator QStringHash<T>::find(const K &key) const
-{
- return iterator(findNode(key));
-}
-
-template<class T>
-class QStringMultiHash : public QStringHash<T>
-{
-public:
- typedef typename QStringHash<T>::ConstIterator ConstIterator;
-
- template<typename K>
- inline void insert(const K &, const T &);
-
- inline void insert(const ConstIterator &);
-
- inline ConstIterator findNext(const ConstIterator &) const;
-};
-
-template<class T>
-template<class K>
-void QStringMultiHash<T>::insert(const K &key, const T &value)
-{
- // Always create a new node
- QStringHash<T>::createNode(key, value);
-}
-
-template<class T>
-void QStringMultiHash<T>::insert(const ConstIterator &iter)
-{
- // Always create a new node
- QStringHash<T>::createNode(iter.key(), iter.value());
-}
-
-template<class T>
-typename QStringHash<T>::ConstIterator QStringMultiHash<T>::findNext(const ConstIterator &iter) const
-{
- QStringHashNode *node = iter.node();
- if (node) {
- QHashedString key(node->key());
-
- while ((node = *node->next)) {
- if (node->equals(key)) {
- return QStringHash<T>::iterator(static_cast<typename QStringHash<T>::Node *>(node));
- }
- }
- }
-
- return ConstIterator();
-}
-
inline uint qHash(const QHashedString &string)
{
return uint(string.hash());
diff --git a/src/qml/qml/ftw/qlinkedstringhash_p.h b/src/qml/qml/ftw/qlinkedstringhash_p.h
new file mode 100644
index 0000000000..67ced7fbbf
--- /dev/null
+++ b/src/qml/qml/ftw/qlinkedstringhash_p.h
@@ -0,0 +1,238 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLINKEDSTRINGHASH_P_H
+#define QLINKEDSTRINGHASH_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/qstringhash_p.h>
+
+QT_BEGIN_NAMESPACE
+
+template<class T>
+class QLinkedStringHash : private QStringHash<T>
+{
+public:
+ using typename QStringHash<T>::Node;
+ using typename QStringHash<T>::NewedNode;
+ using typename QStringHash<T>::ReservedNodePool;
+ using typename QStringHash<T>::mapped_type;
+
+ using ConstIteratorData = QStringHashData::IteratorData<const QLinkedStringHash>;
+ using ConstIterator = typename QStringHash<T>::template Iterator<ConstIteratorData, const T>;
+
+ void linkAndReserve(const QLinkedStringHash<T> &other, int additionalReserve)
+ {
+ clear();
+
+ if (other.count()) {
+ data.size = other.data.size;
+ data.rehashToSize(other.count() + additionalReserve);
+
+ if (data.numBuckets == other.data.numBuckets) {
+ nodePool = new ReservedNodePool;
+ nodePool->count = additionalReserve;
+ nodePool->used = 0;
+ nodePool->nodes = new Node[additionalReserve];
+
+ for (int ii = 0; ii < data.numBuckets; ++ii)
+ data.buckets[ii] = (Node *)other.data.buckets[ii];
+
+ link = &other;
+ return;
+ }
+
+ data.size = 0;
+ }
+
+ data.numBits = other.data.numBits;
+ reserve(other.count() + additionalReserve);
+ copy(other);
+ }
+
+ inline bool isLinked() const
+ {
+ return link != 0;
+ }
+
+ void clear()
+ {
+ QStringHash<T>::clear();
+ link = nullptr;
+ }
+
+ template<typename K>
+ void insert(const K &key, const T &value)
+ {
+ // If this is a linked hash, we can't rely on owning the node, so we always
+ // create a new one.
+ Node *n = link ? nullptr : QStringHash<T>::findNode(key);
+ if (n)
+ n->value = value;
+ else
+ QStringHash<T>::createNode(key, value);
+ }
+
+ template<typename K>
+ inline ConstIterator find(const K &key) const
+ {
+ return iterator(QStringHash<T>::findNode(key));
+ }
+
+ ConstIterator begin() const
+ {
+ return ConstIterator(
+ QStringHash<T>::template iterateFirst<const QLinkedStringHash<T>,
+ ConstIteratorData>(this));
+ }
+
+ ConstIterator end() const { return ConstIterator(); }
+
+ inline T *value(const ConstIterator &iter) { return value(iter.node()->key()); }
+
+ using QStringHash<T>::value;
+ using QStringHash<T>::reserve;
+ using QStringHash<T>::copy;
+
+protected:
+ friend QStringHash<T>;
+ using QStringHash<T>::data;
+ using QStringHash<T>::nodePool;
+
+ using QStringHash<T>::createNode;
+
+ inline ConstIteratorData iterateFirst() const
+ {
+ const ConstIteratorData rv
+ = QStringHash<T>::template iterateFirst<const QLinkedStringHash<T>,
+ ConstIteratorData>(this);
+ return (rv.n == nullptr && link) ? link->iterateFirst() : rv;
+ }
+
+ static inline ConstIteratorData iterateNext(const ConstIteratorData &d)
+ {
+ const QLinkedStringHash<T> *self = d.p;
+ const ConstIteratorData rv = QStringHash<T>::iterateNext(d);
+ return (rv.n == nullptr && self->link) ? self->link->iterateFirst() : rv;
+ }
+
+ inline ConstIterator iterator(Node *n) const
+ {
+ if (!n)
+ return ConstIterator();
+
+ const QLinkedStringHash<T> *container = this;
+
+ if (link) {
+ // This node could be in the linked hash
+ if ((n >= nodePool->nodes) && (n < (nodePool->nodes + nodePool->used))) {
+ // The node is in this hash
+ } else if ((n >= link->nodePool->nodes)
+ && (n < (link->nodePool->nodes + link->nodePool->used))) {
+ // The node is in the linked hash
+ container = link;
+ } else {
+ const NewedNode *ln = link->newedNodes;
+ while (ln) {
+ if (ln == n) {
+ // This node is in the linked hash's newed list
+ container = link;
+ break;
+ }
+ ln = ln->nextNewed;
+ }
+ }
+ }
+
+
+ ConstIteratorData rv;
+ rv.n = n;
+ rv.p = container;
+ return ConstIterator(rv);
+ }
+
+ const QLinkedStringHash<T> *link = nullptr;
+};
+
+template<class T>
+class QLinkedStringMultiHash : public QLinkedStringHash<T>
+{
+public:
+ using ConstIterator = typename QLinkedStringHash<T>::ConstIterator;
+
+ template<typename K>
+ inline void insert(const K &key, const T &value)
+ {
+ // Always create a new node
+ QLinkedStringHash<T>::createNode(key, value);
+ }
+
+ inline void insert(const ConstIterator &iter)
+ {
+ // Always create a new node
+ QLinkedStringHash<T>::createNode(iter.key(), iter.value());
+ }
+
+ inline ConstIterator findNext(const ConstIterator &iter) const
+ {
+ if (auto *node = iter.node()) {
+ QHashedString key(node->key());
+ while ((node = static_cast<typename QLinkedStringHash<T>::Node *>(*node->next))) {
+ if (node->equals(key))
+ return QLinkedStringHash<T>::iterator(node);
+ }
+ }
+
+ return ConstIterator();
+ }
+};
+
+QT_END_NAMESPACE
+
+#endif // QLINKEDSTRINGHASH_P_H
diff --git a/src/qml/qml/ftw/qprimefornumbits_p.h b/src/qml/qml/ftw/qprimefornumbits_p.h
new file mode 100644
index 0000000000..6e9acbf7fd
--- /dev/null
+++ b/src/qml/qml/ftw/qprimefornumbits_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRIMEFORNUMBITS_P_H
+#define QPRIMEFORNUMBITS_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/qtqmlglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ The prime_deltas array is a table of selected prime values, even
+ though it doesn't look like one. The primes we are using are 1,
+ 2, 5, 11, 17, 37, 67, 131, 257, ..., i.e. primes in the immediate
+ surrounding of a power of two.
+
+ The qPrimeForNumBits() function returns the prime associated to a
+ power of two. For example, qPrimeForNumBits(8) returns 257.
+*/
+
+inline int qPrimeForNumBits(int numBits)
+{
+ static constexpr const uchar prime_deltas[] = {
+ 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3,
+ 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0
+ };
+
+ return (1 << numBits) + prime_deltas[numBits];
+}
+
+QT_END_NAMESPACE
+
+#endif // QPRIMEFORNUMBITS_P_H
diff --git a/src/qml/qml/ftw/qqmlrefcount_p.h b/src/qml/qml/ftw/qqmlrefcount_p.h
index d32a08e0f5..b4f8acad49 100644
--- a/src/qml/qml/ftw/qqmlrefcount_p.h
+++ b/src/qml/qml/ftw/qqmlrefcount_p.h
@@ -60,18 +60,18 @@ QT_BEGIN_NAMESPACE
class Q_QML_PRIVATE_EXPORT QQmlRefCount
{
+ Q_DISABLE_COPY_MOVE(QQmlRefCount)
public:
inline QQmlRefCount();
- inline virtual ~QQmlRefCount();
- inline void addref();
- inline void release();
+ inline void addref() const;
+ inline void release() const;
inline int count() const;
protected:
- inline virtual void destroy();
+ inline virtual ~QQmlRefCount();
private:
- QAtomicInt refCount;
+ mutable QAtomicInt refCount;
};
template<class T>
@@ -113,30 +113,25 @@ QQmlRefCount::QQmlRefCount()
QQmlRefCount::~QQmlRefCount()
{
- Q_ASSERT(refCount.load() == 0);
+ Q_ASSERT(refCount.loadRelaxed() == 0);
}
-void QQmlRefCount::addref()
+void QQmlRefCount::addref() const
{
- Q_ASSERT(refCount.load() > 0);
+ Q_ASSERT(refCount.loadRelaxed() > 0);
refCount.ref();
}
-void QQmlRefCount::release()
+void QQmlRefCount::release() const
{
- Q_ASSERT(refCount.load() > 0);
+ Q_ASSERT(refCount.loadRelaxed() > 0);
if (!refCount.deref())
- destroy();
+ delete this;
}
int QQmlRefCount::count() const
{
- return refCount.load();
-}
-
-void QQmlRefCount::destroy()
-{
- delete this;
+ return refCount.loadRelaxed();
}
template<class T>
diff --git a/src/qml/qml/ftw/qqmlthread.cpp b/src/qml/qml/ftw/qqmlthread.cpp
index e961ed3d0d..7d2ad354d6 100644
--- a/src/qml/qml/ftw/qqmlthread.cpp
+++ b/src/qml/qml/ftw/qqmlthread.cpp
@@ -131,6 +131,9 @@ QQmlThreadPrivate::QQmlThreadPrivate(QQmlThread *q)
m_mainThreadWaiting(false), mainSync(nullptr), m_mainObject(this)
{
setObjectName(QStringLiteral("QQmlThread"));
+ // This size is aligned with the recursion depth limits in the parser/codegen. In case of
+ // absurd content we want to hit the recursion checks instead of running out of stack.
+ setStackSize(8 * 1024 * 1024);
}
bool QQmlThreadPrivate::event(QEvent *e)
diff --git a/src/qml/qml/ftw/qstringhash_p.h b/src/qml/qml/ftw/qstringhash_p.h
new file mode 100644
index 0000000000..f9435b4919
--- /dev/null
+++ b/src/qml/qml/ftw/qstringhash_p.h
@@ -0,0 +1,807 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTRINGHASH_P_H
+#define QSTRINGHASH_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/qhashedstring_p.h>
+#include <private/qprimefornumbits_p.h>
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QStringHashData;
+class QStringHashNode
+{
+public:
+ QStringHashNode()
+ : ckey(nullptr)
+ {
+ }
+
+ QStringHashNode(const QHashedString &key)
+ : length(key.length()), hash(key.hash()), symbolId(0)
+ {
+ strData = const_cast<QHashedString &>(key).data_ptr();
+ setQString(true);
+ strData->ref.ref();
+ }
+
+ QStringHashNode(const QHashedCStringRef &key)
+ : length(key.length()), hash(key.hash()), symbolId(0), ckey(key.constData())
+ {
+ }
+
+ QStringHashNode(const QStringHashNode &o)
+ : length(o.length), hash(o.hash), symbolId(o.symbolId), ckey(o.ckey)
+ {
+ setQString(o.isQString());
+ if (isQString()) { strData->ref.ref(); }
+ }
+
+ ~QStringHashNode()
+ {
+ if (isQString()) { if (!strData->ref.deref()) free(strData); }
+ }
+
+ QFlagPointer<QStringHashNode> next;
+
+ qint32 length = 0;
+ quint32 hash = 0;
+ quint32 symbolId = 0;
+
+ union {
+ const char *ckey;
+ QStringData *strData;
+ };
+
+ inline QHashedString key() const
+ {
+ if (isQString())
+ return QHashedString(QString((QChar *)strData->data(), length), hash);
+
+ return QHashedString(QString::fromLatin1(ckey, length), hash);
+ }
+
+ bool isQString() const { return next.flag(); }
+ void setQString(bool v) { if (v) next.setFlag(); else next.clearFlag(); }
+
+ inline char *cStrData() const { return (char *)ckey; }
+ inline quint16 *utf16Data() const { return (quint16 *)strData->data(); }
+
+ inline bool equals(const QV4::Value &string) const {
+ QString s = string.toQStringNoThrow();
+ if (isQString()) {
+ QStringDataPtr dd;
+ dd.ptr = strData;
+ strData->ref.ref();
+ return QString(dd) == s;
+ } else {
+ return QLatin1String(cStrData(), length) == s;
+ }
+ }
+
+ inline bool equals(const QV4::String *string) const {
+ if (length != string->d()->length() || hash != string->hashValue())
+ return false;
+ if (isQString()) {
+ QStringDataPtr dd;
+ dd.ptr = strData;
+ strData->ref.ref();
+ return QString(dd) == string->toQString();
+ } else {
+ return QLatin1String(cStrData(), length) == string->toQString();
+ }
+ }
+
+ inline bool equals(const QHashedStringRef &string) const {
+ return length == string.length() &&
+ hash == string.hash() &&
+ (isQString()?QHashedString::compare(string.constData(), (const QChar *)utf16Data(), length):
+ QHashedString::compare(string.constData(), cStrData(), length));
+ }
+
+ inline bool equals(const QHashedCStringRef &string) const {
+ return length == string.length() &&
+ hash == string.hash() &&
+ (isQString()?QHashedString::compare((const QChar *)utf16Data(), string.constData(), length):
+ QHashedString::compare(string.constData(), cStrData(), length));
+ }
+};
+
+class QStringHashData
+{
+ Q_DISABLE_COPY_MOVE(QStringHashData)
+public:
+ QStringHashData() = default;
+ ~QStringHashData() = default;
+
+ /*
+ A QHash has initially around pow(2, MinNumBits) buckets. For
+ example, if MinNumBits is 4, it has 17 buckets.
+ */
+ enum { MinNumBits = 4 };
+
+ QStringHashNode **buckets = nullptr; // life cycle managed by QStringHash
+ int numBuckets = 0;
+ int size = 0;
+ short numBits = 0;
+
+ template<typename StringHash>
+ struct IteratorData {
+ IteratorData(QStringHashNode *n = nullptr, StringHash *p = nullptr) : n(n), p(p) {}
+
+ template<typename OtherData>
+ IteratorData(const OtherData &other) : n(other.n), p(other.p) {}
+
+ QStringHashNode *n;
+ StringHash *p;
+ };
+
+ void rehashToBits(short bits)
+ {
+ numBits = qMax(short(MinNumBits), bits);
+
+ int nb = qPrimeForNumBits(numBits);
+ if (nb == numBuckets && buckets)
+ return;
+
+ QStringHashNode **newBuckets = new QStringHashNode *[nb];
+ ::memset(newBuckets, 0, sizeof(QStringHashNode *) * nb);
+
+ // Preserve the existing order within buckets so that items with the
+ // same key will retain the same find/findNext order
+ for (int i = 0; i < numBuckets; ++i) {
+ QStringHashNode *bucket = buckets[i];
+ if (bucket)
+ rehashNode(newBuckets, nb, bucket);
+ }
+
+ delete [] buckets;
+ buckets = newBuckets;
+ numBuckets = nb;
+ }
+
+ void rehashToSize(int size)
+ {
+ short bits = qMax(short(MinNumBits), numBits);
+ while (qPrimeForNumBits(bits) < size)
+ bits++;
+
+ if (bits > numBits)
+ rehashToBits(bits);
+ }
+
+ void rehashNode(QStringHashNode **newBuckets, int nb, QStringHashNode *node)
+ {
+ QStringHashNode *next = node->next.data();
+ if (next)
+ rehashNode(newBuckets, nb, next);
+
+ int bucket = node->hash % nb;
+ node->next = newBuckets[bucket];
+ newBuckets[bucket] = node;
+ }
+};
+
+// For a supplied key type, in what form do we need to keep a hashed version?
+template<typename T>
+struct HashedForm {};
+
+template<> struct HashedForm<QString> { typedef QHashedString Type; };
+template<> struct HashedForm<QStringRef> { typedef QHashedStringRef Type; };
+template<> struct HashedForm<QHashedString> { typedef const QHashedString &Type; };
+template<> struct HashedForm<QV4::String *> { typedef const QV4::String *Type; };
+template<> struct HashedForm<const QV4::String *> { typedef const QV4::String *Type; };
+template<> struct HashedForm<QHashedStringRef> { typedef const QHashedStringRef &Type; };
+template<> struct HashedForm<QLatin1String> { typedef QHashedCStringRef Type; };
+template<> struct HashedForm<QHashedCStringRef> { typedef const QHashedCStringRef &Type; };
+
+class QStringHashBase
+{
+public:
+ static HashedForm<QString>::Type hashedString(const QString &s) { return QHashedString(s);}
+ static HashedForm<QStringRef>::Type hashedString(const QStringRef &s) { return QHashedStringRef(s.constData(), s.size());}
+ static HashedForm<QHashedString>::Type hashedString(const QHashedString &s) { return s; }
+ static HashedForm<QV4::String *>::Type hashedString(QV4::String *s) { return s; }
+ static HashedForm<const QV4::String *>::Type hashedString(const QV4::String *s) { return s; }
+ static HashedForm<QHashedStringRef>::Type hashedString(const QHashedStringRef &s) { return s; }
+
+ static HashedForm<QLatin1String>::Type hashedString(const QLatin1String &s) { return QHashedCStringRef(s.data(), s.size()); }
+ static HashedForm<QHashedCStringRef>::Type hashedString(const QHashedCStringRef &s) { return s; }
+
+ static const QString &toQString(const QString &s) { return s; }
+ static const QString &toQString(const QHashedString &s) { return s; }
+ static QString toQString(const QV4::String *s) { return s->toQString(); }
+ static QString toQString(const QHashedStringRef &s) { return s.toString(); }
+
+ static QString toQString(const QLatin1String &s) { return QString(s); }
+ static QString toQString(const QHashedCStringRef &s) { return s.toUtf16(); }
+
+ static inline quint32 hashOf(const QHashedStringRef &s) { return s.hash(); }
+ static inline quint32 hashOf(QV4::String *s) { return s->hashValue(); }
+ static inline quint32 hashOf(const QV4::String *s) { return s->hashValue(); }
+
+ template<typename K>
+ static inline quint32 hashOf(const K &key) { return hashedString(key).hash(); }
+};
+
+template<class T>
+class QStringHash : public QStringHashBase
+{
+public:
+ typedef QHashedString key_type;
+ typedef T mapped_type;
+
+ using MutableIteratorData = QStringHashData::IteratorData<QStringHash<T>>;
+ using ConstIteratorData = QStringHashData::IteratorData<const QStringHash<T>>;
+
+ struct Node : public QStringHashNode {
+ Node(const QHashedString &key, const T &value) : QStringHashNode(key), value(value) {}
+ Node(const QHashedCStringRef &key, const T &value) : QStringHashNode(key), value(value) {}
+ Node(const Node &o) : QStringHashNode(o), value(o.value) {}
+ Node() {}
+ T value;
+ };
+ struct NewedNode : public Node {
+ NewedNode(const QHashedString &key, const T &value) : Node(key, value), nextNewed(nullptr) {}
+ NewedNode(const QHashedCStringRef &key, const T &value) : Node(key, value), nextNewed(nullptr) {}
+ NewedNode(const Node &o) : Node(o), nextNewed(nullptr) {}
+ NewedNode *nextNewed;
+ };
+ struct ReservedNodePool
+ {
+ ReservedNodePool() : nodes(nullptr) {}
+ ~ReservedNodePool() { delete [] nodes; }
+ int count = 0;
+ int used = 0;
+ Node *nodes;
+ };
+
+ QStringHashData data;
+ NewedNode *newedNodes;
+ ReservedNodePool *nodePool;
+
+ template<typename K>
+ inline Node *findNode(const K &) const;
+
+ inline Node *createNode(const Node &o);
+
+ template<typename K>
+ inline Node *createNode(const K &, const T &);
+
+ inline Node *insertNode(Node *, quint32);
+
+ inline void initializeNode(Node *, const QHashedString &key);
+ inline void initializeNode(Node *, const QHashedCStringRef &key);
+
+ template<typename K>
+ inline Node *takeNode(const K &key, const T &value);
+
+ inline Node *takeNode(const Node &o);
+
+ inline void copy(const QStringHash<T> &);
+
+ void copyNode(const QStringHashNode *otherNode);
+
+ template<typename StringHash, typename Data>
+ static inline Data iterateFirst(StringHash *self);
+
+ template<typename Data>
+ static inline Data iterateNext(const Data &);
+
+public:
+ inline QStringHash();
+ inline QStringHash(const QStringHash &);
+ inline ~QStringHash();
+
+ QStringHash &operator=(const QStringHash<T> &);
+
+ void copyAndReserve(const QStringHash<T> &other, int additionalReserve);
+
+ inline bool isEmpty() const;
+ inline void clear();
+ inline int count() const;
+
+ inline int numBuckets() const;
+
+ template<typename Data, typename Value>
+ class Iterator {
+ public:
+ inline Iterator() = default;
+ inline Iterator(const Data &d) : d(d) {}
+
+ inline Iterator &operator++()
+ {
+ d = QStringHash<T>::iterateNext(d);
+ return *this;
+ }
+
+ inline bool operator==(const Iterator &o) const { return d.n == o.d.n; }
+ inline bool operator!=(const Iterator &o) const { return d.n != o.d.n; }
+
+ template<typename K>
+ inline bool equals(const K &key) const { return d.n->equals(key); }
+
+ inline QHashedString key() const { return static_cast<Node *>(d.n)->key(); }
+ inline Value &value() const { return static_cast<Node *>(d.n)->value; }
+ inline Value &operator*() const { return static_cast<Node *>(d.n)->value; }
+
+ Node *node() const { return static_cast<Node *>(d.n); }
+ private:
+ Data d;
+ };
+
+ using MutableIterator = Iterator<MutableIteratorData, T>;
+ using ConstIterator = Iterator<ConstIteratorData, const T>;
+
+ template<typename K>
+ inline void insert(const K &, const T &);
+ inline void insert(const MutableIterator &);
+ inline void insert(const ConstIterator &);
+
+ template<typename K>
+ inline T *value(const K &) const;
+ inline T *value(const QV4::String *string) const;
+ inline T *value(const MutableIterator &) const;
+ inline T *value(const ConstIterator &) const;
+
+ template<typename K>
+ inline bool contains(const K &) const;
+
+ template<typename K>
+ inline T &operator[](const K &);
+
+ inline MutableIterator begin();
+ inline ConstIterator begin() const;
+ inline ConstIterator constBegin() const { return begin(); }
+
+ inline MutableIterator end();
+ inline ConstIterator end() const;
+ inline ConstIterator constEnd() const { return end(); }
+
+ template<typename K>
+ inline MutableIterator find(const K &);
+
+ template<typename K>
+ inline ConstIterator find(const K &) const;
+
+ inline void reserve(int);
+};
+
+template<class T>
+QStringHash<T>::QStringHash()
+: newedNodes(nullptr), nodePool(nullptr)
+{
+}
+
+template<class T>
+QStringHash<T>::QStringHash(const QStringHash<T> &other)
+: newedNodes(nullptr), nodePool(nullptr)
+{
+ data.numBits = other.data.numBits;
+ data.size = other.data.size;
+ reserve(other.count());
+ copy(other);
+}
+
+template<class T>
+QStringHash<T> &QStringHash<T>::operator=(const QStringHash<T> &other)
+{
+ if (&other == this)
+ return *this;
+
+ clear();
+
+ data.numBits = other.data.numBits;
+ data.size = other.data.size;
+ reserve(other.count());
+ copy(other);
+
+ return *this;
+}
+
+template<class T>
+void QStringHash<T>::copyAndReserve(const QStringHash<T> &other, int additionalReserve)
+{
+ clear();
+ data.numBits = other.data.numBits;
+ reserve(other.count() + additionalReserve);
+ copy(other);
+}
+
+template<class T>
+QStringHash<T>::~QStringHash()
+{
+ clear();
+}
+
+template<class T>
+void QStringHash<T>::clear()
+{
+ // Delete the individually allocated nodes
+ NewedNode *n = newedNodes;
+ while (n) {
+ NewedNode *c = n;
+ n = c->nextNewed;
+ delete c;
+ }
+ // Delete the pool allocated nodes
+ if (nodePool) delete nodePool;
+ delete [] data.buckets;
+
+ data.buckets = nullptr;
+ data.numBuckets = 0;
+ data.numBits = 0;
+ data.size = 0;
+
+ newedNodes = nullptr;
+ nodePool = nullptr;
+}
+
+template<class T>
+bool QStringHash<T>::isEmpty() const
+{
+ return data.size== 0;
+}
+
+template<class T>
+int QStringHash<T>::count() const
+{
+ return data.size;
+}
+
+template<class T>
+int QStringHash<T>::numBuckets() const
+{
+ return data.numBuckets;
+}
+
+template<class T>
+void QStringHash<T>::initializeNode(Node *node, const QHashedString &key)
+{
+ node->length = key.length();
+ node->hash = key.hash();
+ node->strData = const_cast<QHashedString &>(key).data_ptr();
+ node->strData->ref.ref();
+ node->setQString(true);
+}
+
+template<class T>
+void QStringHash<T>::initializeNode(Node *node, const QHashedCStringRef &key)
+{
+ node->length = key.length();
+ node->hash = key.hash();
+ node->ckey = key.constData();
+}
+
+template<class T>
+template<class K>
+typename QStringHash<T>::Node *QStringHash<T>::takeNode(const K &key, const T &value)
+{
+ if (nodePool && nodePool->used != nodePool->count) {
+ Node *rv = nodePool->nodes + nodePool->used++;
+ initializeNode(rv, hashedString(key));
+ rv->value = value;
+ return rv;
+ } else {
+ NewedNode *rv = new NewedNode(hashedString(key), value);
+ rv->nextNewed = newedNodes;
+ newedNodes = rv;
+ return rv;
+ }
+}
+
+template<class T>
+typename QStringHash<T>::Node *QStringHash<T>::takeNode(const Node &o)
+{
+ if (nodePool && nodePool->used != nodePool->count) {
+ Node *rv = nodePool->nodes + nodePool->used++;
+ rv->length = o.length;
+ rv->hash = o.hash;
+ if (o.isQString()) {
+ rv->strData = o.strData;
+ rv->strData->ref.ref();
+ rv->setQString(true);
+ } else {
+ rv->ckey = o.ckey;
+ }
+ rv->symbolId = o.symbolId;
+ rv->value = o.value;
+ return rv;
+ } else {
+ NewedNode *rv = new NewedNode(o);
+ rv->nextNewed = newedNodes;
+ newedNodes = rv;
+ return rv;
+ }
+}
+
+template<class T>
+void QStringHash<T>::copyNode(const QStringHashNode *otherNode)
+{
+ // Copy the predecessor before the successor
+ QStringHashNode *next = otherNode->next.data();
+ if (next)
+ copyNode(next);
+
+ Node *mynode = takeNode(*(const Node *)otherNode);
+ int bucket = mynode->hash % data.numBuckets;
+ mynode->next = data.buckets[bucket];
+ data.buckets[bucket] = mynode;
+}
+
+template<class T>
+void QStringHash<T>::copy(const QStringHash<T> &other)
+{
+ Q_ASSERT(data.size == 0);
+
+ data.size = other.data.size;
+
+ // Ensure buckets array is created
+ data.rehashToBits(data.numBits);
+
+ // Preserve the existing order within buckets
+ for (int i = 0; i < other.data.numBuckets; ++i) {
+ QStringHashNode *bucket = other.data.buckets[i];
+ if (bucket)
+ copyNode(bucket);
+ }
+}
+
+template<class T>
+template<typename Data>
+Data QStringHash<T>::iterateNext(const Data &d)
+{
+ auto *This = d.p;
+ Node *node = (Node *)d.n;
+
+ if (This->nodePool && node >= This->nodePool->nodes &&
+ node < (This->nodePool->nodes + This->nodePool->used)) {
+ node--;
+ if (node < This->nodePool->nodes)
+ node = nullptr;
+ } else {
+ NewedNode *nn = (NewedNode *)node;
+ node = nn->nextNewed;
+
+ if (node == nullptr && This->nodePool && This->nodePool->used)
+ node = This->nodePool->nodes + This->nodePool->used - 1;
+ }
+
+ Data rv;
+ rv.n = node;
+ rv.p = d.p;
+ return rv;
+}
+
+template<class T>
+template<typename StringHash, typename Data>
+Data QStringHash<T>::iterateFirst(StringHash *self)
+{
+ typename StringHash::Node *n = nullptr;
+ if (self->newedNodes)
+ n = self->newedNodes;
+ else if (self->nodePool && self->nodePool->used)
+ n = self->nodePool->nodes + self->nodePool->used - 1;
+
+ Data rv;
+ rv.n = n;
+ rv.p = self;
+ return rv;
+}
+
+template<class T>
+typename QStringHash<T>::Node *QStringHash<T>::createNode(const Node &o)
+{
+ Node *n = takeNode(o);
+ return insertNode(n, n->hash);
+}
+
+template<class T>
+template<class K>
+typename QStringHash<T>::Node *QStringHash<T>::createNode(const K &key, const T &value)
+{
+ Node *n = takeNode(key, value);
+ return insertNode(n, hashOf(key));
+}
+
+template<class T>
+typename QStringHash<T>::Node *QStringHash<T>::insertNode(Node *n, quint32 hash)
+{
+ if (data.size >= data.numBuckets)
+ data.rehashToBits(data.numBits + 1);
+
+ int bucket = hash % data.numBuckets;
+ n->next = data.buckets[bucket];
+ data.buckets[bucket] = n;
+
+ data.size++;
+
+ return n;
+}
+
+template<class T>
+template<class K>
+void QStringHash<T>::insert(const K &key, const T &value)
+{
+ Node *n = findNode(key);
+ if (n)
+ n->value = value;
+ else
+ createNode(key, value);
+}
+
+template<class T>
+void QStringHash<T>::insert(const MutableIterator &iter)
+{
+ insert(iter.key(), iter.value());
+}
+
+template<class T>
+void QStringHash<T>::insert(const ConstIterator &iter)
+{
+ insert(iter.key(), iter.value());
+}
+
+template<class T>
+template<class K>
+typename QStringHash<T>::Node *QStringHash<T>::findNode(const K &key) const
+{
+ QStringHashNode *node = data.numBuckets?data.buckets[hashOf(key) % data.numBuckets]:nullptr;
+
+ typename HashedForm<K>::Type hashedKey(hashedString(key));
+ while (node && !node->equals(hashedKey))
+ node = (*node->next);
+
+ return (Node *)node;
+}
+
+template<class T>
+template<class K>
+T *QStringHash<T>::value(const K &key) const
+{
+ Node *n = findNode(key);
+ return n?&n->value:nullptr;
+}
+
+template<typename T>
+T *QStringHash<T>::value(const MutableIterator &iter) const
+{
+ return value(iter.node()->key());
+}
+
+template<class T>
+T *QStringHash<T>::value(const ConstIterator &iter) const
+{
+ return value(iter.node()->key());
+}
+
+template<class T>
+T *QStringHash<T>::value(const QV4::String *string) const
+{
+ Node *n = findNode(string);
+ return n?&n->value:nullptr;
+}
+
+template<class T>
+template<class K>
+bool QStringHash<T>::contains(const K &key) const
+{
+ return nullptr != value(key);
+}
+
+template<class T>
+template<class K>
+T &QStringHash<T>::operator[](const K &key)
+{
+ Node *n = findNode(key);
+ if (n) return n->value;
+ else return createNode(key, T())->value;
+}
+
+template<class T>
+void QStringHash<T>::reserve(int n)
+{
+ if (nodePool || 0 == n)
+ return;
+
+ nodePool = new ReservedNodePool;
+ nodePool->count = n;
+ nodePool->used = 0;
+ nodePool->nodes = new Node[n];
+
+ data.rehashToSize(n);
+}
+
+template<class T>
+typename QStringHash<T>::MutableIterator QStringHash<T>::begin()
+{
+ return MutableIterator(iterateFirst<QStringHash<T>, MutableIteratorData>(this));
+}
+
+template<class T>
+typename QStringHash<T>::ConstIterator QStringHash<T>::begin() const
+{
+ return ConstIterator(iterateFirst<const QStringHash<T>, ConstIteratorData>(this));
+}
+
+template<class T>
+typename QStringHash<T>::MutableIterator QStringHash<T>::end()
+{
+ return MutableIterator();
+}
+
+template<class T>
+typename QStringHash<T>::ConstIterator QStringHash<T>::end() const
+{
+ return ConstIterator();
+}
+
+template<class T>
+template<class K>
+typename QStringHash<T>::MutableIterator QStringHash<T>::find(const K &key)
+{
+ Node *n = findNode(key);
+ return n ? MutableIterator(MutableIteratorData(n, this)) : MutableIterator();
+}
+
+template<class T>
+template<class K>
+typename QStringHash<T>::ConstIterator QStringHash<T>::find(const K &key) const
+{
+ Node *n = findNode(key);
+ return n ? ConstIterator(ConstIteratorData(n, this)) : ConstIterator();
+}
+
+QT_END_NAMESPACE
+
+#endif // QSTRINGHASH_P_H
diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri
index ca13ce9211..2e9c6f3de6 100644
--- a/src/qml/qml/qml.pri
+++ b/src/qml/qml/qml.pri
@@ -1,5 +1,14 @@
SOURCES += \
+ $$PWD/qqml.cpp \
+ $$PWD/qqmldatablob.cpp \
+ $$PWD/qqmldirdata.cpp \
+ $$PWD/qqmlerror.cpp \
$$PWD/qqmlopenmetaobject.cpp \
+ $$PWD/qqmlscriptblob.cpp \
+ $$PWD/qqmlscriptdata.cpp \
+ $$PWD/qqmltypedata.cpp \
+ $$PWD/qqmltypeloaderqmldircontent.cpp \
+ $$PWD/qqmltypeloaderthread.cpp \
$$PWD/qqmlvmemetaobject.cpp \
$$PWD/qqmlengine.cpp \
$$PWD/qqmlexpression.cpp \
@@ -14,14 +23,21 @@ SOURCES += \
$$PWD/qqmlvme.cpp \
$$PWD/qqmlboundsignal.cpp \
$$PWD/qqmlmetatype.cpp \
+ $$PWD/qqmlmetatypedata.cpp \
$$PWD/qqmlstringconverters.cpp \
+ $$PWD/qqmltype.cpp \
+ $$PWD/qqmltypemodule.cpp \
+ $$PWD/qqmltypemoduleversion.cpp \
$$PWD/qqmlparserstatus.cpp \
$$PWD/qqmltypeloader.cpp \
$$PWD/qqmlinfo.cpp \
$$PWD/qqmlvaluetype.cpp \
$$PWD/qqmlcleanup.cpp \
$$PWD/qqmlpropertycache.cpp \
+ $$PWD/qqmlmetaobject.cpp \
$$PWD/qqmlnotifier.cpp \
+ $$PWD/qqmlobjectorgadget.cpp \
+ $$PWD/qqmlstaticmetaobject.cpp \
$$PWD/qqmltypenotavailable.cpp \
$$PWD/qqmltypenamecache.cpp \
$$PWD/qqmlscriptstring.cpp \
@@ -44,13 +60,26 @@ SOURCES += \
$$PWD/qqmlfileselector.cpp \
$$PWD/qqmlobjectcreator.cpp \
$$PWD/qqmldelayedcallqueue.cpp \
- $$PWD/qqmlloggingcategory.cpp
+ $$PWD/qqmlloggingcategory.cpp \
+ $$PWD/qqmlirloader.cpp \
+ $$PWD/qqmlpropertyresolver.cpp \
+ $$PWD/qqmltypecompiler.cpp \
+ $$PWD/qqmlpropertycachecreator.cpp \
+ $$PWD/qqmlpropertyvalidator.cpp
HEADERS += \
+ $$PWD/qqmldatablob_p.h \
+ $$PWD/qqmldirdata_p.h \
$$PWD/qqmlglobal_p.h \
$$PWD/qqmlopenmetaobject_p.h \
+ $$PWD/qqmlscriptblob_p.h \
+ $$PWD/qqmlscriptdata_p.h \
+ $$PWD/qqmltypedata_p.h \
+ $$PWD/qqmltypeloaderqmldircontent_p.h \
+ $$PWD/qqmltypeloaderthread_p.h \
$$PWD/qqmlvmemetaobject_p.h \
$$PWD/qqml.h \
+ $$PWD/qqmlerror.h \
$$PWD/qqmlproperty.h \
$$PWD/qqmlcomponent.h \
$$PWD/qqmlcomponent_p.h \
@@ -68,6 +97,12 @@ HEADERS += \
$$PWD/qqmlexpression_p.h \
$$PWD/qqmlprivate.h \
$$PWD/qqmlmetatype_p.h \
+ $$PWD/qqmlmetatypedata_p.h \
+ $$PWD/qqmltype_p.h \
+ $$PWD/qqmltype_p_p.h \
+ $$PWD/qqmltypemodule_p.h \
+ $$PWD/qqmltypemodule_p_p.h \
+ $$PWD/qqmltypemoduleversion_p.h \
$$PWD/qqmlengine.h \
$$PWD/qqmlcontext.h \
$$PWD/qqmlexpression.h \
@@ -81,9 +116,17 @@ HEADERS += \
$$PWD/qqmldata_p.h \
$$PWD/qqmlvaluetype_p.h \
$$PWD/qqmlcleanup_p.h \
+ $$PWD/qqmlenumdata_p.h \
+ $$PWD/qqmlenumvalue_p.h \
$$PWD/qqmlpropertycache_p.h \
+ $$PWD/qqmlpropertycachemethodarguments_p.h \
+ $$PWD/qqmlpropertycachevector_p.h \
+ $$PWD/qqmlpropertydata_p.h \
$$PWD/qqmlpropertyindex_p.h \
+ $$PWD/qqmlmetaobject_p.h \
$$PWD/qqmlnotifier_p.h \
+ $$PWD/qqmlobjectorgadget_p.h \
+ $$PWD/qqmlstaticmetaobject_p.h \
$$PWD/qqmltypenotavailable_p.h \
$$PWD/qqmltypenamecache_p.h \
$$PWD/qqmlscriptstring.h \
@@ -110,7 +153,13 @@ HEADERS += \
$$PWD/qqmlfileselector.h \
$$PWD/qqmlobjectcreator_p.h \
$$PWD/qqmldelayedcallqueue_p.h \
- $$PWD/qqmlloggingcategory_p.h
+ $$PWD/qqmlloggingcategory_p.h \
+ $$PWD/qqmlirloader_p.h \
+ $$PWD/qqmlpropertyresolver_p.h \
+ $$PWD/qqmltypecompiler_p.h \
+ $$PWD/qqmlpropertycachecreator_p.h \
+ $$PWD/qqmlpropertyvalidator_p.h \
+ $$PWD/qqmlsourcecoordinate_p.h
qtConfig(qml-xml-http-request) {
HEADERS += \
@@ -129,5 +178,15 @@ qtConfig(qml-locale) {
$$PWD/qqmllocale.cpp
}
+qtConfig(qml-network) {
+ HEADERS += \
+ $$PWD/qqmltypeloadernetworkreplyproxy_p.h
+
+ SOURCES += \
+ $$PWD/qqmltypeloadernetworkreplyproxy.cpp
+}
+
+android: DEFINES += LIBS_SUFFIX='\\"_$${QT_ARCH}.so\\"'
+
include(ftw/ftw.pri)
include(v8/v8.pri)
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
new file mode 100644
index 0000000000..5b16a3c9e2
--- /dev/null
+++ b/src/qml/qml/qqml.cpp
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqml.h"
+
+#include <QtQml/qqmlprivate.h>
+
+#include <private/qqmlengine_p.h>
+#include <private/qqmlmetatype_p.h>
+#include <private/qqmlmetatypedata_p.h>
+#include <private/qqmltype_p_p.h>
+#include <private/qqmltypemodule_p_p.h>
+
+#include <QtCore/qmutex.h>
+
+QT_BEGIN_NAMESPACE
+
+void qmlClearTypeRegistrations() // Declared in qqml.h
+{
+ QQmlMetaType::clearTypeRegistrations();
+ QQmlEnginePrivate::baseModulesUninitialized = true; //So the engine re-registers its types
+ qmlClearEnginePlugins();
+}
+
+//From qqml.h
+bool qmlProtectModule(const char *uri, int majVersion)
+{
+ return QQmlMetaType::protectModule(uri, majVersion);
+}
+
+//From qqml.h
+void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor)
+{
+ QQmlMetaType::registerModule(uri, versionMajor, versionMinor);
+}
+
+//From qqml.h
+int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
+{
+ return QQmlMetaType::typeId(uri, versionMajor, versionMinor, qmlName);
+}
+
+// From qqmlprivate.h
+QObject *QQmlPrivate::RegisterSingletonFunctor::operator()(QQmlEngine *qeng, QJSEngine *)
+{
+ if (!m_object) {
+ QQmlError error;
+ error.setDescription(QLatin1String("The registered singleton has already been deleted. Ensure that it outlives the engine."));
+ QQmlEnginePrivate::get(qeng)->warning(qeng, error);
+ return nullptr;
+ }
+
+ if (qeng->thread() != m_object->thread()) {
+ QQmlError error;
+ error.setDescription(QLatin1String("Registered object must live in the same thread as the engine it was registered with"));
+ QQmlEnginePrivate::get(qeng)->warning(qeng, error);
+ return nullptr;
+ }
+ if (alreadyCalled) {
+ QQmlError error;
+ error.setDescription(QLatin1String("Singleton registered by registerSingletonInstance must only be accessed from one engine"));
+ QQmlEnginePrivate::get(qeng)->warning(qeng, error);
+ return nullptr;
+ }
+ alreadyCalled = true;
+ qeng->setObjectOwnership(m_object, QQmlEngine::CppOwnership);
+ return m_object;
+};
+
+/*
+This method is "over generalized" to allow us to (potentially) register more types of things in
+the future without adding exported symbols.
+*/
+int QQmlPrivate::qmlregister(RegistrationType type, void *data)
+{
+ if (type == AutoParentRegistration) {
+ return QQmlMetaType::registerAutoParentFunction(
+ *reinterpret_cast<RegisterAutoParent *>(data));
+ } else if (type == QmlUnitCacheHookRegistration) {
+ return QQmlMetaType::registerUnitCacheHook(
+ *reinterpret_cast<RegisterQmlUnitCacheHook *>(data));
+ }
+
+ QQmlType dtype;
+ if (type == TypeRegistration)
+ dtype = QQmlMetaType::registerType(*reinterpret_cast<RegisterType *>(data));
+ else if (type == InterfaceRegistration)
+ dtype = QQmlMetaType::registerInterface(*reinterpret_cast<RegisterInterface *>(data));
+ else if (type == SingletonRegistration)
+ dtype = QQmlMetaType::registerSingletonType(*reinterpret_cast<RegisterSingletonType *>(data));
+ else if (type == CompositeRegistration)
+ dtype = QQmlMetaType::registerCompositeType(*reinterpret_cast<RegisterCompositeType *>(data));
+ else if (type == CompositeSingletonRegistration)
+ dtype = QQmlMetaType::registerCompositeSingletonType(*reinterpret_cast<RegisterCompositeSingletonType *>(data));
+ else
+ return -1;
+
+ if (!dtype.isValid())
+ return -1;
+
+ QQmlMetaType::registerUndeletableType(dtype);
+ return dtype.index();
+}
+
+void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data)
+{
+ switch (type) {
+ case AutoParentRegistration:
+ QQmlMetaType::unregisterAutoParentFunction(reinterpret_cast<AutoParentFunction>(data));
+ break;
+ case QmlUnitCacheHookRegistration:
+ QQmlMetaType::removeCachedUnitLookupFunction(
+ reinterpret_cast<QmlUnitCacheLookupFunction>(data));
+ break;
+ case TypeRegistration:
+ case InterfaceRegistration:
+ case SingletonRegistration:
+ case CompositeRegistration:
+ case CompositeSingletonRegistration:
+ QQmlMetaType::unregisterType(data);
+ break;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h
index 6bd66464b5..b2bf1f328f 100644
--- a/src/qml/qml/qqml.h
+++ b/src/qml/qml/qqml.h
@@ -102,7 +102,7 @@ class QQmlPropertyValueInterceptor;
void Q_QML_EXPORT qmlClearTypeRegistrations();
template<typename T>
-int qmlRegisterType()
+int qmlRegisterAnonymousType(const char *uri, int versionMajor)
{
QML_GETTYPENAMES
@@ -115,7 +115,7 @@ int qmlRegisterType()
nullptr,
QString(),
- nullptr, 0, 0, nullptr, &T::staticMetaObject,
+ uri, versionMajor, 0, nullptr, &T::staticMetaObject,
QQmlPrivate::attachedPropertiesFunc<T>(),
QQmlPrivate::attachedPropertiesMetaObject<T>(),
@@ -133,6 +133,14 @@ int qmlRegisterType()
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
}
+#if QT_DEPRECATED_SINCE(5, 14)
+template<typename T>
+QT_DEPRECATED_VERSION_X_5_14("Use qmlRegisterAnonymousType instead") int qmlRegisterType()
+{
+ return qmlRegisterAnonymousType<T>("", 1);
+}
+#endif
+
int Q_QML_EXPORT qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message);
template<typename T>
@@ -579,9 +587,13 @@ namespace QtQml {
Q_QML_EXPORT void qmlExecuteDeferred(QObject *);
Q_QML_EXPORT QQmlContext *qmlContext(const QObject *);
Q_QML_EXPORT QQmlEngine *qmlEngine(const QObject *);
- Q_QML_EXPORT QObject *qmlAttachedPropertiesObjectById(int, const QObject *, bool create = true);
- Q_QML_EXPORT QObject *qmlAttachedPropertiesObject(int *, const QObject *,
- const QMetaObject *, bool create);
+#if QT_DEPRECATED_SINCE(5, 14)
+ Q_QML_EXPORT QT_DEPRECATED_VERSION_X_5_14("Use qmlAttachedPropertiesObject(QObject *, QQmlAttachedPropertiesFunc, bool")
+ QObject *qmlAttachedPropertiesObjectById(int, const QObject *, bool create = true);
+ Q_QML_EXPORT QT_DEPRECATED_VERSION_X_5_14("Use qmlAttachedPropertiesObject(QObject *, QQmlAttachedPropertiesFunc, bool")
+ QObject *qmlAttachedPropertiesObject(
+ int *, const QObject *, const QMetaObject *, bool create);
+#endif
Q_QML_EXPORT QQmlAttachedPropertiesFunc qmlAttachedPropertiesFunction(QObject *,
const QMetaObject *);
Q_QML_EXPORT QObject *qmlAttachedPropertiesObject(QObject *, QQmlAttachedPropertiesFunc func,
@@ -614,8 +626,6 @@ QObject *qmlAttachedPropertiesObject(const QObject *obj, bool create = true)
return qmlAttachedPropertiesObject(const_cast<QObject *>(obj), func, create);
}
-Q_QML_EXPORT void qmlRegisterBaseTypes(const char *uri, int versionMajor, int versionMinor);
-
inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName,
QJSValue (*callback)(QQmlEngine *, QJSEngine *))
{
@@ -624,13 +634,13 @@ inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versi
uri, versionMajor, versionMinor, typeName,
- callback, nullptr, nullptr, 0, 0
+ callback, nullptr, nullptr, 0, 0, {}
};
return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &api);
}
-enum { QmlCurrentSingletonTypeRegistrationVersion = 2 };
+enum { QmlCurrentSingletonTypeRegistrationVersion = 3 };
template <typename T>
inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName,
QObject *(*callback)(QQmlEngine *, QJSEngine *))
@@ -642,12 +652,40 @@ inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versi
uri, versionMajor, versionMinor, typeName,
- nullptr, callback, &T::staticMetaObject, qRegisterNormalizedMetaType<T *>(pointerName.constData()), 0
+ nullptr, nullptr, &T::staticMetaObject, qRegisterNormalizedMetaType<T *>(pointerName.constData()), 0, callback
};
return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &api);
}
+template <typename T, typename F, typename std::enable_if<std::is_convertible<F, std::function<QObject *(QQmlEngine *, QJSEngine *)>>::value
+ && !std::is_convertible<F, QObject *(*)(QQmlEngine *, QJSEngine *)>::value, void>::type* = nullptr>
+inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName,
+ F&& callback)
+{
+
+ QML_GETTYPENAMES
+
+ QQmlPrivate::RegisterSingletonType api = {
+ QmlCurrentSingletonTypeRegistrationVersion,
+
+ uri, versionMajor, versionMinor, typeName,
+
+ nullptr, nullptr, &T::staticMetaObject, qRegisterNormalizedMetaType<T *>(pointerName.constData()), 0, callback
+ };
+
+ return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &api);
+}
+
+template<typename T>
+inline auto qmlRegisterSingletonInstance(const char *uri, int versionMajor, int versionMinor,
+ const char *typeName, T *cppObject) -> typename std::enable_if<std::is_base_of<QObject, T>::value, int>::type
+{
+ QQmlPrivate::RegisterSingletonFunctor registrationFunctor;
+ registrationFunctor.m_object = cppObject;
+ return qmlRegisterSingletonType<T>(uri, versionMajor, versionMinor, typeName, registrationFunctor);
+}
+
inline int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)
{
if (url.isRelative()) {
diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp
index facd79d211..adb036e2d0 100644
--- a/src/qml/qml/qqmlapplicationengine.cpp
+++ b/src/qml/qml/qqmlapplicationengine.cpp
@@ -37,6 +37,7 @@
**
****************************************************************************/
+#include <QtQml/qqmlfile.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QTranslator>
#include <QQmlComponent>
@@ -62,9 +63,6 @@ void QQmlApplicationEnginePrivate::cleanUp()
obj->disconnect(q);
qDeleteAll(objects);
-#if QT_CONFIG(translation)
- qDeleteAll(translators);
-#endif
}
void QQmlApplicationEnginePrivate::init()
@@ -75,10 +73,11 @@ void QQmlApplicationEnginePrivate::init()
q->connect(q, &QQmlApplicationEngine::exit, QCoreApplication::instance(),
&QCoreApplication::exit, Qt::QueuedConnection);
#if QT_CONFIG(translation)
- QTranslator* qtTranslator = new QTranslator;
+ QTranslator* qtTranslator = new QTranslator(q);
if (qtTranslator->load(QLocale(), QLatin1String("qt"), QLatin1String("_"), QLibraryInfo::location(QLibraryInfo::TranslationsPath), QLatin1String(".qm")))
QCoreApplication::installTranslator(qtTranslator);
- translators << qtTranslator;
+ else
+ delete qtTranslator;
#endif
new QQmlFileSelector(q,q);
QCoreApplication::instance()->setProperty("__qml_using_qqmlapplicationengine", QVariant(true));
@@ -92,13 +91,12 @@ void QQmlApplicationEnginePrivate::loadTranslations(const QUrl &rootFile)
QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(rootFile));
- QTranslator *translator = new QTranslator;
- if (translator->load(QLocale(), QLatin1String("qml"), QLatin1String("_"), fi.path() + QLatin1String("/i18n"), QLatin1String(".qm"))) {
+ Q_Q(QQmlApplicationEngine);
+ QTranslator *translator = new QTranslator(q);
+ if (translator->load(QLocale(), QLatin1String("qml"), QLatin1String("_"), fi.path() + QLatin1String("/i18n"), QLatin1String(".qm")))
QCoreApplication::installTranslator(translator);
- translators << translator;
- } else {
+ else
delete translator;
- }
#else
Q_UNUSED(rootFile)
#endif
@@ -133,7 +131,7 @@ void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c)
q->objectCreated(nullptr, c->url());
break;
case QQmlComponent::Ready: {
- auto newObj = c->create();
+ auto newObj = initialProperties.empty() ? c->create() : c->createWithInitialProperties(initialProperties);
objects << newObj;
QObject::connect(newObj, &QObject::destroyed, q, [&](QObject *obj) { objects.removeAll(obj); });
q->objectCreated(objects.constLast(), c->url());
@@ -281,6 +279,22 @@ void QQmlApplicationEngine::load(const QString &filePath)
}
/*!
+ Sets the \a initialProperties with which the QML component gets initialized after
+ it gets loaded.
+
+
+ \sa QQmlComponent::setInitialProperties
+ \sa QQmlApplicationEngine::load
+ \sa QQmlApplicationEngine::loadData
+ \since 5.14
+*/
+void QQmlApplicationEngine::setInitialProperties(const QVariantMap &initialProperties)
+{
+ Q_D(QQmlApplicationEngine);
+ d->initialProperties = initialProperties;
+}
+
+/*!
Loads the QML given in \a data. The object tree defined by \a data is
instantiated immediately.
diff --git a/src/qml/qml/qqmlapplicationengine.h b/src/qml/qml/qqmlapplicationengine.h
index bb5d6b5d68..2b4de91154 100644
--- a/src/qml/qml/qqmlapplicationengine.h
+++ b/src/qml/qml/qqmlapplicationengine.h
@@ -66,6 +66,7 @@ public:
public Q_SLOTS:
void load(const QUrl &url);
void load(const QString &filePath);
+ void setInitialProperties(const QVariantMap &initialProperties);
void loadData(const QByteArray &data, const QUrl &url = QUrl());
Q_SIGNALS:
diff --git a/src/qml/qml/qqmlapplicationengine_p.h b/src/qml/qml/qqmlapplicationengine_p.h
index 6cf6828832..1279e400e8 100644
--- a/src/qml/qml/qqmlapplicationengine_p.h
+++ b/src/qml/qml/qqmlapplicationengine_p.h
@@ -59,7 +59,6 @@
QT_BEGIN_NAMESPACE
-class QTranslator;
class QFileSelector;
class Q_QML_PRIVATE_EXPORT QQmlApplicationEnginePrivate : public QQmlEnginePrivate
{
@@ -74,10 +73,7 @@ public:
void loadTranslations(const QUrl &rootFile);
void finishLoad(QQmlComponent *component);
QList<QObject *> objects;
-
-#if QT_CONFIG(translation)
- QList<QTranslator *> translators;
-#endif
+ QVariantMap initialProperties;
};
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index b164517011..52572be2b2 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -49,6 +49,7 @@
#include <private/qqmlbuiltinfunctions_p.h>
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlvaluetypewrapper_p.h>
+#include <private/qv4qmlcontext_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qv4variantobject_p.h>
#include <private/qv4jscall_p.h>
@@ -297,8 +298,9 @@ protected:
case QMetaType::Int:
if (result.isInteger())
return doStore<int>(result.integerValue(), pd, flags);
- else if (result.isNumber())
- return doStore<int>(result.doubleValue(), pd, flags);
+ else if (result.isNumber()) {
+ return doStore<int>(QV4::StaticValue::toInteger(result.doubleValue()), pd, flags);
+ }
break;
case QMetaType::Double:
if (result.isNumber())
@@ -335,7 +337,7 @@ protected:
class QQmlTranslationBinding : public GenericBinding<QMetaType::QString> {
public:
- QQmlTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
+ QQmlTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
{
setCompilationUnit(compilationUnit);
m_binding = binding;
@@ -356,7 +358,7 @@ public:
if (!isAddedToObject() || hasError())
return;
- const QString result = m_binding->valueAsString(m_compilationUnit.data());
+ const QString result = m_compilationUnit->bindingValueAsString(m_binding);
Q_ASSERT(targetObject());
@@ -378,7 +380,7 @@ private:
const QV4::CompiledData::Binding *m_binding;
};
-QQmlBinding *QQmlBinding::createTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt)
+QQmlBinding *QQmlBinding::createTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt)
{
QQmlTranslationBinding *b = new QQmlTranslationBinding(unit, binding);
@@ -517,9 +519,9 @@ QString QQmlBinding::expressionIdentifier() const
{
if (auto f = function()) {
QString url = f->sourceFile();
- quint16 lineNumber = f->compiledFunction->location.line;
- quint16 columnNumber = f->compiledFunction->location.column;
- return url + QString::asprintf(":%u:%u", uint(lineNumber), uint(columnNumber));
+ uint lineNumber = f->compiledFunction->location.line;
+ uint columnNumber = f->compiledFunction->location.column;
+ return url + QString::asprintf(":%u:%u", lineNumber, columnNumber);
}
return QStringLiteral("[native code]");
diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h
index f192de4342..7f96b4df9f 100644
--- a/src/qml/qml/qqmlbinding_p.h
+++ b/src/qml/qml/qqmlbinding_p.h
@@ -63,6 +63,7 @@
#include <private/qqmlabstractbinding_p.h>
#include <private/qqmljavascriptexpression_p.h>
+#include <private/qv4functionobject_p.h>
QT_BEGIN_NAMESPACE
@@ -79,7 +80,7 @@ public:
const QString &url = QString(), quint16 lineNumber = 0);
static QQmlBinding *create(const QQmlPropertyData *property, QV4::Function *function,
QObject *obj, QQmlContextData *ctxt, QV4::ExecutionContext *scope);
- static QQmlBinding *createTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, const QV4::CompiledData::Binding *binding,
+ static QQmlBinding *createTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, const QV4::CompiledData::Binding *binding,
QObject *obj, QQmlContextData *ctxt);
~QQmlBinding() override;
diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp
index e5b78591e0..ff01e737ca 100644
--- a/src/qml/qml/qqmlboundsignal.cpp
+++ b/src/qml/qml/qqmlboundsignal.cpp
@@ -44,7 +44,6 @@
#include "qqmlengine_p.h"
#include "qqmlexpression_p.h"
#include "qqmlcontext_p.h"
-#include "qqmlmetatype_p.h"
#include "qqml.h"
#include "qqmlcontext.h"
#include "qqmlglobal_p.h"
@@ -57,6 +56,7 @@
#include <private/qv4value_p.h>
#include <private/qv4jscall_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4qmlcontext_p.h>
#include <QtCore/qdebug.h>
@@ -210,8 +210,6 @@ void QQmlBoundSignalExpression::evaluate(void **a)
} else if (type == QMetaType::Int) {
//### optimization. Can go away if we switch to metaTypeToJS, or be expanded otherwise
jsCall->args[ii] = QV4::Value::fromInt32(*reinterpret_cast<const int*>(a[ii + 1]));
- } else if (type == qMetaTypeId<QQmlV4Handle>()) {
- jsCall->args[ii] = *reinterpret_cast<QQmlV4Handle *>(a[ii + 1]);
} else if (ep->isQObject(type)) {
if (!*reinterpret_cast<void* const *>(a[ii + 1]))
jsCall->args[ii] = QV4::Value::nullValue();
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index 7ad9310160..b26b90d2aa 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -51,8 +51,6 @@
#include "qqmlincubator_p.h"
#include <private/qqmljavascriptexpression_p.h>
-#include <private/qv8engine_p.h>
-
#include <private/qv4functionobject_p.h>
#include <private/qv4script_p.h>
#include <private/qv4scopedvalue_p.h>
@@ -66,7 +64,6 @@
#include <QThreadStorage>
#include <QtCore/qdebug.h>
#include <qqmlinfo.h>
-#include "qqmlmemoryprofiler_p.h"
namespace {
QThreadStorage<int> creationDepth;
@@ -74,7 +71,7 @@ namespace {
QT_BEGIN_NAMESPACE
-class QQmlComponentExtension : public QV8Engine::Deletable
+class QQmlComponentExtension : public QV4::ExecutionEngine::Deletable
{
public:
QQmlComponentExtension(QV4::ExecutionEngine *v4);
@@ -353,6 +350,32 @@ void QQmlComponentPrivate::clear()
compilationUnit = nullptr;
}
+QObject *QQmlComponentPrivate::doBeginCreate(QQmlComponent *q, QQmlContext *context)
+{
+ if (!engine) {
+ // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check
+ qWarning("QQmlComponent: Must provide an engine before calling create");
+ return nullptr;
+ }
+ if (!context)
+ context = engine->rootContext();
+ return q->beginCreate(context);
+}
+
+bool QQmlComponentPrivate::setInitialProperty(QObject *component, const QString& name, const QVariant &value)
+{
+ QQmlProperty prop(component, name);
+ auto privProp = QQmlPropertyPrivate::get(prop);
+ if (!prop.isValid() || !privProp->writeValueProperty(value, nullptr)) {
+ QQmlError error{};
+ error.setUrl(url);
+ error.setDescription(QLatin1String("Could not set property %1").arg(name));
+ state.errors.push_back(error);
+ return false;
+ } else
+ return true;
+}
+
/*!
\internal
*/
@@ -552,7 +575,8 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
/*!
\internal
*/
-QQmlComponent::QQmlComponent(QQmlEngine *engine, QV4::CompiledData::CompilationUnit *compilationUnit, int start, QObject *parent)
+QQmlComponent::QQmlComponent(QQmlEngine *engine, QV4::ExecutableCompilationUnit *compilationUnit,
+ int start, QObject *parent)
: QQmlComponent(engine, parent)
{
Q_D(QQmlComponent);
@@ -781,20 +805,30 @@ QQmlComponent::QQmlComponent(QQmlComponentPrivate &dd, QObject *parent)
QObject *QQmlComponent::create(QQmlContext *context)
{
Q_D(QQmlComponent);
- QML_MEMORY_SCOPE_URL(url());
- if (!d->engine) {
- // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check
- qWarning("QQmlComponent: Must provide an engine before calling create");
- return nullptr;
- }
+ QObject *rv = d->doBeginCreate(this, context);
+ if (rv)
+ completeCreate();
+ return rv;
+}
- if (!context)
- context = d->engine->rootContext();
+/*!
+ Create an object instance of this component, and initialize its toplevel
+ properties with \a initialProperties. \a context specifies the context
+ where the object instance is to be created.
- QObject *rv = beginCreate(context);
- if (rv)
+ \sa QQmlComponent::create
+ \since 5.14
+*/
+QObject *QQmlComponent::createWithInitialProperties(const QVariantMap& initialProperties, QQmlContext *context)
+{
+ Q_D(QQmlComponent);
+
+ QObject *rv = d->doBeginCreate(this, context);
+ if (rv) {
+ setInitialProperties(rv, initialProperties);
completeCreate();
+ }
return rv;
}
@@ -1070,6 +1104,26 @@ void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context,
enginePriv->incubate(incubator, forContextData);
}
+/*!
+ Set toplevel \a properties of the \a component.
+
+
+ This method provides advanced control over component instance creation.
+ In general, programmers should use
+ \l QQmlComponent::createWithInitialProperties to create a component.
+
+ Use this method after beginCreate and before completeCreate has been called.
+ If a provided property does not exist, a warning is issued.
+
+ \since 5.14
+*/
+void QQmlComponent::setInitialProperties(QObject *component, const QVariantMap &properties)
+{
+ Q_D(QQmlComponent);
+ for (auto it = properties.constBegin(); it != properties.constEnd(); ++it)
+ d->setInitialProperty(component, it.key(), it.value());
+}
+
/*
This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData
arguments instead of QQmlContext which means we don't have to construct the rather weighty
diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h
index 20199d0b21..f259c99b08 100644
--- a/src/qml/qml/qqmlcomponent.h
+++ b/src/qml/qml/qqmlcomponent.h
@@ -59,9 +59,7 @@ class QQmlComponentPrivate;
class QQmlComponentAttached;
namespace QV4 {
-namespace CompiledData {
-struct CompilationUnit;
-}
+class ExecutableCompilationUnit;
}
class Q_QML_EXPORT QQmlComponent : public QObject
@@ -102,6 +100,8 @@ public:
QUrl url() const;
virtual QObject *create(QQmlContext *context = nullptr);
+ QObject *createWithInitialProperties(const QVariantMap& initialProperties, QQmlContext *context = nullptr);
+ void setInitialProperties(QObject *component, const QVariantMap &properties);
virtual QObject *beginCreate(QQmlContext *);
virtual void completeCreate();
@@ -128,7 +128,8 @@ protected:
Q_INVOKABLE void incubateObject(QQmlV4Function *);
private:
- QQmlComponent(QQmlEngine *, QV4::CompiledData::CompilationUnit *compilationUnit, int, QObject *parent);
+ QQmlComponent(QQmlEngine *, QV4::ExecutableCompilationUnit *compilationUnit, int,
+ QObject *parent);
Q_DISABLE_COPY(QQmlComponent)
friend class QQmlTypeData;
diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h
index 4d9e4c6c15..2170646b89 100644
--- a/src/qml/qml/qqmlcomponent_p.h
+++ b/src/qml/qml/qqmlcomponent_p.h
@@ -60,6 +60,7 @@
#include "qqmlerror.h"
#include "qqml.h"
#include <private/qqmlobjectcreator_p.h>
+#include <private/qqmltypedata_p.h>
#include <QtCore/QString>
#include <QtCore/QStringList>
@@ -105,7 +106,7 @@ public:
qreal progress;
int start;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
struct ConstructionState {
ConstructionState()
@@ -142,6 +143,9 @@ public:
static QQmlComponentPrivate *get(QQmlComponent *c) {
return static_cast<QQmlComponentPrivate *>(QObjectPrivate::get(c));
}
+
+ QObject *doBeginCreate(QQmlComponent *q, QQmlContext *context);
+ bool setInitialProperty(QObject *component, const QString &name, const QVariant& value);
};
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp
index 3710cee162..f75a076bcb 100644
--- a/src/qml/qml/qqmlcontext.cpp
+++ b/src/qml/qml/qqmlcontext.cpp
@@ -317,6 +317,12 @@ void QQmlContext::setContextProperty(const QString &name, const QVariant &value)
d->propertyValues[idx] = value;
QMetaObject::activate(this, d->notifyIndex, idx, nullptr);
}
+
+ if (auto *obj = qvariant_cast<QObject *>(value)) {
+ connect(obj, &QObject::destroyed, this, [d, name](QObject *destroyed) {
+ d->dropDestroyedQObject(name, destroyed);
+ });
+ }
}
/*!
@@ -352,7 +358,7 @@ void QQmlContext::setContextProperties(const QVector<PropertyPair> &properties)
data->expressions = nullptr;
data->childContexts = nullptr;
- for (auto property : properties)
+ for (const auto &property : properties)
setContextProperty(property.name, property.value);
data->expressions = expressions;
@@ -438,23 +444,20 @@ QUrl QQmlContext::resolvedUrl(const QUrl &src)
QUrl QQmlContextData::resolvedUrl(const QUrl &src)
{
- QQmlContextData *ctxt = this;
-
QUrl resolved;
if (src.isRelative() && !src.isEmpty()) {
- if (ctxt) {
- while(ctxt) {
- if (ctxt->url().isValid())
- break;
- else
- ctxt = ctxt->parent;
- }
-
- if (ctxt)
- resolved = ctxt->url().resolved(src);
- else if (engine)
- resolved = engine->baseUrl().resolved(src);
- }
+ QQmlContextData *ctxt = this;
+ do {
+ if (ctxt->url().isValid())
+ break;
+ else
+ ctxt = ctxt->parent;
+ } while (ctxt);
+
+ if (ctxt)
+ resolved = ctxt->url().resolved(src);
+ else if (engine)
+ resolved = engine->baseUrl().resolved(src);
} else {
resolved = src;
}
@@ -527,6 +530,20 @@ QObject *QQmlContextPrivate::context_at(QQmlListProperty<QObject> *prop, int ind
}
}
+void QQmlContextPrivate::dropDestroyedQObject(const QString &name, QObject *destroyed)
+{
+ if (!data->isValid())
+ return;
+
+ const int idx = data->propertyNames().value(name);
+ Q_ASSERT(idx >= 0);
+ if (qvariant_cast<QObject *>(propertyValues[idx]) != destroyed)
+ return;
+
+ propertyValues[idx] = QVariant::fromValue<QObject *>(nullptr);
+ QMetaObject::activate(q_func(), notifyIndex, idx, nullptr);
+}
+
QQmlContextData::QQmlContextData()
: QQmlContextData(nullptr)
@@ -845,7 +862,7 @@ QQmlContextPrivate *QQmlContextData::asQQmlContextPrivate()
return QQmlContextPrivate::get(asQQmlContext());
}
-void QQmlContextData::initFromTypeCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, int subComponentIndex)
+void QQmlContextData::initFromTypeCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, int subComponentIndex)
{
typeCompilationUnit = unit;
componentObjectIndex = subComponentIndex == -1 ? /*root object*/0 : subComponentIndex;
diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h
index 7e3cef8e1d..5f7316b00c 100644
--- a/src/qml/qml/qqmlcontext_p.h
+++ b/src/qml/qml/qqmlcontext_p.h
@@ -66,7 +66,7 @@
#include <private/qflagpointer_p.h>
#include <private/qqmlguard_p.h>
-#include <private/qv4compileddata_p.h>
+#include <private/qv4executablecompilationunit_p.h>
#include <private/qv4identifier_p.h>
QT_BEGIN_NAMESPACE
@@ -104,6 +104,8 @@ public:
static int context_count(QQmlListProperty<QObject> *);
static QObject *context_at(QQmlListProperty<QObject> *, int);
+
+ void dropDestroyedQObject(const QString &name, QObject *destroyed);
};
class QQmlComponentAttached;
@@ -152,12 +154,12 @@ public:
QQmlIncubatorPrivate *incubator;
// Compilation unit for contexts that belong to a compiled type.
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> typeCompilationUnit;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> typeCompilationUnit;
// object index in CompiledData::Unit to component that created this context
int componentObjectIndex;
- void initFromTypeCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, int subComponentIndex);
+ void initFromTypeCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, int subComponentIndex);
// flag indicates whether the context owns the cache (after mutation) or not.
mutable QV4::IdentifierHash propertyNameCache;
diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp
index 5cf87f5264..a5f34dafdf 100644
--- a/src/qml/qml/qqmlcustomparser.cpp
+++ b/src/qml/qml/qqmlcustomparser.cpp
@@ -39,7 +39,7 @@
#include "qqmlcustomparser_p.h"
-#include <private/qqmltypecompiler_p.h>
+#include <private/qv4compileddata_p.h>
#include <QtCore/qdebug.h>
@@ -100,7 +100,12 @@ void QQmlCustomParser::clearErrors()
*/
void QQmlCustomParser::error(const QV4::CompiledData::Location &location, const QString &description)
{
- exceptions << QQmlCompileError(location, description);
+ QQmlJS::DiagnosticMessage error;
+ error.line = location.line;
+ error.column = location.column;
+ error.message = description;
+
+ exceptions << error;
}
struct StaticQtMetaObject : public QObject
diff --git a/src/qml/qml/qqmlcustomparser_p.h b/src/qml/qml/qqmlcustomparser_p.h
index bf28bca447..df8cbc9072 100644
--- a/src/qml/qml/qqmlcustomparser_p.h
+++ b/src/qml/qml/qqmlcustomparser_p.h
@@ -51,10 +51,9 @@
// We mean it.
//
-#include "qqmlmetatype_p.h"
#include "qqmlerror.h"
#include "qqmlbinding_p.h"
-#include <private/qqmltypecompiler_p.h>
+#include <private/qv4compileddata_p.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qxmlstream.h>
@@ -81,10 +80,10 @@ public:
void clearErrors();
Flags flags() const { return m_flags; }
- virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) = 0;
- virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) = 0;
+ virtual void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) = 0;
+ virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) = 0;
- QVector<QQmlCompileError> errors() const { return exceptions; }
+ QVector<QQmlJS::DiagnosticMessage> errors() const { return exceptions; }
protected:
void error(const QV4::CompiledData::Binding *binding, const QString& description)
@@ -98,7 +97,7 @@ protected:
const QMetaObject *resolveType(const QString&) const;
private:
- QVector<QQmlCompileError> exceptions;
+ QVector<QQmlJS::DiagnosticMessage> exceptions;
QQmlEnginePrivate *engine;
const QQmlPropertyValidator *validator;
Flags m_flags;
diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h
index f4c03fc17c..299476f5c8 100644
--- a/src/qml/qml/qqmldata_p.h
+++ b/src/qml/qml/qqmldata_p.h
@@ -76,8 +76,8 @@ class QQmlDataExtended;
class QQmlNotifierEndpoint;
namespace QV4 {
+class ExecutableCompilationUnit;
namespace CompiledData {
-struct CompilationUnit;
struct Binding;
}
}
@@ -226,14 +226,14 @@ public:
~DeferredData();
unsigned int deferredIdx;
QMultiHash<int, const QV4::CompiledData::Binding *> bindings;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;//Not always the same as the other compilation unit
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;//Not always the same as the other compilation unit
QQmlContextData *context;//Could be either context or outerContext
Q_DISABLE_COPY(DeferredData);
};
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
QVector<DeferredData *> deferredData;
- void deferData(int objectIndex, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, QQmlContextData *);
+ void deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, QQmlContextData *);
void releaseDeferredData();
QV4::WeakValue jsWrapper;
diff --git a/src/qml/qml/qqmldatablob.cpp b/src/qml/qml/qqmldatablob.cpp
new file mode 100644
index 0000000000..750fc6de50
--- /dev/null
+++ b/src/qml/qml/qqmldatablob.cpp
@@ -0,0 +1,639 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qqmldatablob_p.h>
+#include <private/qqmlglobal_p.h>
+#include <private/qqmlprofiler_p.h>
+#include <private/qqmltypeloader_p.h>
+#include <private/qqmltypeloaderthread_p.h>
+
+#include <QtQml/qqmlengine.h>
+
+#ifdef DATABLOB_DEBUG
+#define ASSERT_CALLBACK() do { if (!m_typeLoader || !m_typeLoader->m_thread->isThisThread()) qFatal("QQmlDataBlob: An API call was made outside a callback"); } while (false)
+#else
+#define ASSERT_CALLBACK()
+#endif
+
+DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS);
+
+QT_BEGIN_NAMESPACE
+
+/*!
+\class QQmlDataBlob
+\brief The QQmlDataBlob encapsulates a data request that can be issued to a QQmlTypeLoader.
+\internal
+
+QQmlDataBlob's are loaded by a QQmlTypeLoader. The user creates the QQmlDataBlob
+and then calls QQmlTypeLoader::load() or QQmlTypeLoader::loadWithStaticData() to load it.
+The QQmlTypeLoader invokes callbacks on the QQmlDataBlob as data becomes available.
+*/
+
+/*!
+\enum QQmlDataBlob::Status
+
+This enum describes the status of the data blob.
+
+\list
+\li Null The blob has not yet been loaded by a QQmlTypeLoader
+\li Loading The blob is loading network data. The QQmlDataBlob::setData() callback has not yet been
+ invoked or has not yet returned.
+\li WaitingForDependencies The blob is waiting for dependencies to be done before continuing.
+ This status only occurs after the QQmlDataBlob::setData() callback has been made, and when the
+ blob has outstanding dependencies.
+\li Complete The blob's data has been loaded and all dependencies are done.
+\li Error An error has been set on this blob.
+\endlist
+*/
+
+/*!
+\enum QQmlDataBlob::Type
+
+This enum describes the type of the data blob.
+
+\list
+\li QmlFile This is a QQmlTypeData
+\li JavaScriptFile This is a QQmlScriptData
+\li QmldirFile This is a QQmlQmldirData
+\endlist
+*/
+
+/*!
+Create a new QQmlDataBlob for \a url and of the provided \a type.
+*/
+QQmlDataBlob::QQmlDataBlob(const QUrl &url, Type type, QQmlTypeLoader *manager)
+: m_typeLoader(manager), m_type(type), m_url(url), m_finalUrl(url), m_redirectCount(0),
+ m_inCallback(false), m_isDone(false)
+{
+ //Set here because we need to get the engine from the manager
+ if (m_typeLoader->engine() && m_typeLoader->engine()->urlInterceptor())
+ m_url = m_typeLoader->engine()->urlInterceptor()->intercept(m_url,
+ (QQmlAbstractUrlInterceptor::DataType)m_type);
+}
+
+/*! \internal */
+QQmlDataBlob::~QQmlDataBlob()
+{
+ Q_ASSERT(m_waitingOnMe.isEmpty());
+
+ cancelAllWaitingFor();
+}
+
+/*!
+ Must be called before loading can occur.
+*/
+void QQmlDataBlob::startLoading()
+{
+ Q_ASSERT(status() == QQmlDataBlob::Null);
+ m_data.setStatus(QQmlDataBlob::Loading);
+}
+
+/*!
+Returns the type provided to the constructor.
+*/
+QQmlDataBlob::Type QQmlDataBlob::type() const
+{
+ return m_type;
+}
+
+/*!
+Returns the blob's status.
+*/
+QQmlDataBlob::Status QQmlDataBlob::status() const
+{
+ return m_data.status();
+}
+
+/*!
+Returns true if the status is Null.
+*/
+bool QQmlDataBlob::isNull() const
+{
+ return status() == Null;
+}
+
+/*!
+Returns true if the status is Loading.
+*/
+bool QQmlDataBlob::isLoading() const
+{
+ return status() == Loading;
+}
+
+/*!
+Returns true if the status is WaitingForDependencies.
+*/
+bool QQmlDataBlob::isWaiting() const
+{
+ return status() == WaitingForDependencies ||
+ status() == ResolvingDependencies;
+}
+
+/*!
+Returns true if the status is Complete.
+*/
+bool QQmlDataBlob::isComplete() const
+{
+ return status() == Complete;
+}
+
+/*!
+Returns true if the status is Error.
+*/
+bool QQmlDataBlob::isError() const
+{
+ return status() == Error;
+}
+
+/*!
+Returns true if the status is Complete or Error.
+*/
+bool QQmlDataBlob::isCompleteOrError() const
+{
+ Status s = status();
+ return s == Error || s == Complete;
+}
+
+/*!
+Returns the data download progress from 0 to 1.
+*/
+qreal QQmlDataBlob::progress() const
+{
+ quint8 p = m_data.progress();
+ if (p == 0xFF) return 1.;
+ else return qreal(p) / qreal(0xFF);
+}
+
+/*!
+Returns the physical url of the data. Initially this is the same as
+finalUrl(), but if a URL interceptor is set, it will work on this URL
+and leave finalUrl() alone.
+
+\sa finalUrl()
+*/
+QUrl QQmlDataBlob::url() const
+{
+ return m_url;
+}
+
+QString QQmlDataBlob::urlString() const
+{
+ if (m_urlString.isEmpty())
+ m_urlString = m_url.toString();
+
+ return m_urlString;
+}
+
+/*!
+Returns the logical URL to be used for resolving further URLs referred to in
+the code.
+
+This is the blob url passed to the constructor. If a URL interceptor rewrites
+the URL, this one stays the same. If a network redirect happens while fetching
+the data, this url is updated to reflect the new location. Therefore, if both
+an interception and a redirection happen, the final url will indirectly
+incorporate the result of the interception, potentially breaking further
+lookups.
+
+\sa url()
+*/
+QUrl QQmlDataBlob::finalUrl() const
+{
+ return m_finalUrl;
+}
+
+/*!
+Returns the finalUrl() as a string.
+*/
+QString QQmlDataBlob::finalUrlString() const
+{
+ if (m_finalUrlString.isEmpty())
+ m_finalUrlString = m_finalUrl.toString();
+
+ return m_finalUrlString;
+}
+
+/*!
+Return the errors on this blob.
+
+May only be called from the load thread, or after the blob isCompleteOrError().
+*/
+QList<QQmlError> QQmlDataBlob::errors() const
+{
+ Q_ASSERT(isCompleteOrError() || (m_typeLoader && m_typeLoader->m_thread->isThisThread()));
+ return m_errors;
+}
+
+/*!
+Mark this blob as having \a errors.
+
+All outstanding dependencies will be cancelled. Requests to add new dependencies
+will be ignored. Entry into the Error state is irreversable.
+
+The setError() method may only be called from within a QQmlDataBlob callback.
+*/
+void QQmlDataBlob::setError(const QQmlError &errors)
+{
+ ASSERT_CALLBACK();
+
+ QList<QQmlError> l;
+ l << errors;
+ setError(l);
+}
+
+/*!
+\overload
+*/
+void QQmlDataBlob::setError(const QList<QQmlError> &errors)
+{
+ ASSERT_CALLBACK();
+
+ Q_ASSERT(status() != Error);
+ Q_ASSERT(m_errors.isEmpty());
+
+ m_errors = errors; // Must be set before the m_data fence
+ m_data.setStatus(Error);
+
+ if (dumpErrors()) {
+ qWarning().nospace() << "Errors for " << urlString();
+ for (int ii = 0; ii < errors.count(); ++ii)
+ qWarning().nospace() << " " << qPrintable(errors.at(ii).toString());
+ }
+ cancelAllWaitingFor();
+
+ if (!m_inCallback)
+ tryDone();
+}
+
+void QQmlDataBlob::setError(const QQmlJS::DiagnosticMessage &error)
+{
+ QQmlError e;
+ e.setColumn(error.column);
+ e.setLine(error.line);
+ e.setDescription(error.message);
+ e.setUrl(url());
+ setError(e);
+}
+
+void QQmlDataBlob::setError(const QVector<QQmlJS::DiagnosticMessage> &errors)
+{
+ QList<QQmlError> finalErrors;
+ finalErrors.reserve(errors.count());
+ for (const auto &error : errors) {
+ QQmlError e;
+ e.setColumn(error.column);
+ e.setLine(error.line);
+ e.setDescription(error.message);
+ e.setUrl(url());
+ finalErrors << e;
+ }
+ setError(finalErrors);
+}
+
+void QQmlDataBlob::setError(const QString &description)
+{
+ QQmlError e;
+ e.setDescription(description);
+ e.setUrl(url());
+ setError(e);
+}
+
+/*!
+Wait for \a blob to become complete or to error. If \a blob is already
+complete or in error, or this blob is already complete, this has no effect.
+
+The setError() method may only be called from within a QQmlDataBlob callback.
+*/
+void QQmlDataBlob::addDependency(QQmlDataBlob *blob)
+{
+ ASSERT_CALLBACK();
+
+ Q_ASSERT(status() != Null);
+
+ if (!blob ||
+ blob->status() == Error || blob->status() == Complete ||
+ status() == Error || status() == Complete || m_isDone)
+ return;
+
+ for (const auto &existingDep: qAsConst(m_waitingFor))
+ if (existingDep.data() == blob)
+ return;
+
+ m_data.setStatus(WaitingForDependencies);
+
+ m_waitingFor.append(blob);
+ blob->m_waitingOnMe.append(this);
+}
+
+/*!
+\fn void QQmlDataBlob::dataReceived(const Data &data)
+
+Invoked when data for the blob is received. Implementors should use this callback
+to determine a blob's dependencies. Within this callback you may call setError()
+or addDependency().
+*/
+
+/*!
+Invoked once data has either been received or a network error occurred, and all
+dependencies are complete.
+
+You can set an error in this method, but you cannot add new dependencies. Implementors
+should use this callback to finalize processing of data.
+
+The default implementation does nothing.
+
+XXX Rename processData() or some such to avoid confusion between done() (processing thread)
+and completed() (main thread)
+*/
+void QQmlDataBlob::done()
+{
+}
+
+#if QT_CONFIG(qml_network)
+/*!
+Invoked if there is a network error while fetching this blob.
+
+The default implementation sets an appropriate QQmlError.
+*/
+void QQmlDataBlob::networkError(QNetworkReply::NetworkError networkError)
+{
+ Q_UNUSED(networkError);
+
+ QQmlError error;
+ error.setUrl(m_url);
+
+ const char *errorString = nullptr;
+ switch (networkError) {
+ default:
+ errorString = "Network error";
+ break;
+ case QNetworkReply::ConnectionRefusedError:
+ errorString = "Connection refused";
+ break;
+ case QNetworkReply::RemoteHostClosedError:
+ errorString = "Remote host closed the connection";
+ break;
+ case QNetworkReply::HostNotFoundError:
+ errorString = "Host not found";
+ break;
+ case QNetworkReply::TimeoutError:
+ errorString = "Timeout";
+ break;
+ case QNetworkReply::ProxyConnectionRefusedError:
+ case QNetworkReply::ProxyConnectionClosedError:
+ case QNetworkReply::ProxyNotFoundError:
+ case QNetworkReply::ProxyTimeoutError:
+ case QNetworkReply::ProxyAuthenticationRequiredError:
+ case QNetworkReply::UnknownProxyError:
+ errorString = "Proxy error";
+ break;
+ case QNetworkReply::ContentAccessDenied:
+ errorString = "Access denied";
+ break;
+ case QNetworkReply::ContentNotFoundError:
+ errorString = "File not found";
+ break;
+ case QNetworkReply::AuthenticationRequiredError:
+ errorString = "Authentication required";
+ break;
+ };
+
+ error.setDescription(QLatin1String(errorString));
+
+ setError(error);
+}
+#endif // qml_network
+
+/*!
+Called if \a blob, which was previously waited for, has an error.
+
+The default implementation does nothing.
+*/
+void QQmlDataBlob::dependencyError(QQmlDataBlob *blob)
+{
+ Q_UNUSED(blob);
+}
+
+/*!
+Called if \a blob, which was previously waited for, has completed.
+
+The default implementation does nothing.
+*/
+void QQmlDataBlob::dependencyComplete(QQmlDataBlob *blob)
+{
+ Q_UNUSED(blob);
+}
+
+/*!
+Called when all blobs waited for have completed. This occurs regardless of
+whether they are in error, or complete state.
+
+The default implementation does nothing.
+*/
+void QQmlDataBlob::allDependenciesDone()
+{
+ m_data.setStatus(QQmlDataBlob::ResolvingDependencies);
+}
+
+/*!
+Called when the download progress of this blob changes. \a progress goes
+from 0 to 1.
+
+This callback is only invoked if an asynchronous load for this blob is
+made. An asynchronous load is one in which the Asynchronous mode is
+specified explicitly, or one that is implicitly delayed due to a network
+operation.
+
+The default implementation does nothing.
+*/
+void QQmlDataBlob::downloadProgressChanged(qreal progress)
+{
+ Q_UNUSED(progress);
+}
+
+/*!
+Invoked on the main thread sometime after done() was called on the load thread.
+
+You cannot modify the blobs state at all in this callback and cannot depend on the
+order or timeliness of these callbacks. Implementors should use this callback to notify
+dependencies on the main thread that the blob is done and not a lot else.
+
+This callback is only invoked if an asynchronous load for this blob is
+made. An asynchronous load is one in which the Asynchronous mode is
+specified explicitly, or one that is implicitly delayed due to a network
+operation.
+
+The default implementation does nothing.
+*/
+void QQmlDataBlob::completed()
+{
+}
+
+
+void QQmlDataBlob::tryDone()
+{
+ if (status() != Loading && m_waitingFor.isEmpty() && !m_isDone) {
+ m_isDone = true;
+ addref();
+
+#ifdef DATABLOB_DEBUG
+ qWarning("QQmlDataBlob::done() %s", qPrintable(urlString()));
+#endif
+ done();
+
+ if (status() != Error)
+ m_data.setStatus(Complete);
+
+ notifyAllWaitingOnMe();
+
+ // Locking is not required here, as anyone expecting callbacks must
+ // already be protected against the blob being completed (as set above);
+#ifdef DATABLOB_DEBUG
+ qWarning("QQmlDataBlob: Dispatching completed");
+#endif
+ m_typeLoader->m_thread->callCompleted(this);
+
+ release();
+ }
+}
+
+void QQmlDataBlob::cancelAllWaitingFor()
+{
+ while (m_waitingFor.count()) {
+ QQmlRefPointer<QQmlDataBlob> blob = m_waitingFor.takeLast();
+
+ Q_ASSERT(blob->m_waitingOnMe.contains(this));
+
+ blob->m_waitingOnMe.removeOne(this);
+ }
+}
+
+void QQmlDataBlob::notifyAllWaitingOnMe()
+{
+ while (m_waitingOnMe.count()) {
+ QQmlDataBlob *blob = m_waitingOnMe.takeLast();
+
+ Q_ASSERT(std::any_of(blob->m_waitingFor.constBegin(), blob->m_waitingFor.constEnd(),
+ [this](const QQmlRefPointer<QQmlDataBlob> &waiting) { return waiting.data() == this; }));
+
+ blob->notifyComplete(this);
+ }
+}
+
+void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob)
+{
+ Q_ASSERT(blob->status() == Error || blob->status() == Complete);
+ QQmlCompilingProfiler prof(typeLoader()->profiler(), blob);
+
+ m_inCallback = true;
+
+ QQmlRefPointer<QQmlDataBlob> blobRef;
+ for (int i = 0; i < m_waitingFor.count(); ++i) {
+ if (m_waitingFor.at(i).data() == blob) {
+ blobRef = m_waitingFor.takeAt(i);
+ break;
+ }
+ }
+ Q_ASSERT(blobRef);
+
+ if (blob->status() == Error) {
+ dependencyError(blob);
+ } else if (blob->status() == Complete) {
+ dependencyComplete(blob);
+ }
+
+ if (!isError() && m_waitingFor.isEmpty())
+ allDependenciesDone();
+
+ m_inCallback = false;
+
+ tryDone();
+}
+
+QString QQmlDataBlob::SourceCodeData::readAll(QString *error) const
+{
+ error->clear();
+ if (hasInlineSourceCode)
+ return inlineSourceCode;
+
+ QFile f(fileInfo.absoluteFilePath());
+ if (!f.open(QIODevice::ReadOnly)) {
+ *error = f.errorString();
+ return QString();
+ }
+
+ const qint64 fileSize = fileInfo.size();
+
+ if (uchar *mappedData = f.map(0, fileSize)) {
+ QString source = QString::fromUtf8(reinterpret_cast<const char *>(mappedData), fileSize);
+ f.unmap(mappedData);
+ return source;
+ }
+
+ QByteArray data(fileSize, Qt::Uninitialized);
+ if (f.read(data.data(), data.length()) != data.length()) {
+ *error = f.errorString();
+ return QString();
+ }
+ return QString::fromUtf8(data);
+}
+
+QDateTime QQmlDataBlob::SourceCodeData::sourceTimeStamp() const
+{
+ if (hasInlineSourceCode)
+ return QDateTime();
+
+ return fileInfo.lastModified();
+}
+
+bool QQmlDataBlob::SourceCodeData::exists() const
+{
+ if (hasInlineSourceCode)
+ return true;
+ return fileInfo.exists();
+}
+
+bool QQmlDataBlob::SourceCodeData::isEmpty() const
+{
+ if (hasInlineSourceCode)
+ return inlineSourceCode.isEmpty();
+ return fileInfo.size() == 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmldatablob_p.h b/src/qml/qml/qqmldatablob_p.h
new file mode 100644
index 0000000000..0450e94c02
--- /dev/null
+++ b/src/qml/qml/qqmldatablob_p.h
@@ -0,0 +1,259 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLDATABLOB_P_H
+#define QQMLDATABLOB_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/qqmlrefcount_p.h>
+#include <private/qqmljsdiagnosticmessage_p.h>
+#include <private/qv4compileddata_p.h>
+
+#if QT_CONFIG(qml_network)
+#include <QtNetwork/qnetworkreply.h>
+#endif
+
+#include <QtQml/qqmlerror.h>
+#include <QtQml/qqmlabstracturlinterceptor.h>
+
+#include <QtCore/qdatetime.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qurl.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlTypeLoader;
+class Q_QML_PRIVATE_EXPORT QQmlDataBlob : public QQmlRefCount
+{
+public:
+ enum Status {
+ Null, // Prior to QQmlTypeLoader::load()
+ Loading, // Prior to data being received and dataReceived() being called
+ WaitingForDependencies, // While there are outstanding addDependency()s
+ ResolvingDependencies, // While resolving outstanding dependencies, to detect cycles
+ Complete, // Finished
+ Error // Error
+ };
+
+ enum Type { //Matched in QQmlAbstractUrlInterceptor
+ QmlFile = QQmlAbstractUrlInterceptor::QmlFile,
+ JavaScriptFile = QQmlAbstractUrlInterceptor::JavaScriptFile,
+ QmldirFile = QQmlAbstractUrlInterceptor::QmldirFile
+ };
+
+ QQmlDataBlob(const QUrl &, Type, QQmlTypeLoader* manager);
+ ~QQmlDataBlob() override;
+
+ void startLoading();
+
+ QQmlTypeLoader *typeLoader() const { return m_typeLoader; }
+
+ Type type() const;
+
+ Status status() const;
+ bool isNull() const;
+ bool isLoading() const;
+ bool isWaiting() const;
+ bool isComplete() const;
+ bool isError() const;
+ bool isCompleteOrError() const;
+
+ qreal progress() const;
+
+ QUrl url() const;
+ QString urlString() const;
+ QUrl finalUrl() const;
+ QString finalUrlString() const;
+
+ QList<QQmlError> errors() const;
+
+ class SourceCodeData {
+ public:
+ QString readAll(QString *error) const;
+ QDateTime sourceTimeStamp() const;
+ bool exists() const;
+ bool isEmpty() const;
+ private:
+ friend class QQmlDataBlob;
+ friend class QQmlTypeLoader;
+ QString inlineSourceCode;
+ QFileInfo fileInfo;
+ bool hasInlineSourceCode = false;
+ };
+
+protected:
+ // Can be called from within callbacks
+ void setError(const QQmlError &);
+ void setError(const QList<QQmlError> &errors);
+ void setError(const QQmlJS::DiagnosticMessage &error);
+ void setError(const QVector<QQmlJS::DiagnosticMessage> &errors);
+ void setError(const QString &description);
+ void addDependency(QQmlDataBlob *);
+
+ // Callbacks made in load thread
+ virtual void dataReceived(const SourceCodeData &) = 0;
+ virtual void initializeFromCachedUnit(const QV4::CompiledData::Unit*) = 0;
+ virtual void done();
+#if QT_CONFIG(qml_network)
+ virtual void networkError(QNetworkReply::NetworkError);
+#endif
+ virtual void dependencyError(QQmlDataBlob *);
+ virtual void dependencyComplete(QQmlDataBlob *);
+ virtual void allDependenciesDone();
+
+ // Callbacks made in main thread
+ virtual void downloadProgressChanged(qreal);
+ virtual void completed();
+
+protected:
+ // Manager that is currently fetching data for me
+ QQmlTypeLoader *m_typeLoader;
+
+private:
+ friend class QQmlTypeLoader;
+ friend class QQmlTypeLoaderThread;
+
+ void tryDone();
+ void cancelAllWaitingFor();
+ void notifyAllWaitingOnMe();
+ void notifyComplete(QQmlDataBlob *);
+
+ struct ThreadData {
+ private:
+ enum {
+ StatusMask = 0x0000FFFF,
+ StatusShift = 0,
+ ProgressMask = 0x00FF0000,
+ ProgressShift = 16,
+ AsyncMask = 0x80000000,
+ NoMask = 0
+ };
+
+ public:
+ inline ThreadData()
+ : _p(0)
+ {
+ }
+
+ inline QQmlDataBlob::Status status() const
+ {
+ return QQmlDataBlob::Status((_p.loadRelaxed() & StatusMask) >> StatusShift);
+ }
+
+ inline void setStatus(QQmlDataBlob::Status status)
+ {
+ while (true) {
+ int d = _p.loadRelaxed();
+ int nd = (d & ~StatusMask) | ((status << StatusShift) & StatusMask);
+ if (d == nd || _p.testAndSetOrdered(d, nd)) return;
+ }
+ }
+
+ inline bool isAsync() const
+ {
+ return _p.loadRelaxed() & AsyncMask;
+ }
+
+ inline void setIsAsync(bool v)
+ {
+ while (true) {
+ int d = _p.loadRelaxed();
+ int nd = (d & ~AsyncMask) | (v ? AsyncMask : NoMask);
+ if (d == nd || _p.testAndSetOrdered(d, nd)) return;
+ }
+ }
+
+ inline quint8 progress() const
+ {
+ return quint8((_p.loadRelaxed() & ProgressMask) >> ProgressShift);
+ }
+
+ inline void setProgress(quint8 v)
+ {
+ while (true) {
+ int d = _p.loadRelaxed();
+ int nd = (d & ~ProgressMask) | ((v << ProgressShift) & ProgressMask);
+ if (d == nd || _p.testAndSetOrdered(d, nd)) return;
+ }
+ }
+
+ private:
+ QAtomicInt _p;
+ };
+ ThreadData m_data;
+
+ // m_errors should *always* be written before the status is set to Error.
+ // We use the status change as a memory fence around m_errors so that locking
+ // isn't required. Once the status is set to Error (or Complete), m_errors
+ // cannot be changed.
+ QList<QQmlError> m_errors;
+
+ Type m_type;
+
+ QUrl m_url;
+ QUrl m_finalUrl;
+ mutable QString m_urlString;
+ mutable QString m_finalUrlString;
+
+ // List of QQmlDataBlob's that are waiting for me to complete.
+protected:
+ QList<QQmlDataBlob *> m_waitingOnMe;
+private:
+
+ // List of QQmlDataBlob's that I am waiting for to complete.
+ QVector<QQmlRefPointer<QQmlDataBlob>> m_waitingFor;
+
+ int m_redirectCount:30;
+ bool m_inCallback:1;
+ bool m_isDone:1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLDATABLOB_P_H
diff --git a/src/qml/qml/qqmldelayedcallqueue.cpp b/src/qml/qml/qqmldelayedcallqueue.cpp
index 61cb0a9065..02fde97b3d 100644
--- a/src/qml/qml/qqmldelayedcallqueue.cpp
+++ b/src/qml/qml/qqmldelayedcallqueue.cpp
@@ -38,12 +38,12 @@
****************************************************************************/
#include "qqmldelayedcallqueue_p.h"
-#include <private/qv8engine_p.h>
#include <private/qqmlengine_p.h>
#include <private/qqmljavascriptexpression_p.h>
#include <private/qv4value_p.h>
#include <private/qv4jscall_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4qmlcontext_p.h>
#include <QQmlError>
diff --git a/src/qml/qml/qqmldirdata.cpp b/src/qml/qml/qqmldirdata.cpp
new file mode 100644
index 0000000000..de74dfdf9b
--- /dev/null
+++ b/src/qml/qml/qqmldirdata.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qqmldirdata_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlQmldirData::QQmlQmldirData(const QUrl &url, QQmlTypeLoader *loader)
+ : QQmlTypeLoader::Blob(url, QmldirFile, loader)
+{
+}
+
+const QString &QQmlQmldirData::content() const
+{
+ return m_content;
+}
+
+QQmlTypeLoader::Blob::PendingImportPtr QQmlQmldirData::import(QQmlTypeLoader::Blob *blob) const
+{
+ auto it = m_imports.find(blob);
+ if (it == m_imports.end())
+ return nullptr;
+ return *it;
+}
+
+void QQmlQmldirData::setImport(QQmlTypeLoader::Blob *blob, QQmlTypeLoader::Blob::PendingImportPtr import)
+{
+ m_imports[blob] = std::move(import);
+}
+
+int QQmlQmldirData::priority(QQmlTypeLoader::Blob *blob) const
+{
+ QHash<QQmlTypeLoader::Blob *, int>::const_iterator it = m_priorities.find(blob);
+ if (it == m_priorities.end())
+ return 0;
+ return *it;
+}
+
+void QQmlQmldirData::setPriority(QQmlTypeLoader::Blob *blob, int priority)
+{
+ m_priorities[blob] = priority;
+}
+
+void QQmlQmldirData::dataReceived(const SourceCodeData &data)
+{
+ QString error;
+ m_content = data.readAll(&error);
+ if (!error.isEmpty()) {
+ setError(error);
+ return;
+ }
+}
+
+void QQmlQmldirData::initializeFromCachedUnit(const QV4::CompiledData::Unit *)
+{
+ Q_UNIMPLEMENTED();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmldirdata_p.h b/src/qml/qml/qqmldirdata_p.h
new file mode 100644
index 0000000000..34f1ff1678
--- /dev/null
+++ b/src/qml/qml/qqmldirdata_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLDIRDATA_P_H
+#define QQMLDIRDATA_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/qqmltypeloader_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQmlQmldirData : public QQmlTypeLoader::Blob
+{
+private:
+ friend class QQmlTypeLoader;
+
+ QQmlQmldirData(const QUrl &, QQmlTypeLoader *);
+
+public:
+ const QString &content() const;
+
+ PendingImportPtr import(QQmlTypeLoader::Blob *) const;
+ void setImport(QQmlTypeLoader::Blob *, PendingImportPtr);
+
+ int priority(QQmlTypeLoader::Blob *) const;
+ void setPriority(QQmlTypeLoader::Blob *, int);
+
+protected:
+ void dataReceived(const SourceCodeData &) override;
+ void initializeFromCachedUnit(const QV4::CompiledData::Unit *) override;
+
+private:
+ QString m_content;
+ QHash<QQmlTypeLoader::Blob *, QQmlTypeLoader::Blob::PendingImportPtr> m_imports;
+ QHash<QQmlTypeLoader::Blob *, int> m_priorities;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLDIRDATA_P_H
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 4c6a1b69d8..0fd07ea209 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -51,14 +51,15 @@
#include "qqmlscriptstring.h"
#include "qqmlglobal_p.h"
#include "qqmlcomponent_p.h"
-#include "qqmldirparser_p.h"
#include "qqmlextensioninterface.h"
#include "qqmllist_p.h"
#include "qqmltypenamecache_p.h"
#include "qqmlnotifier_p.h"
#include "qqmlincubator.h"
#include "qqmlabstracturlinterceptor.h"
+#include <private/qqmldirparser_p.h>
#include <private/qqmlboundsignal_p.h>
+#include <private/qqmljsdiagnosticmessage_p.h>
#include <QtCore/qstandardpaths.h>
#include <QtCore/qsettings.h>
#include <QtCore/qmetaobject.h>
@@ -86,19 +87,7 @@
#if QT_CONFIG(qml_animation)
#include <private/qqmltimer_p.h>
#endif
-#if QT_CONFIG(qml_list_model)
-#include <private/qqmllistmodel_p.h>
-#endif
#include <private/qqmlplatform_p.h>
-#include <private/qquickpackage_p.h>
-#if QT_CONFIG(qml_delegate_model)
-#include <private/qqmldelegatemodel_p.h>
-#endif
-#include <private/qqmlobjectmodel_p.h>
-#if QT_CONFIG(qml_worker_script)
-#include <private/qquickworkerscript_p.h>
-#endif
-#include <private/qqmlinstantiator_p.h>
#include <private/qqmlloggingcategory_p.h>
#ifdef Q_OS_WIN // for %APPDATA%
@@ -116,13 +105,6 @@ Q_DECLARE_METATYPE(QQmlProperty)
QT_BEGIN_NAMESPACE
-void qmlRegisterBaseTypes(const char *uri, int versionMajor, int versionMinor)
-{
- QQmlEnginePrivate::registerBaseTypes(uri, versionMajor, versionMinor);
- QQmlEnginePrivate::registerQtQuick2Types(uri, versionMajor, versionMinor);
- QQmlValueTypeFactory::registerValueTypes(uri, versionMajor, versionMinor);
-}
-
// Declared in qqml.h
int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
const char *uri, int versionMajor,
@@ -215,63 +197,56 @@ int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
bool QQmlEnginePrivate::qml_debugging_enabled = false;
bool QQmlEnginePrivate::s_designerMode = false;
-// these types are part of the QML language
-void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int versionMinor)
+void QQmlEnginePrivate::defineModule()
{
- qmlRegisterType<QQmlComponent>(uri,versionMajor,versionMinor,"Component");
- qmlRegisterType<QObject>(uri,versionMajor,versionMinor,"QtObject");
- qmlRegisterType<QQmlBind>(uri, versionMajor, versionMinor,"Binding");
- qmlRegisterType<QQmlBind,8>(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "Binding"); //Only available in >=2.8
- qmlRegisterCustomType<QQmlConnections>(uri, versionMajor, 0, "Connections", new QQmlConnectionsParser);
- if (!strcmp(uri, "QtQuick"))
- qmlRegisterCustomType<QQmlConnections,1>(uri, versionMajor, 7, "Connections", new QQmlConnectionsParser); //Only available in QtQuick >=2.7
- else
- qmlRegisterCustomType<QQmlConnections,1>(uri, versionMajor, 3, "Connections", new QQmlConnectionsParser); //Only available in QtQml >=2.3
-#if QT_CONFIG(qml_animation)
- qmlRegisterType<QQmlTimer>(uri, versionMajor, versionMinor,"Timer");
-#endif
- qmlRegisterType<QQmlInstantiator>(uri, versionMajor, (versionMinor < 1 ? 1 : versionMinor), "Instantiator"); //Only available in >=2.1
- qmlRegisterType<QQmlInstanceModel>();
+ const char uri[] = "QtQml";
- qmlRegisterType<QQmlLoggingCategory>(uri, versionMajor, 8, "LoggingCategory"); //Only available in >=2.8
- qmlRegisterType<QQmlLoggingCategory,1>(uri, versionMajor, 12, "LoggingCategory"); //Only available in >=2.12
-}
+ qmlRegisterType<QQmlComponent>(uri, 2, 0, "Component");
+ qmlRegisterType<QObject>(uri, 2, 0, "QtObject");
+ qmlRegisterType<QQmlBind>(uri, 2, 0, "Binding");
+ qmlRegisterType<QQmlBind, 8>(uri, 2, 8, "Binding"); // Only available in >= 2.8
+ qmlRegisterType<QQmlBind, 14>(uri, 2, 14, "Binding");
+ // TODO: We won't need Connections to be a custom type anymore once we can drop the
+ // automatic signal handler inference from undeclared properties.
+ qmlRegisterCustomType<QQmlConnections>(uri, 2, 0, "Connections", new QQmlConnectionsParser);
+ qmlRegisterCustomType<QQmlConnections, 3>(uri, 2, 3, "Connections", new QQmlConnectionsParser); // Only available in QtQml >= 2.3
-// These QtQuick types' implementation resides in the QtQml module
-void QQmlEnginePrivate::registerQtQuick2Types(const char *uri, int versionMajor, int versionMinor)
-{
-#if QT_CONFIG(qml_list_model)
- qmlRegisterType<QQmlListElement>(uri, versionMajor, versionMinor, "ListElement"); // Now in QtQml.Models, here for compatibility
- qmlRegisterCustomType<QQmlListModel>(uri, versionMajor, versionMinor, "ListModel", new QQmlListModelParser); // Now in QtQml.Models, here for compatibility
-#endif
-#if QT_CONFIG(qml_worker_script)
- qmlRegisterType<QQuickWorkerScript>(uri, versionMajor, versionMinor, "WorkerScript");
+#if QT_CONFIG(qml_animation)
+ qmlRegisterType<QQmlTimer>(uri, 2, 0, "Timer");
#endif
- qmlRegisterType<QQuickPackage>(uri, versionMajor, versionMinor, "Package");
-#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
-#if QT_CONFIG(qml_delegate_model)
- qmlRegisterType<QQmlDelegateModel>(uri, versionMajor, versionMinor, "VisualDataModel");
- qmlRegisterType<QQmlDelegateModelGroup>(uri, versionMajor, versionMinor, "VisualDataGroup");
+
+ qmlRegisterType<QQmlLoggingCategory>(uri, 2, 8, "LoggingCategory"); // Only available in >= 2.8
+ qmlRegisterType<QQmlLoggingCategory, 12>(uri, 2, 12, "LoggingCategory"); // Only available in >= 2.12
+
+#if QT_CONFIG(qml_locale)
+ qmlRegisterUncreatableType<QQmlLocale>(uri, 2, 2, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()"));
#endif
- qmlRegisterType<QQmlObjectModel>(uri, versionMajor, versionMinor, "VisualItemModel");
-#endif // < Qt 6
}
-void QQmlEnginePrivate::defineQtQuick2Module()
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+void QQmlEnginePrivate::registerQuickTypes()
{
- // register the base types into the QtQuick namespace
- registerBaseTypes("QtQuick",2,0);
+ // Don't add anything here. These are only for backwards compatibility.
- // register the QtQuick2 types which are implemented in the QtQml module.
- registerQtQuick2Types("QtQuick",2,0);
+ const char uri[] = "QtQuick";
+
+ qmlRegisterType<QQmlComponent>(uri, 2, 0, "Component");
+ qmlRegisterType<QObject>(uri, 2, 0, "QtObject");
+ qmlRegisterType<QQmlBind>(uri, 2, 0, "Binding");
+ qmlRegisterType<QQmlBind, 8>(uri, 2, 8, "Binding");
+ qmlRegisterCustomType<QQmlConnections>(uri, 2, 0, "Connections", new QQmlConnectionsParser);
+ qmlRegisterCustomType<QQmlConnections, 3>(uri, 2, 7, "Connections", new QQmlConnectionsParser);
+#if QT_CONFIG(qml_animation)
+ qmlRegisterType<QQmlTimer>(uri, 2, 0,"Timer");
+#endif
+ qmlRegisterType<QQmlLoggingCategory>(uri, 2, 8, "LoggingCategory");
+ qmlRegisterType<QQmlLoggingCategory, 12>(uri, 2, 12, "LoggingCategory");
#if QT_CONFIG(qml_locale)
- qmlRegisterUncreatableType<QQmlLocale>("QtQuick", 2, 0, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()"));
+ qmlRegisterUncreatableType<QQmlLocale>(uri, 2, 0, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()"));
#endif
-
- // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward
- qmlRegisterModule("QtQuick", 2, QT_VERSION_MINOR);
}
+#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
bool QQmlEnginePrivate::designerMode()
{
@@ -841,19 +816,20 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in
// marshalled back onto the QObject's thread and handled by QML from there. This is tested
// by the qqmlecmascript::threadSignal() autotest.
if (ddata->notifyList &&
- QThread::currentThreadId() != QObjectPrivate::get(object)->threadData->threadId.load()) {
+ QThread::currentThreadId() != QObjectPrivate::get(object)->threadData->threadId.loadRelaxed()) {
- if (!QObjectPrivate::get(object)->threadData->thread)
+ if (!QObjectPrivate::get(object)->threadData->thread.loadAcquire())
return;
QMetaMethod m = QMetaObjectPrivate::signal(object->metaObject(), index);
QList<QByteArray> parameterTypes = m.parameterTypes();
- int *types = (int *)malloc((parameterTypes.count() + 1) * sizeof(int));
- void **args = (void **) malloc((parameterTypes.count() + 1) *sizeof(void *));
+ QScopedPointer<QMetaCallEvent> ev(new QMetaCallEvent(m.methodIndex(), 0, nullptr,
+ object, index,
+ parameterTypes.count() + 1));
- types[0] = 0; // return type
- args[0] = nullptr; // return value
+ void **args = ev->args();
+ int *types = ev->types();
for (int ii = 0; ii < parameterTypes.count(); ++ii) {
const QByteArray &typeName = parameterTypes.at(ii);
@@ -866,21 +842,16 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in
qWarning("QObject::connect: Cannot queue arguments of type '%s'\n"
"(Make sure '%s' is registered using qRegisterMetaType().)",
typeName.constData(), typeName.constData());
- free(types);
- free(args);
return;
}
args[ii + 1] = QMetaType::create(types[ii + 1], a[ii + 1]);
}
- QMetaCallEvent *ev = new QMetaCallEvent(m.methodIndex(), 0, nullptr, object, index,
- parameterTypes.count() + 1, types, args);
-
QQmlThreadNotifierProxyObject *mpo = new QQmlThreadNotifierProxyObject;
mpo->target = object;
- mpo->moveToThread(QObjectPrivate::get(object)->threadData->thread);
- QCoreApplication::postEvent(mpo, ev);
+ mpo->moveToThread(QObjectPrivate::get(object)->threadData->thread.loadAcquire());
+ QCoreApplication::postEvent(mpo, ev.take());
} else {
QQmlNotifierEndpoint *ep = ddata->notify(index);
@@ -975,14 +946,10 @@ void QQmlEnginePrivate::init()
Q_Q(QQmlEngine);
if (baseModulesUninitialized) {
- qmlRegisterType<QQmlComponent>("QML", 1, 0, "Component"); // required for the Compiler.
- registerBaseTypes("QtQml", 2, 0); // import which provides language building blocks.
-#if QT_CONFIG(qml_locale)
- qmlRegisterUncreatableType<QQmlLocale>("QtQml", 2, 2, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()"));
-#endif
- // Auto-increment the import to stay in sync with ALL future QtQml minor versions from 5.11 onward
- qmlRegisterModule("QtQml", 2, QT_VERSION_MINOR);
+ // required for the Compiler.
+ qmlRegisterType<QObject>("QML", 1, 0, "QtObject");
+ qmlRegisterType<QQmlComponent>("QML", 1, 0, "Component");
QQmlData::init();
baseModulesUninitialized = false;
@@ -994,24 +961,13 @@ void QQmlEnginePrivate::init()
qRegisterMetaType<QQmlComponent::Status>();
qRegisterMetaType<QList<QObject*> >();
qRegisterMetaType<QList<int> >();
- qRegisterMetaType<QQmlV4Handle>();
qRegisterMetaType<QQmlBinding*>();
- v8engine()->setEngine(q);
+ q->handle()->setQmlEngine(q);
rootContext = new QQmlContext(q,true);
}
-#if QT_CONFIG(qml_worker_script)
-QQuickWorkerScriptEngine *QQmlEnginePrivate::getWorkerScriptEngine()
-{
- Q_Q(QQmlEngine);
- if (!workerScriptEngine)
- workerScriptEngine = new QQuickWorkerScriptEngine(q);
- return workerScriptEngine;
-}
-#endif
-
/*!
\class QQmlEngine
\since 5.0
@@ -1091,7 +1047,7 @@ QQmlEngine::~QQmlEngine()
// XXX TODO: performance -- store list of singleton types separately?
QList<QQmlType> singletonTypes = QQmlMetaType::qmlSingletonTypes();
for (const QQmlType &currType : singletonTypes)
- currType.singletonInstanceInfo()->destroy(this);
+ d->destroySingletonInstance(currType);
delete d->rootContext;
d->rootContext = nullptr;
@@ -1214,6 +1170,13 @@ void QQmlEnginePrivate::registerFinalizeCallback(QObject *obj, int index)
}
}
+QSharedPointer<QQmlImageProviderBase> QQmlEnginePrivate::imageProvider(const QString &providerId) const
+{
+ const QString providerIdLower = providerId.toLower();
+ QMutexLocker locker(&mutex);
+ return imageProviders.value(providerIdLower);
+}
+
#if QT_CONFIG(qml_network)
/*!
Sets the \a factory to use for creating QNetworkAccessManager(s).
@@ -1303,8 +1266,10 @@ QNetworkAccessManager *QQmlEngine::networkAccessManager() const
void QQmlEngine::addImageProvider(const QString &providerId, QQmlImageProviderBase *provider)
{
Q_D(QQmlEngine);
+ QString providerIdLower = providerId.toLower();
+ QSharedPointer<QQmlImageProviderBase> sp(provider);
QMutexLocker locker(&d->mutex);
- d->imageProviders.insert(providerId.toLower(), QSharedPointer<QQmlImageProviderBase>(provider));
+ d->imageProviders.insert(std::move(providerIdLower), std::move(sp));
}
/*!
@@ -1315,8 +1280,9 @@ void QQmlEngine::addImageProvider(const QString &providerId, QQmlImageProviderBa
QQmlImageProviderBase *QQmlEngine::imageProvider(const QString &providerId) const
{
Q_D(const QQmlEngine);
+ const QString providerIdLower = providerId.toLower();
QMutexLocker locker(&d->mutex);
- return d->imageProviders.value(providerId.toLower()).data();
+ return d->imageProviders.value(providerIdLower).data();
}
/*!
@@ -1327,8 +1293,9 @@ QQmlImageProviderBase *QQmlEngine::imageProvider(const QString &providerId) cons
void QQmlEngine::removeImageProvider(const QString &providerId)
{
Q_D(QQmlEngine);
+ const QString providerIdLower = providerId.toLower();
QMutexLocker locker(&d->mutex);
- d->imageProviders.take(providerId.toLower());
+ d->imageProviders.take(providerIdLower);
}
/*!
@@ -1436,23 +1403,13 @@ void QQmlEngine::setOutputWarningsToStandardError(bool enabled)
template<>
QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId)
{
+ Q_D(QQmlEngine);
QQmlType type = QQmlMetaType::qmlType(qmlTypeId, QQmlMetaType::TypeIdCategory::QmlType);
if (!type.isValid() || !type.isSingleton())
return QJSValue();
- QQmlType::SingletonInstanceInfo* info = type.singletonInstanceInfo();
- info->init(this);
-
- if (QObject* o = info->qobjectApi(this))
- return this->newQObject(o);
- else {
- QJSValue value = info->scriptApi(this);
- if (!value.isUndefined())
- return value;
- }
-
- return QJSValue();
+ return d->singletonInstance<QJSValue>(type);
}
/*!
@@ -1669,6 +1626,7 @@ static QObject *resolveAttachedProperties(QQmlAttachedPropertiesFunc pf, QQmlDat
return rv;
}
+#if QT_DEPRECATED_SINCE(5, 14)
QObject *qmlAttachedPropertiesObjectById(int id, const QObject *object, bool create)
{
QQmlData *data = QQmlData::get(object, create);
@@ -1696,6 +1654,7 @@ QObject *qmlAttachedPropertiesObject(int *idCache, const QObject *object,
return qmlAttachedPropertiesObjectById(*idCache, object, create);
}
+#endif
QQmlAttachedPropertiesFunc qmlAttachedPropertiesFunction(QObject *object,
const QMetaObject *attachedMetaObject)
@@ -1824,7 +1783,7 @@ void QQmlData::NotifyList::layout()
todo = nullptr;
}
-void QQmlData::deferData(int objectIndex, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlContextData *context)
+void QQmlData::deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlContextData *context)
{
QQmlData::DeferredData *deferData = new QQmlData::DeferredData;
deferData->deferredIdx = objectIndex;
@@ -1832,7 +1791,7 @@ void QQmlData::deferData(int objectIndex, const QQmlRefPointer<QV4::CompiledData
deferData->context = context;
const QV4::CompiledData::Object *compiledObject = compilationUnit->objectAt(objectIndex);
- const QV4::CompiledData::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(objectIndex);
+ const QV4::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(objectIndex);
const QV4::CompiledData::Binding *binding = compiledObject->bindingTable();
for (quint32 i = 0; i < compiledObject->nBindings; ++i, ++binding) {
@@ -2157,20 +2116,21 @@ void QQmlEnginePrivate::warning(QQmlEnginePrivate *engine, const QList<QQmlError
dumpwarning(error);
}
-QList<QQmlError> QQmlEnginePrivate::qmlErrorFromDiagnostics(const QString &fileName, const QList<DiagnosticMessage> &diagnosticMessages)
+QList<QQmlError> QQmlEnginePrivate::qmlErrorFromDiagnostics(
+ const QString &fileName, const QList<QQmlJS::DiagnosticMessage> &diagnosticMessages)
{
QList<QQmlError> errors;
- for (const DiagnosticMessage &m : diagnosticMessages) {
+ for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
if (m.isWarning()) {
- qWarning("%s:%d : %s", qPrintable(fileName), m.loc.startLine, qPrintable(m.message));
+ qWarning("%s:%d : %s", qPrintable(fileName), m.line, qPrintable(m.message));
continue;
}
QQmlError error;
error.setUrl(QUrl(fileName));
error.setDescription(m.message);
- error.setLine(m.loc.startLine);
- error.setColumn(m.loc.startColumn);
+ error.setLine(m.line);
+ error.setColumn(m.column);
errors << error;
}
return errors;
@@ -2298,6 +2258,7 @@ void QQmlEngine::setPluginPathList(const QStringList &paths)
d->importDatabase.setPluginPathList(paths);
}
+#if QT_CONFIG(library)
/*!
Imports the plugin named \a filePath with the \a uri provided.
Returns true if the plugin was successfully imported; otherwise returns false.
@@ -2311,6 +2272,7 @@ bool QQmlEngine::importPlugin(const QString &filePath, const QString &uri, QList
Q_D(QQmlEngine);
return d->importDatabase.importDynamicPlugin(filePath, uri, QString(), -1, errors);
}
+#endif
/*!
\property QQmlEngine::offlineStoragePath
@@ -2365,6 +2327,17 @@ QString QQmlEngine::offlineStorageDatabaseFilePath(const QString &databaseName)
return d->offlineStorageDatabaseDirectory() + QLatin1String(md5.result().toHex());
}
+// #### Qt 6: Remove this function, it exists only for binary compatibility.
+/*!
+ * \internal
+ */
+bool QQmlEngine::addNamedBundle(const QString &name, const QString &fileName)
+{
+ Q_UNUSED(name)
+ Q_UNUSED(fileName)
+ return false;
+}
+
QString QQmlEnginePrivate::offlineStorageDatabaseDirectory() const
{
Q_Q(const QQmlEngine);
@@ -2461,7 +2434,7 @@ QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t, int minorVe
}
}
-void QQmlEnginePrivate::registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit)
+void QQmlEnginePrivate::registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit)
{
compilationUnit->isRegisteredWithEngine = true;
@@ -2471,7 +2444,7 @@ void QQmlEnginePrivate::registerInternalCompositeType(QV4::CompiledData::Compila
m_compositeTypes.insert(compilationUnit->metaTypeId, compilationUnit);
}
-void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit)
+void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit)
{
compilationUnit->isRegisteredWithEngine = false;
@@ -2479,6 +2452,70 @@ void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::CompiledData::Compi
m_compositeTypes.remove(compilationUnit->metaTypeId);
}
+template<>
+QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
+{
+ Q_Q(QQmlEngine);
+
+ QJSValue value = singletonInstances.value(type);
+ if (!value.isUndefined()) {
+ return value;
+ }
+
+ QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo();
+ Q_ASSERT(siinfo != nullptr);
+
+ if (siinfo->scriptCallback) {
+ value = siinfo->scriptCallback(q, q);
+ if (value.isQObject()) {
+ QObject *o = value.toQObject();
+ // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj)
+ // should behave identically to QML singleton types.
+ q->setContextForObject(o, new QQmlContext(q->rootContext(), q));
+ }
+ singletonInstances.insert(type, value);
+
+ } else if (siinfo->qobjectCallback) {
+ QObject *o = siinfo->qobjectCallback(q, q);
+ if (!o) {
+ QQmlError error;
+ error.setMessageType(QtMsgType::QtCriticalMsg);
+ error.setDescription(QString::asprintf("qmlRegisterSingletonType(): \"%s\" is not available because the callback function returns a null pointer.",
+ qPrintable(QString::fromUtf8(type.typeName()))));
+ warning(error);
+ } else {
+ // if this object can use a property cache, create it now
+ QQmlData::ensurePropertyCache(q, o);
+ }
+ // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj)
+ // should behave identically to QML singleton types.
+ q->setContextForObject(o, new QQmlContext(q->rootContext(), q));
+ value = q->newQObject(o);
+ singletonInstances.insert(type, value);
+ } else if (!siinfo->url.isEmpty()) {
+ QQmlComponent component(q, siinfo->url, QQmlComponent::PreferSynchronous);
+ QObject *o = component.beginCreate(q->rootContext());
+ value = q->newQObject(o);
+ singletonInstances.insert(type, value);
+ component.completeCreate();
+ }
+
+ return value;
+}
+
+void QQmlEnginePrivate::destroySingletonInstance(const QQmlType &type)
+{
+ Q_ASSERT(type.isSingleton() || type.isCompositeSingleton());
+
+ QObject* o = singletonInstances.take(type).toQObject();
+ if (o) {
+ QQmlData *ddata = QQmlData::get(o, false);
+ if (type.singletonInstanceInfo()->url.isEmpty() && ddata && ddata->indestructible && ddata->explicitIndestructibleSet)
+ return;
+ delete o;
+ }
+}
+
bool QQmlEnginePrivate::isTypeLoaded(const QUrl &url) const
{
return typeLoader.isTypeLoaded(url);
diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h
index 871e6bd9b4..31fe3a1849 100644
--- a/src/qml/qml/qqmlengine.h
+++ b/src/qml/qml/qqmlengine.h
@@ -114,7 +114,9 @@ public:
bool addNamedBundle(const QString &name, const QString &fileName);
+#if QT_CONFIG(library)
bool importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors);
+#endif
#if QT_CONFIG(qml_network)
void setNetworkAccessManagerFactory(QQmlNetworkAccessManagerFactory *);
@@ -175,12 +177,7 @@ Q_QML_EXPORT QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId);
template<typename T>
T QQmlEngine::singletonInstance(int qmlTypeId) {
- QJSValue instance = singletonInstance<QJSValue>(qmlTypeId);
- if (!instance.isQObject())
- return nullptr;
-
- QObject *object = instance.toQObject();
- return qobject_cast<T>(object);
+ return qobject_cast<T>(singletonInstance<QJSValue>(qmlTypeId).toQObject());
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index 92e56cd957..385ae02ce5 100644
--- a/src/qml/qml/qqmlengine_p.h
+++ b/src/qml/qml/qqmlengine_p.h
@@ -62,11 +62,11 @@
#include "qqmlcontext_p.h"
#include "qqmlexpression.h"
#include "qqmlproperty_p.h"
-#include "qqmlpropertycache_p.h"
#include "qqmlmetatype_p.h"
#include <private/qintrusivelist_p.h>
#include <private/qrecyclepool_p.h>
#include <private/qfieldlist_p.h>
+#include <private/qv4engine_p.h>
#include <QtCore/qlist.h>
#include <QtCore/qpair.h>
@@ -77,7 +77,6 @@
#include <private/qobject_p.h>
-#include <private/qv8engine_p.h>
#include <private/qjsengine_p.h>
#include <private/qqmldirparser_p.h>
@@ -95,12 +94,12 @@ class QQmlTypeNameCache;
class QQmlComponentAttached;
class QQmlCleanup;
class QQmlDelayedError;
-class QQuickWorkerScriptEngine;
class QQmlObjectCreator;
class QDir;
class QQmlIncubator;
class QQmlProfiler;
class QQmlPropertyCapture;
+class QQmlMetaObject;
// This needs to be declared here so that the pool for it can live in QQmlEnginePrivate.
// The inline method definitions are in qqmljavascriptexpression_p.h
@@ -150,12 +149,10 @@ public:
QQmlDelayedError *erroredBindings;
int inProgressCreations;
- QV8Engine *v8engine() const { return q_func()->handle()->v8Engine; }
QV4::ExecutionEngine *v4engine() const { return q_func()->handle(); }
#if QT_CONFIG(qml_worker_script)
- QQuickWorkerScriptEngine *getWorkerScriptEngine();
- QQuickWorkerScriptEngine *workerScriptEngine;
+ QThread *workerScriptEngine;
#endif
QUrl baseUrl;
@@ -171,6 +168,8 @@ public:
mutable QQmlNetworkAccessManagerFactory *networkAccessManagerFactory;
#endif
QHash<QString,QSharedPointer<QQmlImageProviderBase> > imageProviders;
+ QSharedPointer<QQmlImageProviderBase> imageProvider(const QString &providerId) const;
+
QQmlAbstractUrlInterceptor* urlInterceptor;
@@ -223,12 +222,16 @@ public:
QQmlMetaObject metaObjectForType(int) const;
QQmlPropertyCache *propertyCacheForType(int);
QQmlPropertyCache *rawPropertyCacheForType(int, int minorVersion = -1);
- void registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit);
- void unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit);
+ void registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit);
+ void unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit);
bool isTypeLoaded(const QUrl &url) const;
bool isScriptLoaded(const QUrl &url) const;
+ template <typename T>
+ T singletonInstance(const QQmlType &type);
+ void destroySingletonInstance(const QQmlType &type);
+
void sendQuit();
void sendExit(int retCode = 0);
void warning(const QQmlError &);
@@ -238,7 +241,6 @@ public:
static void warning(QQmlEnginePrivate *, const QQmlError &);
static void warning(QQmlEnginePrivate *, const QList<QQmlError> &);
- inline static QV8Engine *getV8Engine(QQmlEngine *e);
inline static QV4::ExecutionEngine *getV4Engine(QQmlEngine *e);
inline static QQmlEnginePrivate *get(QQmlEngine *e);
inline static const QQmlEnginePrivate *get(const QQmlEngine *e);
@@ -249,9 +251,10 @@ public:
static QList<QQmlError> qmlErrorFromDiagnostics(const QString &fileName, const QList<QQmlJS::DiagnosticMessage> &diagnosticMessages);
- static void registerBaseTypes(const char *uri, int versionMajor, int versionMinor);
- static void registerQtQuick2Types(const char *uri, int versionMajor, int versionMinor);
- static void defineQtQuick2Module();
+ static void defineModule();
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ static void registerQuickTypes();
+#endif
static bool designerMode();
static void activateDesignerMode();
@@ -261,9 +264,11 @@ public:
mutable QMutex networkAccessManagerMutex;
private:
+ QHash<QQmlType, QJSValue> singletonInstances;
+
// These members must be protected by a QQmlEnginePrivate::Locker as they are required by
// the threaded loader. Only access them through their respective accessor methods.
- QHash<int, QV4::CompiledData::CompilationUnit *> m_compositeTypes;
+ QHash<int, QV4::ExecutableCompilationUnit *> m_compositeTypes;
static bool s_designerMode;
// These members is protected by the full QQmlEnginePrivate::mutex mutex
@@ -381,13 +386,6 @@ QQmlPropertyCache *QQmlEnginePrivate::cache(const QQmlType &type, int minorVersi
return QQmlMetaType::propertyCache(type, minorVersion);
}
-QV8Engine *QQmlEnginePrivate::getV8Engine(QQmlEngine *e)
-{
- Q_ASSERT(e);
-
- return e->handle()->v8Engine;
-}
-
QV4::ExecutionEngine *QQmlEnginePrivate::getV4Engine(QQmlEngine *e)
{
Q_ASSERT(e);
@@ -434,6 +432,14 @@ QQmlEnginePrivate *QQmlEnginePrivate::get(QV4::ExecutionEngine *e)
return get(qmlEngine);
}
+template<>
+Q_QML_PRIVATE_EXPORT QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type);
+
+template<typename T>
+T QQmlEnginePrivate::singletonInstance(const QQmlType &type) {
+ return qobject_cast<T>(singletonInstance<QJSValue>(type).toQObject());
+}
+
QT_END_NAMESPACE
#endif // QQMLENGINE_P_H
diff --git a/src/qml/qml/qqmlenumdata_p.h b/src/qml/qml/qqmlenumdata_p.h
new file mode 100644
index 0000000000..df99c2c1bc
--- /dev/null
+++ b/src/qml/qml/qqmlenumdata_p.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLENUMDATA_P_H
+#define QQMLENUMDATA_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/qqmlenumvalue_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QQmlEnumData
+{
+ QString name;
+ QVector<QQmlEnumValue> values;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLENUMDATA_P_H
diff --git a/src/qml/qml/qqmlenumvalue_p.h b/src/qml/qml/qqmlenumvalue_p.h
new file mode 100644
index 0000000000..ea0fc244cb
--- /dev/null
+++ b/src/qml/qml/qqmlenumvalue_p.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLENUMVALUE_P_H
+#define QQMLENUMVALUE_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/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QQmlEnumValue
+{
+ QQmlEnumValue() {}
+ QQmlEnumValue(const QString &n, int v) : namedValue(n), value(v) {}
+ QString namedValue;
+ int value = -1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLENUMVALUE_P_H
diff --git a/src/qml/qmldirparser/qqmlerror.cpp b/src/qml/qml/qqmlerror.cpp
index 5e181f7e27..0b94ed3b49 100644
--- a/src/qml/qmldirparser/qqmlerror.cpp
+++ b/src/qml/qml/qqmlerror.cpp
@@ -38,17 +38,17 @@
****************************************************************************/
#include "qqmlerror.h"
+#include "qqmlfile.h"
#include "qqmlsourcecoordinate_p.h"
+#include <private/qqmljsdiagnosticmessage_p.h>
#include <QtCore/qdebug.h>
#include <QtCore/qfile.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qvector.h>
-#ifndef QT_NO_QOBJECT
#include <QtCore/qobject.h>
#include <QtCore/qpointer.h>
-#endif
QT_BEGIN_NAMESPACE
@@ -77,26 +77,14 @@ QT_BEGIN_NAMESPACE
\sa QQuickView::errors(), QQmlComponent::errors()
*/
-class QQmlErrorPrivate
+class QQmlErrorPrivate : public QQmlJS::DiagnosticMessage
{
public:
- QQmlErrorPrivate();
-
+ QQmlErrorPrivate() { type = QtWarningMsg; }
QUrl url;
- QString description;
- quint16 line;
- quint16 column;
- QtMsgType messageType;
-#ifndef QT_NO_QOBJECT
QPointer<QObject> object;
-#endif
};
-QQmlErrorPrivate::QQmlErrorPrivate()
-: line(0), column(0), messageType(QtMsgType::QtWarningMsg)
-{
-}
-
/*!
Creates an empty error object.
*/
@@ -126,13 +114,11 @@ QQmlError &QQmlError::operator=(const QQmlError &other)
if (!d)
d = new QQmlErrorPrivate;
d->url = other.d->url;
- d->description = other.d->description;
+ d->message = other.d->message;
d->line = other.d->line;
d->column = other.d->column;
-#ifndef QT_NO_QOBJECT
d->object = other.d->object;
-#endif
- d->messageType = other.d->messageType;
+ d->type = other.d->type;
}
return *this;
}
@@ -179,7 +165,7 @@ void QQmlError::setUrl(const QUrl &url)
QString QQmlError::description() const
{
if (d)
- return d->description;
+ return d->message;
return QString();
}
@@ -190,7 +176,7 @@ void QQmlError::setDescription(const QString &description)
{
if (!d)
d = new QQmlErrorPrivate;
- d->description = description;
+ d->message = description;
}
/*!
@@ -199,7 +185,7 @@ void QQmlError::setDescription(const QString &description)
int QQmlError::line() const
{
if (d)
- return qmlSourceCoordinate(d->line);
+ return qmlConvertSourceCoordinate<quint32, int>(d->line);
return -1;
}
@@ -210,7 +196,7 @@ void QQmlError::setLine(int line)
{
if (!d)
d = new QQmlErrorPrivate;
- d->line = qmlSourceCoordinate(line);
+ d->line = qmlConvertSourceCoordinate<int, quint32>(line);
}
/*!
@@ -219,7 +205,7 @@ void QQmlError::setLine(int line)
int QQmlError::column() const
{
if (d)
- return qmlSourceCoordinate(d->column);
+ return qmlConvertSourceCoordinate<quint32, int>(d->column);
return -1;
}
@@ -230,10 +216,9 @@ void QQmlError::setColumn(int column)
{
if (!d)
d = new QQmlErrorPrivate;
- d->column = qmlSourceCoordinate(column);
+ d->column = qmlConvertSourceCoordinate<int, quint32>(column);
}
-#ifndef QT_NO_QOBJECT
/*!
Returns the nearest object where this error occurred.
Exceptions in bound property expressions set this to the object
@@ -256,7 +241,6 @@ void QQmlError::setObject(QObject *object)
d = new QQmlErrorPrivate;
d->object = object;
}
-#endif // QT_NO_QOBJECT
/*!
\since 5.9
@@ -266,7 +250,7 @@ void QQmlError::setObject(QObject *object)
QtMsgType QQmlError::messageType() const
{
if (d)
- return d->messageType;
+ return d->type;
return QtMsgType::QtWarningMsg;
}
@@ -280,7 +264,7 @@ void QQmlError::setMessageType(QtMsgType messageType)
{
if (!d)
d = new QQmlErrorPrivate;
- d->messageType = messageType;
+ d->type = messageType;
}
/*!
@@ -324,8 +308,8 @@ QDebug operator<<(QDebug debug, const QQmlError &error)
QUrl url = error.url();
- if (error.line() > 0 && url.scheme() == QLatin1String("file")) {
- QString file = url.toLocalFile();
+ if (error.line() > 0 && (url.scheme() == QLatin1String("file") || url.scheme() == QLatin1String("qrc"))) {
+ QString file = QQmlFile::urlToLocalFileOrQrc(url);
QFile f(file);
if (f.open(QIODevice::ReadOnly)) {
QByteArray data = f.readAll();
diff --git a/src/qml/qmldirparser/qqmlerror.h b/src/qml/qml/qqmlerror.h
index f4221358e9..8ea493c11d 100644
--- a/src/qml/qmldirparser/qqmlerror.h
+++ b/src/qml/qml/qqmlerror.h
@@ -69,10 +69,8 @@ public:
int column() const;
void setColumn(int);
-#ifndef QT_NO_QOBJECT
QObject *object() const;
void setObject(QObject *);
-#endif
QtMsgType messageType() const;
void setMessageType(QtMsgType messageType);
diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp
index ac2629979f..f6a5afb891 100644
--- a/src/qml/qml/qqmlexpression.cpp
+++ b/src/qml/qml/qqmlexpression.cpp
@@ -45,8 +45,8 @@
#include "qqmlcontext_p.h"
#include "qqmlscriptstring_p.h"
#include "qqmlbinding_p.h"
-#include <private/qv8engine_p.h>
#include <private/qqmlsourcecoordinate_p.h>
+#include <private/qv4qmlcontext_p.h>
#include <QtCore/qdebug.h>
@@ -352,7 +352,7 @@ QString QQmlExpression::sourceFile() const
int QQmlExpression::lineNumber() const
{
Q_D(const QQmlExpression);
- return qmlSourceCoordinate(d->line);
+ return qmlConvertSourceCoordinate<quint16, int>(d->line);
}
/*!
@@ -362,7 +362,7 @@ int QQmlExpression::lineNumber() const
int QQmlExpression::columnNumber() const
{
Q_D(const QQmlExpression);
- return qmlSourceCoordinate(d->column);
+ return qmlConvertSourceCoordinate<quint16, int>(d->column);
}
/*!
@@ -373,8 +373,8 @@ void QQmlExpression::setSourceLocation(const QString &url, int line, int column)
{
Q_D(QQmlExpression);
d->url = url;
- d->line = qmlSourceCoordinate(line);
- d->column = qmlSourceCoordinate(column);
+ d->line = qmlConvertSourceCoordinate<int, quint16>(line);
+ d->column = qmlConvertSourceCoordinate<int, quint16>(column);
}
/*!
diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp
index 1d60c518c4..acebb6bac3 100644
--- a/src/qml/qml/qqmlglobal.cpp
+++ b/src/qml/qml/qqmlglobal.cpp
@@ -149,7 +149,8 @@ QVariant QQmlValueTypeProvider::createVariantFromString(int type, const QString
return QVariant();
}
-QVariant QQmlValueTypeProvider::createVariantFromJsObject(int type, QQmlV4Handle obj, QV4::ExecutionEngine *e, bool *ok)
+QVariant QQmlValueTypeProvider::createVariantFromJsObject(int type, const QV4::Value &obj,
+ QV4::ExecutionEngine *e, bool *ok)
{
QVariant v;
@@ -225,63 +226,53 @@ bool QQmlValueTypeProvider::createFromString(int, const QString &, void *, size_
bool QQmlValueTypeProvider::createStringFrom(int, const void *, QString *) { return false; }
bool QQmlValueTypeProvider::variantFromString(const QString &, QVariant *) { return false; }
bool QQmlValueTypeProvider::variantFromString(int, const QString &, QVariant *) { return false; }
-bool QQmlValueTypeProvider::variantFromJsObject(int, QQmlV4Handle, QV4::ExecutionEngine *, QVariant *) { return false; }
+bool QQmlValueTypeProvider::variantFromJsObject(int, const QV4::Value &, QV4::ExecutionEngine *, QVariant *) { return false; }
bool QQmlValueTypeProvider::equal(int, const void *, const QVariant&) { return false; }
bool QQmlValueTypeProvider::store(int, const void *, void *, size_t) { return false; }
bool QQmlValueTypeProvider::read(const QVariant&, void *, int) { return false; }
bool QQmlValueTypeProvider::write(int, const void *, QVariant&) { return false; }
-Q_GLOBAL_STATIC(QQmlValueTypeProvider, nullValueTypeProvider)
-static QQmlValueTypeProvider *valueTypeProvider = nullptr;
+struct ValueTypeProviderList {
+ QQmlValueTypeProvider nullProvider;
+ QQmlValueTypeProvider *head = &nullProvider;
+};
-static QQmlValueTypeProvider **getValueTypeProvider(void)
-{
- if (valueTypeProvider == nullptr) {
- valueTypeProvider = nullValueTypeProvider;
- }
-
- return &valueTypeProvider;
-}
+Q_GLOBAL_STATIC(ValueTypeProviderList, valueTypeProviders)
Q_QML_PRIVATE_EXPORT void QQml_addValueTypeProvider(QQmlValueTypeProvider *newProvider)
{
- static QQmlValueTypeProvider **providerPtr = getValueTypeProvider();
- newProvider->next = *providerPtr;
- *providerPtr = newProvider;
+ if (ValueTypeProviderList *providers = valueTypeProviders()) {
+ newProvider->next = providers->head;
+ providers->head = newProvider;
+ }
}
Q_QML_PRIVATE_EXPORT void QQml_removeValueTypeProvider(QQmlValueTypeProvider *oldProvider)
{
- if (oldProvider == nullValueTypeProvider) {
- // don't remove the null provider
- // we get here when the QtQml library is being unloaded
- return;
- }
-
- // the only entry with next = 0 is the null provider
- Q_ASSERT(oldProvider->next);
+ if (ValueTypeProviderList *providers = valueTypeProviders()) {
+ QQmlValueTypeProvider *prev = providers->head;
+ if (prev == oldProvider) {
+ providers->head = oldProvider->next;
+ return;
+ }
- QQmlValueTypeProvider *prev = valueTypeProvider;
- if (prev == oldProvider) {
- valueTypeProvider = oldProvider->next;
- return;
- }
+ // singly-linked list removal
+ for (; prev; prev = prev->next) {
+ if (prev->next != oldProvider)
+ continue; // this is not the provider you're looking for
+ prev->next = oldProvider->next;
+ return;
+ }
- // singly-linked list removal
- for ( ; prev; prev = prev->next) {
- if (prev->next != oldProvider)
- continue; // this is not the provider you're looking for
- prev->next = oldProvider->next;
- return;
+ qWarning("QQml_removeValueTypeProvider: was asked to remove provider %p but it was not found", oldProvider);
}
-
- qWarning("QQml_removeValueTypeProvider: was asked to remove provider %p but it was not found", oldProvider);
}
-Q_AUTOTEST_EXPORT QQmlValueTypeProvider *QQml_valueTypeProvider(void)
+Q_AUTOTEST_EXPORT QQmlValueTypeProvider *QQml_valueTypeProvider()
{
- static QQmlValueTypeProvider **providerPtr = getValueTypeProvider();
- return *providerPtr;
+ if (ValueTypeProviderList *providers = valueTypeProviders())
+ return providers->head;
+ return nullptr;
}
QQmlColorProvider::~QQmlColorProvider() {}
diff --git a/src/qml/qml/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h
index 818537560c..3c540a6124 100644
--- a/src/qml/qml/qqmlglobal_p.h
+++ b/src/qml/qml/qqmlglobal_p.h
@@ -53,9 +53,8 @@
#include <private/qtqmlglobal_p.h>
#include <QtCore/QObject>
-#include <private/qqmlpropertycache_p.h>
+#include <private/qqmlmetaobject_p.h>
#include <private/qmetaobject_p.h>
-#include <private/qv8engine_p.h>
QT_BEGIN_NAMESPACE
@@ -91,7 +90,7 @@ QT_BEGIN_NAMESPACE
\endcode
*/
#define qmlobject_connect(Sender, SenderType, Signal, Receiver, ReceiverType, Method) \
-{ \
+do { \
SenderType *sender = (Sender); \
ReceiverType *receiver = (Receiver); \
const char *signal = (Signal); \
@@ -99,11 +98,11 @@ QT_BEGIN_NAMESPACE
static int signalIdx = -1; \
static int methodIdx = -1; \
if (signalIdx < 0) { \
- Q_ASSERT(((int)(*signal) - '0') == QSIGNAL_CODE); \
+ Q_ASSERT((int(*signal) - '0') == QSIGNAL_CODE); \
signalIdx = SenderType::staticMetaObject.indexOfSignal(signal+1); \
} \
if (methodIdx < 0) { \
- int code = ((int)(*method) - '0'); \
+ int code = (int(*method) - '0'); \
Q_ASSERT(code == QSLOT_CODE || code == QSIGNAL_CODE); \
if (code == QSLOT_CODE) \
methodIdx = ReceiverType::staticMetaObject.indexOfSlot(method+1); \
@@ -112,7 +111,7 @@ QT_BEGIN_NAMESPACE
} \
Q_ASSERT(signalIdx != -1 && methodIdx != -1); \
QMetaObject::connect(sender, signalIdx, receiver, methodIdx, Qt::DirectConnection); \
-}
+} while (0)
/*!
Disconnect \a Signal of \a Sender from \a Method of \a Receiver. \a Signal must be
@@ -130,7 +129,7 @@ QT_BEGIN_NAMESPACE
\endcode
*/
#define qmlobject_disconnect(Sender, SenderType, Signal, Receiver, ReceiverType, Method) \
-{ \
+do { \
SenderType *sender = (Sender); \
ReceiverType *receiver = (Receiver); \
const char *signal = (Signal); \
@@ -138,11 +137,11 @@ QT_BEGIN_NAMESPACE
static int signalIdx = -1; \
static int methodIdx = -1; \
if (signalIdx < 0) { \
- Q_ASSERT(((int)(*signal) - '0') == QSIGNAL_CODE); \
+ Q_ASSERT((int(*signal) - '0') == QSIGNAL_CODE); \
signalIdx = SenderType::staticMetaObject.indexOfSignal(signal+1); \
} \
if (methodIdx < 0) { \
- int code = ((int)(*method) - '0'); \
+ int code = (int(*method) - '0'); \
Q_ASSERT(code == QSLOT_CODE || code == QSIGNAL_CODE); \
if (code == QSLOT_CODE) \
methodIdx = ReceiverType::staticMetaObject.indexOfSlot(method+1); \
@@ -151,7 +150,7 @@ QT_BEGIN_NAMESPACE
} \
Q_ASSERT(signalIdx != -1 && methodIdx != -1); \
QMetaObject::disconnect(sender, signalIdx, receiver, methodIdx); \
-}
+} while (0)
/*!
This method is identical to qobject_cast<T>() except that it does not require lazy
@@ -233,7 +232,7 @@ public:
QVariant createVariantFromString(const QString &);
QVariant createVariantFromString(int, const QString &, bool *);
- QVariant createVariantFromJsObject(int, QQmlV4Handle, QV4::ExecutionEngine *, bool*);
+ QVariant createVariantFromJsObject(int, const QV4::Value &, QV4::ExecutionEngine *, bool *);
bool equalValueType(int, const void *, const QVariant&);
bool storeValueType(int, const void *, void *, size_t);
@@ -250,7 +249,7 @@ private:
virtual bool variantFromString(const QString &, QVariant *);
virtual bool variantFromString(int, const QString &, QVariant *);
- virtual bool variantFromJsObject(int, QQmlV4Handle, QV4::ExecutionEngine *, QVariant *);
+ virtual bool variantFromJsObject(int, const QV4::Value &, QV4::ExecutionEngine *, QVariant *);
virtual bool equal(int, const void *, const QVariant&);
virtual bool store(int, const void *, void *, size_t);
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index ba8dce4b6e..3bf8d807a9 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -54,6 +54,8 @@
#include <private/qqmltypenamecache_p.h>
#include <private/qqmlengine_p.h>
#include <private/qfieldlist_p.h>
+#include <private/qqmltypemodule_p.h>
+#include <private/qqmltypeloaderqmldircontent_p.h>
#include <QtCore/qjsonobject.h>
#include <QtCore/qjsonarray.h>
@@ -131,85 +133,6 @@ bool isPathAbsolute(const QString &path)
#endif
}
-/*
- \internal
-
- Fetches the QQmlType instance registered for \a urlString, creating a
- registration for it if it is not already registered, using the associated
- \a typeName, \a isCompositeSingleton, \a majorVersion and \a minorVersion
- details.
-
- Errors (if there are any) are placed into \a errors, if it is nonzero. Note
- that errors are treated as fatal if \a errors is not set.
-*/
-QQmlType fetchOrCreateTypeForUrl(const QString &urlString, const QHashedStringRef& typeName,
- bool isCompositeSingleton, QList<QQmlError> *errors,
- int majorVersion=-1, int minorVersion=-1)
-{
- QUrl url(urlString); // ### unfortunate (costly) conversion
- QQmlType ret = QQmlMetaType::qmlType(url);
- if (ret.isValid())
- return ret;
-
- int dot = typeName.indexOf(QLatin1Char('.'));
- QHashedStringRef unqualifiedtype = dot < 0 ? typeName : QHashedStringRef(typeName.constData() + dot + 1, typeName.length() - dot - 1);
-
- // We need a pointer, but we were passed a string. Take a copy so we
- // can guarentee it will live long enough to reach qmlregister.
- QByteArray buf(unqualifiedtype.toString().toUtf8());
-
- QQmlMetaTypeRegistrationFailureRecorder failureRecorder;
-
- // Register the type. Note that the URI parameters here are empty; for
- // file type imports, we do not place them in a URI as we don't
- // necessarily have a good and unique one (picture a library import,
- // which may be found in multiple plugin locations on disk), but there
- // are other reasons for this too.
- //
- // By not putting them in a URI, we prevent the types from being
- // registered on a QQmlTypeModule; this is important, as once types are
- // placed on there, they cannot be easily removed, meaning if the
- // developer subsequently loads a different import (meaning different
- // types) with the same URI (using, say, a different plugin path), it is
- // very undesirable that we continue to associate the types from the
- // "old" URI with that new module.
- //
- // Not having URIs also means that the types cannot be found by name
- // etc, the only way to look them up is through QQmlImports -- for
- // better or worse.
- if (isCompositeSingleton) {
- QQmlPrivate::RegisterCompositeSingletonType reg = {
- url,
- "", // uri
- majorVersion,
- minorVersion,
- buf.constData()
- };
- ret = QQmlMetaType::registerCompositeSingletonType(reg);
- } else {
- QQmlPrivate::RegisterCompositeType reg = {
- url,
- "", // uri
- majorVersion,
- minorVersion,
- buf.constData()
- };
- ret = QQmlMetaType::registerCompositeType(reg);
- }
-
- // This means that the type couldn't be found by URL, but could not be
- // registered either, meaning we most likely were passed some kind of bad
- // data.
- if (!ret.isValid()) {
- if (!errors) // Cannot list errors properly, just quit
- qFatal("%s", failureRecorder.failures().join('\n').toLatin1().constData());
- QQmlError error;
- error.setDescription(failureRecorder.failures().join('\n'));
- errors->prepend(error);
- }
- return ret;
-}
-
} // namespace
struct RegisteredPlugin {
@@ -219,6 +142,13 @@ struct RegisteredPlugin {
struct StringRegisteredPluginMap : public QMap<QString, RegisteredPlugin> {
QMutex mutex;
+
+ ~StringRegisteredPluginMap()
+ {
+ QMutexLocker lock(&mutex);
+ for (const RegisteredPlugin &plugin : qAsConst(*this))
+ delete plugin.loader;
+ }
};
Q_GLOBAL_STATIC(StringRegisteredPluginMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri and the PluginLoaders
@@ -831,9 +761,9 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
if (candidate != end) {
if (!base) // ensure we have a componentUrl
componentUrl = resolveLocalUrl(QString(url + candidate->typeName + dotqml_string), candidate->fileName);
- QQmlType returnType = fetchOrCreateTypeForUrl(componentUrl, type, isCompositeSingleton,
- nullptr, candidate->majorVersion,
- candidate->minorVersion);
+ QQmlType returnType = QQmlMetaType::typeForUrl(componentUrl, type, isCompositeSingleton,
+ nullptr, candidate->majorVersion,
+ candidate->minorVersion);
if (vmajor)
*vmajor = candidate->majorVersion;
if (vminor)
@@ -850,12 +780,12 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
typeStr + dotqml_string, // Type -> Type.qml
typeStr + dotuidotqml_string // Type -> Type.ui.qml
};
- for (uint i = 0; i < sizeof(urlsToTry) / sizeof(urlsToTry[0]); ++i) {
- exists = typeLoader->fileExists(localDirectoryPath, urlsToTry[i]);
+ for (const QString &urlToTry : urlsToTry) {
+ exists = typeLoader->fileExists(localDirectoryPath, urlToTry);
if (exists) {
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
// don't let function.qml confuse the use of "new Function(...)" for example.
- if (!QQml_isFileCaseCorrect(localDirectoryPath + urlsToTry[i])) {
+ if (!QQml_isFileCaseCorrect(localDirectoryPath + urlToTry)) {
exists = false;
if (errors) {
QQmlError caseError;
@@ -867,7 +797,7 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
#else
Q_UNUSED(errors);
#endif
- qmlUrl = url + urlsToTry[i];
+ qmlUrl = url + urlToTry;
break;
}
}
@@ -877,8 +807,8 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
if (typeRecursionDetected)
*typeRecursionDetected = true;
} else {
- QQmlType returnType = fetchOrCreateTypeForUrl(
- qmlUrl, type, registrationType == QQmlType::CompositeSingletonType, errors);
+ QQmlType returnType = QQmlMetaType::typeForUrl(
+ qmlUrl, type, registrationType == QQmlType::CompositeSingletonType, errors);
if (type_return)
*type_return = returnType;
return returnType.isValid();
@@ -926,7 +856,10 @@ bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor,
return true;
if (s->imports.count() == 1 && !s->imports.at(0)->isLibrary && type_return && s != &unqualifiedset) {
// qualified, and only 1 url
- *type_return = fetchOrCreateTypeForUrl(resolveLocalUrl(s->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml")), type, false, errors);
+ *type_return = QQmlMetaType::typeForUrl(
+ resolveLocalUrl(s->imports.at(0)->url,
+ unqualifiedtype.toString() + QLatin1String(".qml")),
+ type, false, errors);
return type_return->isValid();
}
}
@@ -1099,13 +1032,6 @@ bool QQmlImportsPrivate::populatePluginPairVector(QVector<StaticPluginPair> &res
return true;
}
-#if defined(QT_SHARED) || !QT_CONFIG(library)
-static inline QString msgCannotLoadPlugin(const QString &uri, const QString &why)
-{
- return QQmlImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri, why);
-}
-#endif
-
/*
Import an extension defined by a qmldir file.
@@ -1154,7 +1080,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath,
int dynamicPluginsFound = 0;
int staticPluginsFound = 0;
-#if defined(QT_SHARED)
+#if QT_CONFIG(library)
const auto qmldirPlugins = qmldir.plugins();
for (const QQmlDirParser::Plugin &plugin : qmldirPlugins) {
QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, plugin.path, plugin.name);
@@ -1165,9 +1091,11 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath,
// XXX TODO: should we leave the import plugin error alone?
// Here, we pop it off the top and coalesce it into this error's message.
// The reason is that the lower level may add url and line/column numbering information.
- QQmlError poppedError = errors->takeFirst();
QQmlError error;
- error.setDescription(msgCannotLoadPlugin(uri, poppedError.description()));
+ error.setDescription(
+ QQmlImportDatabase::tr(
+ "plugin cannot be loaded for module \"%1\": %2")
+ .arg(uri, errors->takeFirst().description()));
error.setUrl(QUrl::fromLocalFile(qmldirFilePath));
errors->prepend(error);
}
@@ -1175,7 +1103,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath,
}
}
}
-#endif // QT_SHARED
+#endif // QT_CONFIG(library)
if (dynamicPluginsFound < qmldirPluginCount) {
// Check if the missing plugins can be resolved statically. We do this by looking at
@@ -1823,6 +1751,16 @@ QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e)
addImportPath(QStringLiteral("qrc:/qt-project.org/imports"));
addImportPath(QCoreApplication::applicationDirPath());
+#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, QString::SkipEmptyParts);
+ for (int ii = paths.count() - 1; ii >= 0; --ii)
+ addPluginPath(paths.at(ii));
+ }
+#endif
}
QQmlImportDatabase::~QQmlImportDatabase()
@@ -1870,6 +1808,19 @@ QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
if (!resolvedPath.endsWith(Slash))
resolvedPath += Slash;
+#if defined(Q_OS_ANDROID)
+ if (qmldirPath.size() > 25 && qmldirPath.at(0) == QLatin1Char(':') && qmldirPath.at(1) == QLatin1Char('/') &&
+ qmldirPath.startsWith(QStringLiteral(":/android_rcc_bundle/qml/"), Qt::CaseInsensitive)) {
+ QString pluginName = qmldirPath.mid(21) + Slash + baseName;
+ pluginName.replace(QLatin1Char('/'), QLatin1Char('_'));
+ QString bundledPath = resolvedPath + QLatin1String("lib") + pluginName;
+ for (const QString &suffix : suffixes) {
+ const QString absolutePath = typeLoader->absoluteFilePath(bundledPath + suffix);
+ if (!absolutePath.isEmpty())
+ return absolutePath;
+ }
+ }
+#endif
resolvedPath += prefix + baseName;
for (const QString &suffix : suffixes) {
const QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + suffix);
@@ -1924,9 +1875,15 @@ QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
QLatin1String(".so"),
QLatin1String(".bundle")
};
-# else // Unix
+#else // Unix
static const QString prefix = QLatin1String("lib");
- static const QStringList suffixes = { QLatin1String(".so") };
+ static const QStringList suffixes = {
+# if defined(Q_OS_ANDROID)
+ QStringLiteral(LIBS_SUFFIX),
+# endif
+ QLatin1String(".so")
+
+ };
#endif
return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, suffixes, prefix);
@@ -2030,7 +1987,9 @@ void QQmlImportDatabase::setImportPathList(const QStringList &paths)
if (qmlImportTrace())
qDebug().nospace() << "QQmlImportDatabase::setImportPathList: " << paths;
- fileImportPath = paths;
+ fileImportPath.clear();
+ for (auto it = paths.crbegin(); it != paths.crend(); ++it)
+ addImportPath(*it);
// Our existing cached paths may have been invalidated
clearDirCache();
@@ -2044,77 +2003,7 @@ bool QQmlImportDatabase::registerPluginTypes(QObject *instance, const QString &b
{
if (qmlImportTrace())
qDebug().nospace() << "QQmlImportDatabase::registerPluginTypes: " << uri << " from " << basePath;
-
- QQmlTypesExtensionInterface *iface = qobject_cast<QQmlTypesExtensionInterface *>(instance);
- if (!iface) {
- if (errors) {
- QQmlError error;
- error.setDescription(tr("Module loaded for URI '%1' does not implement QQmlTypesExtensionInterface").arg(typeNamespace));
- errors->prepend(error);
- }
- return false;
- }
-
- const QByteArray bytes = uri.toUtf8();
- const char *moduleId = bytes.constData();
-
- QQmlMetaTypeRegistrationFailureRecorder failureRecorder;
- {
- // Create a scope for QWriteLocker to keep it as narrow as possible, and
- // to ensure that we release it before the call to initalizeEngine below
- QMutexLocker lock(QQmlMetaType::typeRegistrationLock());
-
- if (!typeNamespace.isEmpty()) {
- // This is an 'identified' module
- if (typeNamespace != uri) {
- // The namespace for type registrations must match the URI for locating the module
- if (errors) {
- QQmlError error;
- error.setDescription(tr("Module namespace '%1' does not match import URI '%2'").arg(typeNamespace).arg(uri));
- errors->prepend(error);
- }
- return false;
- }
-
- if (QQmlMetaType::namespaceContainsRegistrations(typeNamespace, vmaj)) {
- // Other modules have already installed to this namespace
- if (errors) {
- QQmlError error;
- error.setDescription(tr("Namespace '%1' has already been used for type registration").arg(typeNamespace));
- errors->prepend(error);
- }
- return false;
- } else {
- QQmlMetaType::protectNamespace(typeNamespace);
- }
- } else {
- // This is not an identified module - provide a warning
- qWarning().nospace() << qPrintable(tr("Module '%1' does not contain a module identifier directive - it cannot be protected from external registrations.").arg(uri));
- }
-
- QQmlMetaType::setTypeRegistrationNamespace(typeNamespace);
-
- if (QQmlExtensionPlugin *plugin = qobject_cast<QQmlExtensionPlugin *>(instance)) {
- // basepath should point to the directory of the module, not the plugin file itself:
- QQmlExtensionPluginPrivate::get(plugin)->baseUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath);
- }
-
- iface->registerTypes(moduleId);
- QQmlMetaType::setTypeRegistrationNamespace(QString());
- } // QWriteLocker lock(QQmlMetaType::typeRegistrationLock())
-
- if (!failureRecorder.failures().isEmpty()) {
- if (errors) {
- for (const QString &failure : failureRecorder.failures()) {
- QQmlError error;
- error.setDescription(failure);
- errors->prepend(error);
- }
- }
- return false;
- }
-
- return true;
+ return QQmlMetaType::registerPluginTypes(instance, basePath, uri, typeNamespace, vmaj, errors);
}
/*!
@@ -2169,13 +2058,13 @@ bool QQmlImportDatabase::importStaticPlugin(QObject *instance, const QString &ba
return true;
}
+#if QT_CONFIG(library)
/*!
\internal
*/
bool QQmlImportDatabase::importDynamicPlugin(const QString &filePath, const QString &uri,
const QString &typeNamespace, int vmaj, QList<QQmlError> *errors)
{
-#if QT_CONFIG(library)
QFileInfo fileInfo(filePath);
const QString absoluteFilePath = fileInfo.absoluteFilePath();
@@ -2253,20 +2142,14 @@ bool QQmlImportDatabase::importDynamicPlugin(const QString &filePath, const QStr
}
return true;
-#else
- Q_UNUSED(filePath);
- Q_UNUSED(uri);
- Q_UNUSED(typeNamespace);
- Q_UNUSED(vmaj);
- Q_UNUSED(errors);
- return false;
-#endif
}
+#endif // QT_CONFIG(library)
+
void QQmlImportDatabase::clearDirCache()
{
- QStringHash<QmldirCache *>::ConstIterator itr = qmldirCache.begin();
- while (itr != qmldirCache.end()) {
+ QStringHash<QmldirCache *>::ConstIterator itr = qmldirCache.constBegin();
+ while (itr != qmldirCache.constEnd()) {
QmldirCache *cache = *itr;
do {
QmldirCache *nextCache = cache->next;
diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h
index f8c01ed876..ea9c2eafb5 100644
--- a/src/qml/qml/qqmlimport_p.h
+++ b/src/qml/qml/qqmlimport_p.h
@@ -44,9 +44,10 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qset.h>
#include <QtCore/qstringlist.h>
+#include <QtQml/qqmlerror.h>
#include <private/qqmldirparser_p.h>
-#include <private/qqmlmetatype_p.h>
-#include <private/qhashedstring_p.h>
+#include <private/qqmltype_p.h>
+#include <private/qstringhash_p.h>
//
// W A R N I N G
@@ -211,7 +212,9 @@ public:
QQmlImportDatabase(QQmlEngine *);
~QQmlImportDatabase();
+#if QT_CONFIG(library)
bool importDynamicPlugin(const QString &filePath, const QString &uri, const QString &importNamespace, int vmaj, QList<QQmlError> *errors);
+#endif
QStringList importPathList(PathType type = LocalOrRemote) const;
void setImportPathList(const QStringList &paths);
diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp
index 39da550d63..bc06226cbf 100644
--- a/src/qml/qml/qqmlincubator.cpp
+++ b/src/qml/qml/qqmlincubator.cpp
@@ -42,7 +42,6 @@
#include "qqmlincubator_p.h"
#include "qqmlexpression_p.h"
-#include "qqmlmemoryprofiler_p.h"
#include "qqmlobjectcreator_p.h"
void QQmlEnginePrivate::incubate(QQmlIncubator &i, QQmlContextData *forContext)
@@ -272,8 +271,6 @@ void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i)
if (!compilationUnit)
return;
- QML_MEMORY_SCOPE_URL(compilationUnit->finalUrl());
-
QExplicitlySharedDataPointer<QQmlIncubatorPrivate> protectThis(this);
QRecursionWatcher<QQmlIncubatorPrivate, &QQmlIncubatorPrivate::recursion> watcher(this);
diff --git a/src/qml/qml/qqmlincubator_p.h b/src/qml/qml/qqmlincubator_p.h
index 676ba1a29a..57ec8249cb 100644
--- a/src/qml/qml/qqmlincubator_p.h
+++ b/src/qml/qml/qqmlincubator_p.h
@@ -87,7 +87,7 @@ public:
QPointer<QObject> result;
QQmlGuardedContextData rootContext;
QQmlEnginePrivate *enginePriv;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
QScopedPointer<QQmlObjectCreator> creator;
int subComponentToCreate;
QQmlVMEGuard vmeGuard;
diff --git a/src/qml/qml/qqmlinfo.cpp b/src/qml/qml/qqmlinfo.cpp
index 6322302422..2bfd2d5bb4 100644
--- a/src/qml/qml/qqmlinfo.cpp
+++ b/src/qml/qml/qqmlinfo.cpp
@@ -57,7 +57,7 @@ QT_BEGIN_NAMESPACE
/*!
\fn QQmlInfo QtQml::qmlDebug(const QObject *object)
- \relates QQmlEngine
+ \relates QtQml
\since 5.9
Prints debug messages that include the file and line number for the
@@ -91,7 +91,7 @@ QT_BEGIN_NAMESPACE
/*!
\fn QQmlInfo QtQml::qmlInfo(const QObject *object)
- \relates QQmlEngine
+ \relates QtQml
Prints informational messages that include the file and line number for the
specified QML \a object.
@@ -119,7 +119,7 @@ QT_BEGIN_NAMESPACE
/*!
\fn QQmlInfo QtQml::qmlWarning(const QObject *object)
- \relates QQmlEngine
+ \relates QtQml
\since 5.9
Prints warning messages that include the file and line number for the
diff --git a/src/qml/qml/qqmlinfo.h b/src/qml/qml/qqmlinfo.h
index 673125632e..faa112d4af 100644
--- a/src/qml/qml/qqmlinfo.h
+++ b/src/qml/qml/qqmlinfo.h
@@ -40,6 +40,7 @@
#ifndef QQMLINFO_H
#define QQMLINFO_H
+#include <QtQml/qtqmlglobal.h>
#include <QtCore/qdebug.h>
#include <QtCore/qurl.h>
#include <QtQml/qqmlerror.h>
diff --git a/src/qml/qml/qqmlirloader.cpp b/src/qml/qml/qqmlirloader.cpp
new file mode 100644
index 0000000000..82cad8eba8
--- /dev/null
+++ b/src/qml/qml/qqmlirloader.cpp
@@ -0,0 +1,206 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlirloader_p.h"
+#include <private/qqmlirbuilder_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlIRLoader::QQmlIRLoader(const QV4::CompiledData::Unit *qmlData, QmlIR::Document *output)
+ : unit(qmlData)
+ , output(output)
+{
+ pool = output->jsParserEngine.pool();
+}
+
+void QQmlIRLoader::load()
+{
+ output->jsGenerator.stringTable.initializeFromBackingUnit(unit);
+
+ const QV4::CompiledData::QmlUnit *qmlUnit = unit->qmlUnit();
+
+ for (quint32 i = 0; i < qmlUnit->nImports; ++i)
+ output->imports << qmlUnit->importAt(i);
+
+ if (unit->flags & QV4::CompiledData::Unit::IsSingleton) {
+ QmlIR::Pragma *p = New<QmlIR::Pragma>();
+ p->location = QV4::CompiledData::Location();
+ p->type = QmlIR::Pragma::PragmaSingleton;
+ output->pragmas << p;
+ }
+
+ for (uint i = 0; i < qmlUnit->nObjects; ++i) {
+ const QV4::CompiledData::Object *serializedObject = qmlUnit->objectAt(i);
+ QmlIR::Object *object = loadObject(serializedObject);
+ output->objects.append(object);
+ }
+}
+
+struct FakeExpression : public QQmlJS::AST::NullExpression
+{
+ FakeExpression(int start, int length)
+ : location(start, length)
+ {}
+
+ virtual QQmlJS::AST::SourceLocation firstSourceLocation() const
+ { return location; }
+
+ virtual QQmlJS::AST::SourceLocation lastSourceLocation() const
+ { return location; }
+
+private:
+ QQmlJS::AST::SourceLocation location;
+};
+
+QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *serializedObject)
+{
+ QmlIR::Object *object = pool->New<QmlIR::Object>();
+ object->init(pool, serializedObject->inheritedTypeNameIndex, serializedObject->idNameIndex);
+
+ object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias;
+ object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias;
+ object->flags = serializedObject->flags;
+ object->id = serializedObject->id;
+ object->location = serializedObject->location;
+ object->locationOfIdProperty = serializedObject->locationOfIdProperty;
+
+ QVector<int> functionIndices;
+ functionIndices.reserve(serializedObject->nFunctions + serializedObject->nBindings / 2);
+
+ for (uint i = 0; i < serializedObject->nBindings; ++i) {
+ QmlIR::Binding *b = pool->New<QmlIR::Binding>();
+ *static_cast<QV4::CompiledData::Binding*>(b) = serializedObject->bindingTable()[i];
+ object->bindings->append(b);
+ if (b->type == QV4::CompiledData::Binding::Type_Script) {
+ functionIndices.append(b->value.compiledScriptIndex);
+ b->value.compiledScriptIndex = functionIndices.count() - 1;
+
+ QmlIR::CompiledFunctionOrExpression *foe = pool->New<QmlIR::CompiledFunctionOrExpression>();
+ foe->nameIndex = 0;
+
+ QQmlJS::AST::ExpressionNode *expr;
+
+ if (b->stringIndex != quint32(0)) {
+ const int start = output->code.length();
+ const QString script = output->stringAt(b->stringIndex);
+ const int length = script.length();
+ output->code.append(script);
+ expr = new (pool) FakeExpression(start, length);
+ } else
+ expr = new (pool) QQmlJS::AST::NullExpression();
+ foe->node = new (pool) QQmlJS::AST::ExpressionStatement(expr); // dummy
+ object->functionsAndExpressions->append(foe);
+ }
+ }
+
+ Q_ASSERT(object->functionsAndExpressions->count == functionIndices.count());
+
+ for (uint i = 0; i < serializedObject->nSignals; ++i) {
+ const QV4::CompiledData::Signal *serializedSignal = serializedObject->signalAt(i);
+ QmlIR::Signal *s = pool->New<QmlIR::Signal>();
+ s->nameIndex = serializedSignal->nameIndex;
+ s->location = serializedSignal->location;
+ s->parameters = pool->New<QmlIR::PoolList<QmlIR::Parameter> >();
+
+ for (uint i = 0; i < serializedSignal->nParameters; ++i) {
+ QmlIR::Parameter *p = pool->New<QmlIR::Parameter>();
+ *static_cast<QV4::CompiledData::Parameter*>(p) = *serializedSignal->parameterAt(i);
+ s->parameters->append(p);
+ }
+
+ object->qmlSignals->append(s);
+ }
+
+ for (uint i = 0; i < serializedObject->nEnums; ++i) {
+ const QV4::CompiledData::Enum *serializedEnum = serializedObject->enumAt(i);
+ QmlIR::Enum *e = pool->New<QmlIR::Enum>();
+ e->nameIndex = serializedEnum->nameIndex;
+ e->location = serializedEnum->location;
+ e->enumValues = pool->New<QmlIR::PoolList<QmlIR::EnumValue> >();
+
+ for (uint i = 0; i < serializedEnum->nEnumValues; ++i) {
+ QmlIR::EnumValue *v = pool->New<QmlIR::EnumValue>();
+ *static_cast<QV4::CompiledData::EnumValue*>(v) = *serializedEnum->enumValueAt(i);
+ e->enumValues->append(v);
+ }
+
+ object->qmlEnums->append(e);
+ }
+
+ const QV4::CompiledData::Property *serializedProperty = serializedObject->propertyTable();
+ for (uint i = 0; i < serializedObject->nProperties; ++i, ++serializedProperty) {
+ QmlIR::Property *p = pool->New<QmlIR::Property>();
+ *static_cast<QV4::CompiledData::Property*>(p) = *serializedProperty;
+ object->properties->append(p);
+ }
+
+ {
+ const QV4::CompiledData::Alias *serializedAlias = serializedObject->aliasTable();
+ for (uint i = 0; i < serializedObject->nAliases; ++i, ++serializedAlias) {
+ QmlIR::Alias *a = pool->New<QmlIR::Alias>();
+ *static_cast<QV4::CompiledData::Alias*>(a) = *serializedAlias;
+ object->aliases->append(a);
+ }
+ }
+
+ const quint32_le *functionIdx = serializedObject->functionOffsetTable();
+ for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) {
+ QmlIR::Function *f = pool->New<QmlIR::Function>();
+ const QV4::CompiledData::Function *compiledFunction = unit->functionAt(*functionIdx);
+
+ functionIndices.append(*functionIdx);
+ f->index = functionIndices.count() - 1;
+ f->location = compiledFunction->location;
+ f->nameIndex = compiledFunction->nameIndex;
+ f->returnType = compiledFunction->returnType;
+
+ f->formals.allocate(pool, int(compiledFunction->nFormals));
+ const QV4::CompiledData::Parameter *formalNameIdx = compiledFunction->formalsTable();
+ for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx)
+ *static_cast<QV4::CompiledData::Parameter*>(&f->formals[i]) = *formalNameIdx;
+
+ object->functions->append(f);
+ }
+
+ object->runtimeFunctionIndices.allocate(pool, functionIndices);
+
+ return object;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlirloader_p.h b/src/qml/qml/qqmlirloader_p.h
new file mode 100644
index 0000000000..4812946ef0
--- /dev/null
+++ b/src/qml/qml/qqmlirloader_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLIRLOADER_P_H
+#define QQMLIRLOADER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtqmlglobal_p.h>
+#include <private/qv4compileddata_p.h>
+#include <private/qqmljsmemorypool_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QmlIR {
+struct Document;
+struct Object;
+}
+
+struct Q_QML_PRIVATE_EXPORT QQmlIRLoader {
+ QQmlIRLoader(const QV4::CompiledData::Unit *unit, QmlIR::Document *output);
+
+ void load();
+
+private:
+ QmlIR::Object *loadObject(const QV4::CompiledData::Object *serializedObject);
+
+ template <typename _Tp> _Tp *New() { return pool->New<_Tp>(); }
+
+ const QV4::CompiledData::Unit *unit;
+ QmlIR::Document *output;
+ QQmlJS::MemoryPool *pool;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLIRLOADER_P_H
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index 9a3a5218e0..8661ebcc13 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -208,7 +208,10 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b
}
Q_ASSERT(m_qmlScope.valueRef());
- QV4::ReturnedValue res = v4Function->call(&callData->thisObject, callData->args, callData->argc(), static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef()));
+ QV4::ReturnedValue res = v4Function->call(
+ &(callData->thisObject.asValue<QV4::Value>()),
+ callData->argValues<QV4::Value>(), callData->argc(),
+ static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef()));
QV4::Scope scope(v4);
QV4::ScopedValue result(scope, res);
@@ -392,10 +395,10 @@ void QQmlJavaScriptExpression::setupFunction(QV4::ExecutionContext *qmlContext,
return;
m_qmlScope.set(qmlContext->engine(), *qmlContext);
m_v4Function = f;
- setCompilationUnit(m_v4Function->compilationUnit);
+ setCompilationUnit(m_v4Function->executableCompilationUnit());
}
-void QQmlJavaScriptExpression::setCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
+void QQmlJavaScriptExpression::setCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit)
{
m_compilationUnit = compilationUnit;
}
diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h
index 453c8ab8a8..eecee08062 100644
--- a/src/qml/qml/qqmljavascriptexpression_p.h
+++ b/src/qml/qml/qqmljavascriptexpression_p.h
@@ -153,7 +153,7 @@ protected:
void createQmlBinding(QQmlContextData *ctxt, QObject *scope, const QString &code, const QString &filename, quint16 line);
void setupFunction(QV4::ExecutionContext *qmlContext, QV4::Function *f);
- void setCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
+ void setCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit);
// We store some flag bits in the following flag pointers.
// activeGuards:flag1 - notifyOnValueChanged
@@ -178,11 +178,11 @@ private:
QQmlJavaScriptExpression *m_nextExpression;
QV4::PersistentValue m_qmlScope;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> m_compilationUnit;
QV4::Function *m_v4Function;
};
-class QQmlPropertyCapture
+class Q_QML_PRIVATE_EXPORT QQmlPropertyCapture
{
public:
QQmlPropertyCapture(QQmlEngine *engine, QQmlJavaScriptExpression *e, QQmlJavaScriptExpression::DeleteWatcher *w)
diff --git a/src/qml/qml/qqmllist_p.h b/src/qml/qml/qqmllist_p.h
index 04bab8d929..e182ced51d 100644
--- a/src/qml/qml/qqmllist_p.h
+++ b/src/qml/qml/qqmllist_p.h
@@ -52,7 +52,7 @@
//
#include "qqmllist.h"
-#include "qqmlpropertycache_p.h"
+#include "qqmlmetaobject_p.h"
QT_BEGIN_NAMESPACE
diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp
index 2f769c1aef..5349572921 100644
--- a/src/qml/qml/qqmllistwrapper.cpp
+++ b/src/qml/qml/qqmllistwrapper.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qqmllistwrapper_p.h"
-#include <private/qv8engine_p.h>
#include <private/qqmllist_p.h>
#include <private/qv4objectproto_p.h>
#include <qv4objectiterator_p.h>
diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp
index 2b17037df0..dca13ac8d4 100644
--- a/src/qml/qml/qqmllocale.cpp
+++ b/src/qml/qml/qqmllocale.cpp
@@ -662,7 +662,7 @@ LOCALE_STRING_PROPERTY(exponential)
LOCALE_STRING_PROPERTY(amText)
LOCALE_STRING_PROPERTY(pmText)
-class QV4LocaleDataDeletable : public QV8Engine::Deletable
+class QV4LocaleDataDeletable : public QV4::ExecutionEngine::Deletable
{
public:
QV4LocaleDataDeletable(QV4::ExecutionEngine *engine);
diff --git a/src/qml/qml/qqmlloggingcategory_p.h b/src/qml/qml/qqmlloggingcategory_p.h
index ece06e04b4..ee5d9af2e7 100644
--- a/src/qml/qml/qqmlloggingcategory_p.h
+++ b/src/qml/qml/qqmlloggingcategory_p.h
@@ -65,7 +65,7 @@ class QQmlLoggingCategory : public QObject, public QQmlParserStatus
Q_INTERFACES(QQmlParserStatus)
Q_PROPERTY(QString name READ name WRITE setName)
- Q_PROPERTY(DefaultLogLevel defaultLogLevel READ defaultLogLevel WRITE setDefaultLogLevel REVISION 1)
+ Q_PROPERTY(DefaultLogLevel defaultLogLevel READ defaultLogLevel WRITE setDefaultLogLevel REVISION 12)
public:
enum DefaultLogLevel {
diff --git a/src/qml/qml/qqmlmetaobject.cpp b/src/qml/qml/qqmlmetaobject.cpp
new file mode 100644
index 0000000000..a967f46b12
--- /dev/null
+++ b/src/qml/qml/qqmlmetaobject.cpp
@@ -0,0 +1,327 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlmetaobject_p.h"
+
+#include <private/qqmlengine_p.h>
+#include <private/qqmlpropertycachemethodarguments_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct StaticQtMetaObject : public QObject
+{
+ static const QMetaObject *get()
+ { return &staticQtMetaObject; }
+};
+
+static bool isNamedEnumeratorInScope(const QMetaObject *resolvedMetaObject, const QByteArray &scope,
+ const QByteArray &name)
+{
+ for (int i = resolvedMetaObject->enumeratorCount() - 1; i >= 0; --i) {
+ QMetaEnum m = resolvedMetaObject->enumerator(i);
+ if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
+ return true;
+ }
+ return false;
+}
+
+static bool isNamedEnumerator(const QMetaObject *metaObj, const QByteArray &scopedName)
+{
+ QByteArray scope;
+ QByteArray name;
+ int scopeIdx = scopedName.lastIndexOf("::");
+ if (scopeIdx != -1) {
+ scope = scopedName.left(scopeIdx);
+ name = scopedName.mid(scopeIdx + 2);
+ } else {
+ name = scopedName;
+ }
+
+ if (scope == "Qt")
+ return isNamedEnumeratorInScope(StaticQtMetaObject::get(), scope, name);
+
+ if (isNamedEnumeratorInScope(metaObj, scope, name))
+ return true;
+
+ if (metaObj->d.relatedMetaObjects && !scope.isEmpty()) {
+ for (auto related = metaObj->d.relatedMetaObjects; *related; ++related) {
+ if (isNamedEnumeratorInScope(*related, scope, name))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Returns true if \a from is assignable to a property of type \a to
+bool QQmlMetaObject::canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to)
+{
+ Q_ASSERT(!from.isNull() && !to.isNull());
+
+ struct I { static bool equal(const QMetaObject *lhs, const QMetaObject *rhs) {
+ return lhs == rhs || (lhs && rhs && lhs->d.stringdata == rhs->d.stringdata);
+ } };
+
+ const QMetaObject *tom = to._m.isT1()?to._m.asT1()->metaObject():to._m.asT2();
+ if (tom == &QObject::staticMetaObject) return true;
+
+ if (from._m.isT1() && to._m.isT1()) { // QQmlPropertyCache -> QQmlPropertyCache
+ QQmlPropertyCache *fromp = from._m.asT1();
+ QQmlPropertyCache *top = to._m.asT1();
+
+ while (fromp) {
+ if (fromp == top) return true;
+ fromp = fromp->parent();
+ }
+ } else if (from._m.isT1() && to._m.isT2()) { // QQmlPropertyCache -> QMetaObject
+ QQmlPropertyCache *fromp = from._m.asT1();
+
+ while (fromp) {
+ const QMetaObject *fromm = fromp->metaObject();
+ if (fromm && I::equal(fromm, tom)) return true;
+ fromp = fromp->parent();
+ }
+ } else if (from._m.isT2() && to._m.isT1()) { // QMetaObject -> QQmlPropertyCache
+ const QMetaObject *fromm = from._m.asT2();
+
+ if (!tom) return false;
+
+ while (fromm) {
+ if (I::equal(fromm, tom)) return true;
+ fromm = fromm->superClass();
+ }
+ } else { // QMetaObject -> QMetaObject
+ const QMetaObject *fromm = from._m.asT2();
+
+ while (fromm) {
+ if (I::equal(fromm, tom)) return true;
+ fromm = fromm->superClass();
+ }
+ }
+
+ return false;
+}
+
+void QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index)
+{
+ int offset;
+
+ switch (type) {
+ case QMetaObject::ReadProperty:
+ case QMetaObject::WriteProperty:
+ case QMetaObject::ResetProperty:
+ case QMetaObject::QueryPropertyDesignable:
+ case QMetaObject::QueryPropertyEditable:
+ case QMetaObject::QueryPropertyScriptable:
+ case QMetaObject::QueryPropertyStored:
+ case QMetaObject::QueryPropertyUser:
+ offset = (*metaObject)->propertyOffset();
+ while (*index < offset) {
+ *metaObject = (*metaObject)->superClass();
+ offset = (*metaObject)->propertyOffset();
+ }
+ break;
+ case QMetaObject::InvokeMetaMethod:
+ offset = (*metaObject)->methodOffset();
+ while (*index < offset) {
+ *metaObject = (*metaObject)->superClass();
+ offset = (*metaObject)->methodOffset();
+ }
+ break;
+ default:
+ offset = 0;
+ Q_UNIMPLEMENTED();
+ offset = INT_MAX;
+ }
+
+ *index -= offset;
+}
+
+QQmlPropertyCache *QQmlMetaObject::propertyCache(QQmlEnginePrivate *e) const
+{
+ if (_m.isNull()) return nullptr;
+ if (_m.isT1()) return _m.asT1();
+ else return e->cache(_m.asT2());
+}
+
+int QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const
+{
+ Q_ASSERT(!_m.isNull() && data.coreIndex() >= 0);
+
+ int type = data.propType();
+
+ const char *propTypeName = nullptr;
+
+ if (type == QMetaType::UnknownType) {
+ // Find the return type name from the method info
+ QMetaMethod m;
+
+ if (_m.isT1()) {
+ QQmlPropertyCache *c = _m.asT1();
+ Q_ASSERT(data.coreIndex() < c->methodIndexCacheStart + c->methodIndexCache.count());
+
+ while (data.coreIndex() < c->methodIndexCacheStart)
+ c = c->_parent;
+
+ const QMetaObject *metaObject = c->createMetaObject();
+ Q_ASSERT(metaObject);
+ m = metaObject->method(data.coreIndex());
+ } else {
+ m = _m.asT2()->method(data.coreIndex());
+ }
+
+ type = m.returnType();
+ propTypeName = m.typeName();
+ }
+
+ if (QMetaType::sizeOf(type) <= int(sizeof(int))) {
+ if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration)
+ return QMetaType::Int;
+
+ if (isNamedEnumerator(metaObject(), propTypeName))
+ return QMetaType::Int;
+
+ if (type == QMetaType::UnknownType) {
+ if (unknownTypeError)
+ *unknownTypeError = propTypeName;
+ }
+ } // else we know that it's a known type, as sizeOf(UnknownType) == 0
+
+ return type;
+}
+
+int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage,
+ QByteArray *unknownTypeError) const
+{
+ Q_ASSERT(!_m.isNull() && index >= 0);
+
+ if (_m.isT1()) {
+ typedef QQmlPropertyCacheMethodArguments A;
+
+ QQmlPropertyCache *c = _m.asT1();
+ Q_ASSERT(index < c->methodIndexCacheStart + c->methodIndexCache.count());
+
+ while (index < c->methodIndexCacheStart)
+ c = c->_parent;
+
+ QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart));
+
+ if (rv->arguments() && static_cast<A *>(rv->arguments())->argumentsValid)
+ return static_cast<A *>(rv->arguments())->arguments;
+
+ const QMetaObject *metaObject = c->createMetaObject();
+ Q_ASSERT(metaObject);
+ QMetaMethod m = metaObject->method(index);
+
+ int argc = m.parameterCount();
+ if (!rv->arguments()) {
+ A *args = c->createArgumentsObject(argc, m.parameterNames());
+ rv->setArguments(args);
+ }
+ A *args = static_cast<A *>(rv->arguments());
+
+ QList<QByteArray> argTypeNames; // Only loaded if needed
+
+ for (int ii = 0; ii < argc; ++ii) {
+ int type = m.parameterType(ii);
+
+ if (QMetaType::sizeOf(type) > int(sizeof(int))) {
+ // Cannot be passed as int
+ // We know that it's a known type, as sizeOf(UnknownType) == 0
+ } else if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) {
+ type = QMetaType::Int;
+ } else {
+ if (argTypeNames.isEmpty())
+ argTypeNames = m.parameterTypes();
+ if (isNamedEnumerator(metaObject, argTypeNames.at(ii))) {
+ type = QMetaType::Int;
+ } else if (type == QMetaType::UnknownType){
+ if (unknownTypeError)
+ *unknownTypeError = argTypeNames.at(ii);
+ return nullptr;
+ }
+
+ }
+ args->arguments[ii + 1] = type;
+ }
+ args->argumentsValid = true;
+ return static_cast<A *>(rv->arguments())->arguments;
+
+ } else {
+ QMetaMethod m = _m.asT2()->method(index);
+ return methodParameterTypes(m, argStorage, unknownTypeError);
+
+ }
+}
+
+int *QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, ArgTypeStorage *argStorage,
+ QByteArray *unknownTypeError) const
+{
+ Q_ASSERT(argStorage);
+
+ int argc = m.parameterCount();
+ argStorage->resize(argc + 1);
+ argStorage->operator[](0) = argc;
+ QList<QByteArray> argTypeNames; // Only loaded if needed
+
+ for (int ii = 0; ii < argc; ++ii) {
+ int type = m.parameterType(ii);
+ if (QMetaType::sizeOf(type) > int(sizeof(int))) {
+ // Cannot be passed as int
+ // We know that it's a known type, as sizeOf(UnknownType) == 0
+ } else if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) {
+ type = QMetaType::Int;
+ } else {
+ if (argTypeNames.isEmpty())
+ argTypeNames = m.parameterTypes();
+ if (isNamedEnumerator(_m.asT2(), argTypeNames.at(ii))) {
+ type = QMetaType::Int;
+ } else if (type == QMetaType::UnknownType) {
+ if (unknownTypeError)
+ *unknownTypeError = argTypeNames.at(ii);
+ return nullptr;
+ }
+ }
+ argStorage->operator[](ii + 1) = type;
+ }
+
+ return argStorage->data();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlmetaobject_p.h b/src/qml/qml/qqmlmetaobject_p.h
new file mode 100644
index 0000000000..65d6361b90
--- /dev/null
+++ b/src/qml/qml/qqmlmetaobject_p.h
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLMETAOBJECT_P_H
+#define QQMLMETAOBJECT_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 <QtQml/qtqmlglobal.h>
+
+#include <private/qflagpointer_p.h>
+#include <private/qqmlpropertycache_p.h>
+
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qmetaobject.h>
+
+QT_BEGIN_NAMESPACE
+
+// QQmlMetaObject serves as a wrapper around either QMetaObject or QQmlPropertyCache.
+// This is necessary as we delay creation of QMetaObject for synthesized QObjects, but
+// we don't want to needlessly generate QQmlPropertyCaches every time we encounter a
+// QObject type used in assignment or when we don't have a QQmlEngine etc.
+//
+// This class does NOT reference the propertycache.
+class QQmlEnginePrivate;
+class QQmlPropertyData;
+class Q_QML_EXPORT QQmlMetaObject
+{
+public:
+ typedef QVarLengthArray<int, 9> ArgTypeStorage;
+
+ inline QQmlMetaObject();
+ inline QQmlMetaObject(QObject *);
+ inline QQmlMetaObject(const QMetaObject *);
+ inline QQmlMetaObject(QQmlPropertyCache *);
+ inline QQmlMetaObject(const QQmlMetaObject &);
+
+ inline QQmlMetaObject &operator=(const QQmlMetaObject &);
+
+ inline bool isNull() const;
+
+ inline const char *className() const;
+ inline int propertyCount() const;
+
+ inline bool hasMetaObject() const;
+ inline const QMetaObject *metaObject() const;
+
+ QQmlPropertyCache *propertyCache(QQmlEnginePrivate *) const;
+
+ int methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const;
+ int *methodParameterTypes(int index, ArgTypeStorage *argStorage,
+ QByteArray *unknownTypeError) const;
+
+ static bool canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to);
+
+ // static_metacall (on Gadgets) doesn't call the base implementation and therefore
+ // we need a helper to find the correct meta object and property/method index.
+ static void resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index);
+
+protected:
+ QBiPointer<QQmlPropertyCache, const QMetaObject> _m;
+ int *methodParameterTypes(const QMetaMethod &method, ArgTypeStorage *argStorage,
+ QByteArray *unknownTypeError) const;
+
+};
+
+QQmlMetaObject::QQmlMetaObject()
+{
+}
+
+QQmlMetaObject::QQmlMetaObject(QObject *o)
+{
+ if (o) {
+ QQmlData *ddata = QQmlData::get(o, false);
+ if (ddata && ddata->propertyCache) _m = ddata->propertyCache;
+ else _m = o->metaObject();
+ }
+}
+
+QQmlMetaObject::QQmlMetaObject(const QMetaObject *m)
+ : _m(m)
+{
+}
+
+QQmlMetaObject::QQmlMetaObject(QQmlPropertyCache *m)
+ : _m(m)
+{
+}
+
+QQmlMetaObject::QQmlMetaObject(const QQmlMetaObject &o)
+ : _m(o._m)
+{
+}
+
+QQmlMetaObject &QQmlMetaObject::operator=(const QQmlMetaObject &o)
+{
+ _m = o._m;
+ return *this;
+}
+
+bool QQmlMetaObject::isNull() const
+{
+ return _m.isNull();
+}
+
+const char *QQmlMetaObject::className() const
+{
+ if (_m.isNull()) {
+ return nullptr;
+ } else if (_m.isT1()) {
+ return _m.asT1()->className();
+ } else {
+ return _m.asT2()->className();
+ }
+}
+
+int QQmlMetaObject::propertyCount() const
+{
+ if (_m.isNull()) {
+ return 0;
+ } else if (_m.isT1()) {
+ return _m.asT1()->propertyCount();
+ } else {
+ return _m.asT2()->propertyCount();
+ }
+}
+
+bool QQmlMetaObject::hasMetaObject() const
+{
+ return _m.isT2() || (!_m.isNull() && _m.asT1()->metaObject());
+}
+
+const QMetaObject *QQmlMetaObject::metaObject() const
+{
+ if (_m.isNull()) return nullptr;
+ if (_m.isT1()) return _m.asT1()->createMetaObject();
+ else return _m.asT2();
+}
+
+QT_END_NAMESPACE
+
+#endif // QQMLMETAOBJECT_P_H
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index 0f8a850584..c2674b402a 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -37,410 +37,78 @@
**
****************************************************************************/
-#include <QtQml/qqmlprivate.h>
#include "qqmlmetatype_p.h"
-#include <private/qqmlproxymetaobject_p.h>
-#include <private/qqmlcustomparser_p.h>
-#include <private/qhashedstring_p.h>
-#include <private/qqmlimport_p.h>
-
-#include <QtCore/qdebug.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qmetaobject.h>
-#include <QtCore/qbitarray.h>
-#include <QtCore/qreadwritelock.h>
-#include <QtCore/private/qmetaobject_p.h>
-#include <QtCore/qloggingcategory.h>
-
-#include <qmetatype.h>
-#include <qobjectdefs.h>
-#include <qbytearray.h>
-#include <qreadwritelock.h>
-#include <qstring.h>
-#include <qstringlist.h>
-#include <qvector.h>
+#include <private/qqmlmetatypedata_p.h>
+#include <private/qqmltypemodule_p_p.h>
+#include <private/qqmltype_p_p.h>
+#include <private/qqmltypeloader_p.h>
+#include <private/qqmlextensionplugin_p.h>
+#include <private/qv4executablecompilationunit_p.h>
-#include <ctype.h>
-#include "qqmlcomponent.h"
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qloggingcategory.h>
Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE)
QT_BEGIN_NAMESPACE
-struct QQmlMetaTypeData
+struct LockedData : private QQmlMetaTypeData
{
- QQmlMetaTypeData();
- ~QQmlMetaTypeData();
- void registerType(QQmlTypePrivate *priv);
- QList<QQmlType> types;
- QSet<QQmlType> undeletableTypes;
- typedef QHash<int, QQmlTypePrivate *> Ids;
- Ids idToType;
- typedef QHash<QHashedStringRef, QQmlTypePrivate *> Names;
- Names nameToType;
- typedef QHash<QUrl, QQmlTypePrivate *> Files; //For file imported composite types only
- Files urlToType;
- Files urlToNonFileImportType; // For non-file imported composite and composite
- // singleton types. This way we can locate any
- // of them by url, even if it was registered as
- // a module via QQmlPrivate::RegisterCompositeType
- typedef QHash<const QMetaObject *, QQmlTypePrivate *> MetaObjects;
- MetaObjects metaObjectToType;
- typedef QHash<int, QQmlMetaType::StringConverter> StringConverters;
- StringConverters stringConverters;
-
- struct VersionedUri {
- VersionedUri()
- : majorVersion(0) {}
- VersionedUri(const QHashedString &uri, int majorVersion)
- : uri(uri), majorVersion(majorVersion) {}
- bool operator==(const VersionedUri &other) const {
- return other.majorVersion == majorVersion && other.uri == uri;
- }
- QHashedString uri;
- int majorVersion;
- };
- typedef QHash<VersionedUri, QQmlTypeModule *> TypeModules;
- TypeModules uriToModule;
-
- QBitArray objects;
- QBitArray interfaces;
- QBitArray lists;
-
- QList<QQmlPrivate::AutoParentFunction> parentFunctions;
- QVector<QQmlPrivate::QmlUnitCacheLookupFunction> lookupCachedQmlUnit;
-
- QSet<QString> protectedNamespaces;
-
- QString typeRegistrationNamespace;
-
- QHash<int, int> qmlLists;
-
- QHash<const QMetaObject *, QQmlPropertyCache *> propertyCaches;
- QQmlPropertyCache *propertyCache(const QMetaObject *metaObject, int minorVersion);
- QQmlPropertyCache *propertyCache(const QQmlType &type, int minorVersion);
-
- void startRecordingTypeRegFailures(QStringList *storage)
- { typeRegistrationFailures = storage; }
- void stopRecordingTypeRegFailures()
- { startRecordingTypeRegFailures(nullptr); }
- void recordTypeRegFailure(const QString &message)
- {
- if (typeRegistrationFailures)
- typeRegistrationFailures->append(message);
- else
- qWarning("%s", message.toUtf8().constData());
- }
-
-private:
- QStringList *typeRegistrationFailures = nullptr;
+ friend class QQmlMetaTypeDataPtr;
};
-struct EnumInfo {
- QStringList path;
- QString metaObjectName;
- QString enumName;
- QString enumKey;
- QString metaEnumScope;
- bool scoped;
-};
+Q_GLOBAL_STATIC(LockedData, metaTypeData)
+Q_GLOBAL_STATIC(QRecursiveMutex, metaTypeDataLock)
-class QQmlTypeModulePrivate
+class QQmlMetaTypeDataPtr
{
+ Q_DISABLE_COPY_MOVE(QQmlMetaTypeDataPtr)
public:
- QQmlTypeModulePrivate()
- : minMinorVersion(INT_MAX), maxMinorVersion(0), locked(false) {}
-
- static QQmlTypeModulePrivate* get(QQmlTypeModule* q) { return q->d; }
+ QQmlMetaTypeDataPtr() : locker(metaTypeDataLock()), data(metaTypeData()) {}
+ ~QQmlMetaTypeDataPtr() = default;
- QQmlMetaTypeData::VersionedUri uri;
+ QQmlMetaTypeData &operator*() { return *data; }
+ QQmlMetaTypeData *operator->() { return data; }
+ operator QQmlMetaTypeData *() { return data; }
- int minMinorVersion;
- int maxMinorVersion;
- bool locked;
+ const QQmlMetaTypeData &operator*() const { return *data; }
+ const QQmlMetaTypeData *operator->() const { return data; }
+ operator const QQmlMetaTypeData *() const { return data; }
- void add(QQmlTypePrivate *);
- void remove(const QQmlTypePrivate *type);
-
- typedef QStringHash<QList<QQmlTypePrivate *> > TypeHash;
- TypeHash typeHash;
-};
-
-Q_GLOBAL_STATIC(QQmlMetaTypeData, metaTypeData)
-Q_GLOBAL_STATIC_WITH_ARGS(QMutex, metaTypeDataLock, (QMutex::Recursive))
-
-static uint qHash(const QQmlMetaTypeData::VersionedUri &v)
-{
- return v.uri.hash() ^ qHash(v.majorVersion);
-}
+ bool isValid() const { return data != nullptr; }
-QQmlMetaTypeRegistrationFailureRecorder::QQmlMetaTypeRegistrationFailureRecorder()
-{
- metaTypeData()->startRecordingTypeRegFailures(&_failures);
-}
-
-QQmlMetaTypeRegistrationFailureRecorder::~QQmlMetaTypeRegistrationFailureRecorder()
-{
- metaTypeData()->stopRecordingTypeRegFailures();
-}
-
-QQmlMetaTypeData::QQmlMetaTypeData()
-{
-}
-
-QQmlMetaTypeData::~QQmlMetaTypeData()
-{
- for (TypeModules::const_iterator i = uriToModule.constBegin(), cend = uriToModule.constEnd(); i != cend; ++i)
- delete *i;
- for (QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator it = propertyCaches.begin(), end = propertyCaches.end();
- it != end; ++it)
- (*it)->release();
-}
-
-class QQmlTypePrivate
-{
- Q_DISABLE_COPY(QQmlTypePrivate)
-public:
- QQmlTypePrivate(QQmlType::RegistrationType type);
- ~QQmlTypePrivate();
-
- void init() const;
- void initEnums(const QQmlPropertyCache *cache = nullptr) const;
- void insertEnums(const QMetaObject *metaObject) const;
- void insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const;
-
- QAtomicInt refCount;
- QQmlType::RegistrationType regType;
-
- struct QQmlCppTypeData
- {
- int allocationSize;
- void (*newFunc)(void *);
- QString noCreationReason;
- int parserStatusCast;
- QObject *(*extFunc)(QObject *);
- const QMetaObject *extMetaObject;
- QQmlCustomParser *customParser;
- QQmlAttachedPropertiesFunc attachedPropertiesFunc;
- const QMetaObject *attachedPropertiesType;
- int propertyValueSourceCast;
- int propertyValueInterceptorCast;
- bool registerEnumClassesUnscoped;
- };
-
- struct QQmlSingletonTypeData
- {
- QQmlType::SingletonInstanceInfo *singletonInstanceInfo;
- };
-
- struct QQmlCompositeTypeData
- {
- QUrl url;
- };
-
- union extraData {
- QQmlCppTypeData* cd;
- QQmlSingletonTypeData* sd;
- QQmlCompositeTypeData* fd;
- } extraData;
-
- const char *iid;
- QHashedString module;
- QString name;
- QString elementName;
- int version_maj;
- int version_min;
- int typeId;
- int listId;
- int revision;
- mutable bool containsRevisionedAttributes;
- mutable QQmlType superType;
- const QMetaObject *baseMetaObject;
-
- int index;
- mutable volatile bool isSetup:1;
- mutable volatile bool isEnumFromCacheSetup:1;
- mutable volatile bool isEnumFromBaseSetup:1;
- mutable bool haveSuperType:1;
- mutable QList<QQmlProxyMetaObject::ProxyData> metaObjects;
- mutable QStringHash<int> enums;
- mutable QStringHash<int> scopedEnumIndex; // maps from enum name to index in scopedEnums
- mutable QList<QStringHash<int>*> scopedEnums;
-
- struct PropertyCacheByMinorVersion
- {
- PropertyCacheByMinorVersion() : cache(nullptr), minorVersion(-1) {}
- explicit PropertyCacheByMinorVersion(QQmlPropertyCache *pc, int ver) : cache(pc), minorVersion(ver) {}
- QQmlPropertyCachePtr cache;
- int minorVersion;
- };
- QVector<PropertyCacheByMinorVersion> propertyCaches;
- QQmlPropertyCache *propertyCacheForMinorVersion(int minorVersion) const;
- void setPropertyCacheForMinorVersion(int minorVersion, QQmlPropertyCache *cache);
private:
- void createListOfPossibleConflictingItems(const QMetaObject *metaObject, QList<EnumInfo> &enumInfoList, QStringList path) const;
- void createEnumConflictReport(const QMetaObject *metaObject, const QString &conflictingKey) const;
+ QMutexLocker locker;
+ LockedData *data = nullptr;
};
-void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv)
+static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data,
+ const QQmlPrivate::RegisterInterface &type)
{
- for (int i = 0; i < types.count(); ++i) {
- if (!types.at(i).isValid()) {
- types[i] = QQmlType(priv);
- priv->index = i;
- return;
- }
- }
- types.append(QQmlType(priv));
- priv->index = types.count() - 1;
-}
-
-void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e)
-{
- if (scriptCallback && scriptApi(e).isUndefined()) {
- QJSValue value = scriptCallback(e, e);
- if (value.isQObject()) {
- QObject *o = value.toQObject();
- // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj)
- // should behave identically to QML singleton types.
- e->setContextForObject(o, new QQmlContext(e->rootContext(), e));
- }
- setScriptApi(e, value);
- } else if (qobjectCallback && !qobjectApi(e)) {
- QObject *o = qobjectCallback(e, e);
- setQObjectApi(e, o);
- if (!o) {
- qFatal("qmlRegisterSingletonType(): \"%s\" is not available because the callback function returns a null pointer.", qPrintable(typeName));
- }
- // if this object can use a property cache, create it now
- QQmlData::ensurePropertyCache(e, o);
- // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj)
- // should behave identically to QML singleton types.
- e->setContextForObject(o, new QQmlContext(e->rootContext(), e));
- } else if (!url.isEmpty() && !qobjectApi(e)) {
- QQmlComponent component(e, url, QQmlComponent::PreferSynchronous);
- QObject *o = component.beginCreate(e->rootContext());
- setQObjectApi(e, o);
- if (o)
- component.completeCreate();
- }
-}
-
-void QQmlType::SingletonInstanceInfo::destroy(QQmlEngine *e)
-{
- // cleans up the engine-specific singleton instances if they exist.
- scriptApis.remove(e);
- QObject *o = qobjectApis.take(e);
- if (o) {
- QQmlData *ddata = QQmlData::get(o, false);
- if (url.isEmpty() && ddata && ddata->indestructible && ddata->explicitIndestructibleSet)
- return;
- delete o;
- }
-}
-
-void QQmlType::SingletonInstanceInfo::setQObjectApi(QQmlEngine *e, QObject *o)
-{
- qobjectApis.insert(e, o);
-}
-
-QObject *QQmlType::SingletonInstanceInfo::qobjectApi(QQmlEngine *e) const
-{
- return qobjectApis.value(e);
-}
-
-void QQmlType::SingletonInstanceInfo::setScriptApi(QQmlEngine *e, const QJSValue &v)
-{
- scriptApis.insert(e, v);
-}
-
-QJSValue QQmlType::SingletonInstanceInfo::scriptApi(QQmlEngine *e) const
-{
- return scriptApis.value(e);
-}
-
-QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type)
-: refCount(1), regType(type), iid(nullptr), typeId(0), listId(0), revision(0),
- containsRevisionedAttributes(false), baseMetaObject(nullptr),
- index(-1), isSetup(false), isEnumFromCacheSetup(false), isEnumFromBaseSetup(false),
- haveSuperType(false)
-{
- switch (type) {
- case QQmlType::CppType:
- extraData.cd = new QQmlCppTypeData;
- extraData.cd->allocationSize = 0;
- extraData.cd->newFunc = nullptr;
- extraData.cd->parserStatusCast = -1;
- extraData.cd->extFunc = nullptr;
- extraData.cd->extMetaObject = nullptr;
- extraData.cd->customParser = nullptr;
- extraData.cd->attachedPropertiesFunc = nullptr;
- extraData.cd->attachedPropertiesType = nullptr;
- extraData.cd->propertyValueSourceCast = -1;
- extraData.cd->propertyValueInterceptorCast = -1;
- extraData.cd->registerEnumClassesUnscoped = true;
- break;
- case QQmlType::SingletonType:
- case QQmlType::CompositeSingletonType:
- extraData.sd = new QQmlSingletonTypeData;
- extraData.sd->singletonInstanceInfo = nullptr;
- break;
- case QQmlType::InterfaceType:
- extraData.cd = nullptr;
- break;
- case QQmlType::CompositeType:
- extraData.fd = new QQmlCompositeTypeData;
- break;
- default: qFatal("QQmlTypePrivate Internal Error.");
- }
-}
-
-QQmlTypePrivate::~QQmlTypePrivate()
-{
- qDeleteAll(scopedEnums);
- switch (regType) {
- case QQmlType::CppType:
- delete extraData.cd->customParser;
- delete extraData.cd;
- break;
- case QQmlType::SingletonType:
- case QQmlType::CompositeSingletonType:
- delete extraData.sd->singletonInstanceInfo;
- delete extraData.sd;
- break;
- case QQmlType::CompositeType:
- delete extraData.fd;
- break;
- default: //Also InterfaceType, because it has no extra data
- break;
- }
-}
-
-QQmlType::QQmlType(QQmlMetaTypeData *data, const QQmlPrivate::RegisterInterface &interface)
- : d(new QQmlTypePrivate(InterfaceType))
-{
- d->iid = interface.iid;
- d->typeId = interface.typeId;
- d->listId = interface.listId;
+ auto *d = new QQmlTypePrivate(QQmlType::InterfaceType);
+ d->iid = type.iid;
+ d->typeId = type.typeId;
+ d->listId = type.listId;
d->isSetup = true;
d->version_maj = 0;
d->version_min = 0;
data->registerType(d);
+ return d;
}
-QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterSingletonType &type)
- : d(new QQmlTypePrivate(SingletonType))
+static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName,
+ const QQmlPrivate::RegisterSingletonType &type)
{
+ auto *d = new QQmlTypePrivate(QQmlType::SingletonType);
data->registerType(d);
- d->elementName = elementName;
- d->module = QString::fromUtf8(type.uri);
-
+ d->setName(QString::fromUtf8(type.uri), elementName);
d->version_maj = type.versionMajor;
d->version_min = type.versionMinor;
- if (type.qobjectApi) {
+ if (type.qobjectApi || (type.version >= 3 && type.generalizedQobjectApi)) {
if (type.version >= 1) // static metaobject added in version 1
d->baseMetaObject = type.instanceMetaObject;
if (type.version >= 2) // typeId added in version 2
@@ -449,37 +117,26 @@ QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQm
d->revision = type.revision;
}
- d->extraData.sd->singletonInstanceInfo = new SingletonInstanceInfo;
+ d->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo;
d->extraData.sd->singletonInstanceInfo->scriptCallback = type.scriptApi;
- d->extraData.sd->singletonInstanceInfo->qobjectCallback = type.qobjectApi;
+ if (type.version >= 3) {
+ d->extraData.sd->singletonInstanceInfo->qobjectCallback = type.generalizedQobjectApi;
+ } else {
+ d->extraData.sd->singletonInstanceInfo->qobjectCallback = type.qobjectApi;
+ }
d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName);
d->extraData.sd->singletonInstanceInfo->instanceMetaObject
- = (type.qobjectApi && type.version >= 1) ? type.instanceMetaObject : nullptr;
-}
-
-QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterCompositeSingletonType &type)
- : d(new QQmlTypePrivate(CompositeSingletonType))
-{
- data->registerType(d);
-
- d->elementName = elementName;
- d->module = QString::fromUtf8(type.uri);
-
- d->version_maj = type.versionMajor;
- d->version_min = type.versionMinor;
+ = ((type.qobjectApi || (type.version >= 3 && type.generalizedQobjectApi) ) && type.version >= 1) ? type.instanceMetaObject : nullptr;
- d->extraData.sd->singletonInstanceInfo = new SingletonInstanceInfo;
- d->extraData.sd->singletonInstanceInfo->url = QQmlTypeLoader::normalize(type.url);
- d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName);
+ return d;
}
-QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterType &type)
- : d(new QQmlTypePrivate(CppType))
+static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName,
+ const QQmlPrivate::RegisterType &type)
{
+ QQmlTypePrivate *d = new QQmlTypePrivate(QQmlType::CppType);
data->registerType(d);
-
- d->elementName = elementName;
- d->module = QString::fromUtf8(type.uri);
+ d->setName(QString::fromUtf8(type.uri), elementName);
d->version_maj = type.versionMajor;
d->version_min = type.versionMinor;
@@ -509,141 +166,41 @@ QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQm
if (indexOfClassInfo != -1 && QString::fromUtf8(d->baseMetaObject->classInfo(indexOfClassInfo).value()) == QLatin1String("false"))
d->extraData.cd->registerEnumClassesUnscoped = false;
}
+
+ return d;
}
-QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterCompositeType &type)
- : d(new QQmlTypePrivate(CompositeType))
+static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName,
+ const QQmlPrivate::RegisterCompositeType &type)
{
+ auto *d = new QQmlTypePrivate(QQmlType::CompositeType);
data->registerType(d);
-
- d->elementName = elementName;
-
- d->module = QString::fromUtf8(type.uri);
+ d->setName(QString::fromUtf8(type.uri), elementName);
d->version_maj = type.versionMajor;
d->version_min = type.versionMinor;
d->extraData.fd->url = QQmlTypeLoader::normalize(type.url);
+ return d;
}
-QQmlType::QQmlType()
- : d(nullptr)
-{
-}
-
-QQmlType::QQmlType(const QQmlType &other)
- : d(other.d)
-{
- if (d)
- d->refCount.ref();
-}
-
-QQmlType &QQmlType::operator =(const QQmlType &other)
-{
- if (d != other.d) {
- if (d && !d->refCount.deref())
- delete d;
- d = other.d;
- if (d)
- d->refCount.ref();
- }
- return *this;
-}
-
-QQmlType::QQmlType(QQmlTypePrivate *priv)
- : d(priv)
-{
- if (d)
- d->refCount.ref();
-}
-
-QQmlType::~QQmlType()
-{
- if (d && !d->refCount.deref())
- delete d;
-}
-
-QHashedString QQmlType::module() const
-{
- if (!d)
- return QHashedString();
- return d->module;
-}
-
-int QQmlType::majorVersion() const
-{
- if (!d)
- return -1;
- return d->version_maj;
-}
-
-int QQmlType::minorVersion() const
+static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName,
+ const QQmlPrivate::RegisterCompositeSingletonType &type)
{
- if (!d)
- return -1;
- return d->version_min;
-}
-
-bool QQmlType::availableInVersion(int vmajor, int vminor) const
-{
- Q_ASSERT(vmajor >= 0 && vminor >= 0);
- if (!d)
- return false;
- return vmajor == d->version_maj && vminor >= d->version_min;
-}
-
-bool QQmlType::availableInVersion(const QHashedStringRef &module, int vmajor, int vminor) const
-{
- Q_ASSERT(vmajor >= 0 && vminor >= 0);
- if (!d)
- return false;
- return module == d->module && vmajor == d->version_maj && vminor >= d->version_min;
-}
-
-// returns the nearest _registered_ super class
-QQmlType QQmlType::superType() const
-{
- if (!d)
- return QQmlType();
- if (!d->haveSuperType && d->baseMetaObject) {
- const QMetaObject *mo = d->baseMetaObject->superClass();
- while (mo && !d->superType.isValid()) {
- d->superType = QQmlMetaType::qmlType(mo, d->module, d->version_maj, d->version_min);
- mo = mo->superClass();
- }
- d->haveSuperType = true;
- }
-
- return d->superType;
-}
+ auto *d = new QQmlTypePrivate(QQmlType::CompositeSingletonType);
+ data->registerType(d);
+ d->setName(QString::fromUtf8(type.uri), elementName);
-QQmlType QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const
-{
- Q_ASSERT(isComposite());
- if (!engine || !d)
- return QQmlType();
- QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()));
- if (td.isNull() || !td->isComplete())
- return QQmlType();
- QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit();
- const QMetaObject *mo = compilationUnit->rootPropertyCache()->firstCppMetaObject();
- return QQmlMetaType::qmlType(mo);
-}
+ d->version_maj = type.versionMajor;
+ d->version_min = type.versionMinor;
-QQmlPropertyCache *QQmlType::compositePropertyCache(QQmlEnginePrivate *engine) const
-{
- // similar logic to resolveCompositeBaseType
- Q_ASSERT(isComposite());
- if (!engine)
- return nullptr;
- QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()));
- if (td.isNull() || !td->isComplete())
- return nullptr;
- QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit();
- return compilationUnit->rootPropertyCache().data();
+ d->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo;
+ d->extraData.sd->singletonInstanceInfo->url = QQmlTypeLoader::normalize(type.url);
+ d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName);
+ return d;
}
-static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
- const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd)
+void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
+ const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd)
{
// Set classname
builder.setClassName(ignoreEnd->className());
@@ -710,918 +267,10 @@ static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
}
}
-static bool isPropertyRevisioned(const QMetaObject *mo, int index)
-{
- int i = index;
- i -= mo->propertyOffset();
- if (i < 0 && mo->d.superdata)
- return isPropertyRevisioned(mo->d.superdata, index);
-
- const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate*>(mo->d.data);
- if (i >= 0 && i < mop->propertyCount) {
- int handle = mop->propertyData + 3*i;
- int flags = mo->d.data[handle + 2];
-
- return (flags & Revisioned);
- }
-
- return false;
-}
-
-void QQmlTypePrivate::init() const
-{
- if (isSetup)
- return;
-
- QMutexLocker lock(metaTypeDataLock());
- if (isSetup)
- return;
-
- const QMetaObject *mo = baseMetaObject;
- if (!mo) {
- // version 0 singleton type without metaobject information
- return;
- }
-
- if (regType == QQmlType::CppType) {
- // Setup extended meta object
- // XXX - very inefficient
- if (extraData.cd->extFunc) {
- QMetaObjectBuilder builder;
- clone(builder, extraData.cd->extMetaObject, extraData.cd->extMetaObject, extraData.cd->extMetaObject);
- builder.setFlags(QMetaObjectBuilder::DynamicMetaObject);
- QMetaObject *mmo = builder.toMetaObject();
- mmo->d.superdata = mo;
- QQmlProxyMetaObject::ProxyData data = { mmo, extraData.cd->extFunc, 0, 0 };
- metaObjects << data;
- }
- }
-
- mo = mo->d.superdata;
- while(mo) {
- QQmlTypePrivate *t = metaTypeData()->metaObjectToType.value(mo);
- if (t) {
- if (t->regType == QQmlType::CppType) {
- if (t->extraData.cd->extFunc) {
- QMetaObjectBuilder builder;
- clone(builder, t->extraData.cd->extMetaObject, t->baseMetaObject, baseMetaObject);
- builder.setFlags(QMetaObjectBuilder::DynamicMetaObject);
- QMetaObject *mmo = builder.toMetaObject();
- mmo->d.superdata = baseMetaObject;
- if (!metaObjects.isEmpty())
- metaObjects.constLast().metaObject->d.superdata = mmo;
- QQmlProxyMetaObject::ProxyData data = { mmo, t->extraData.cd->extFunc, 0, 0 };
- metaObjects << data;
- }
- }
- }
- mo = mo->d.superdata;
- }
-
- for (int ii = 0; ii < metaObjects.count(); ++ii) {
- metaObjects[ii].propertyOffset =
- metaObjects.at(ii).metaObject->propertyOffset();
- metaObjects[ii].methodOffset =
- metaObjects.at(ii).metaObject->methodOffset();
- }
-
- // Check for revisioned details
- {
- const QMetaObject *mo = nullptr;
- if (metaObjects.isEmpty())
- mo = baseMetaObject;
- else
- mo = metaObjects.constFirst().metaObject;
-
- for (int ii = 0; !containsRevisionedAttributes && ii < mo->propertyCount(); ++ii) {
- if (isPropertyRevisioned(mo, ii))
- containsRevisionedAttributes = true;
- }
-
- for (int ii = 0; !containsRevisionedAttributes && ii < mo->methodCount(); ++ii) {
- if (mo->method(ii).revision() != 0)
- containsRevisionedAttributes = true;
- }
- }
-
- isSetup = true;
- lock.unlock();
-}
-
-void QQmlTypePrivate::initEnums(const QQmlPropertyCache *cache) const
-{
- if ((isEnumFromBaseSetup || !baseMetaObject)
- && (isEnumFromCacheSetup || !cache)) {
- return;
- }
-
- init();
-
- QMutexLocker lock(metaTypeDataLock());
-
- if (!isEnumFromCacheSetup && cache) {
- insertEnumsFromPropertyCache(cache);
- isEnumFromCacheSetup = true;
- }
-
- if (!isEnumFromBaseSetup && baseMetaObject) { // could be singleton type without metaobject
- insertEnums(baseMetaObject);
- isEnumFromBaseSetup = true;
- }
-}
-
-void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const
-{
- // Add any enum values defined by 'related' classes
- if (metaObject->d.relatedMetaObjects) {
- const auto *related = metaObject->d.relatedMetaObjects;
- if (related) {
- while (*related)
- insertEnums(*related++);
- }
- }
-
- QSet<QString> localEnums;
- const QMetaObject *localMetaObject = nullptr;
-
- // Add any enum values defined by this class, overwriting any inherited values
- for (int ii = 0; ii < metaObject->enumeratorCount(); ++ii) {
- QMetaEnum e = metaObject->enumerator(ii);
- const bool isScoped = e.isScoped();
- QStringHash<int> *scoped = isScoped ? new QStringHash<int>() : nullptr;
-
- // We allow enums in sub-classes to overwrite enums from base-classes, such as
- // ListView.Center (from enum PositionMode) overwriting Item.Center (from enum TransformOrigin).
- // This is acceptable because the _use_ of the enum from the QML side requires qualification
- // anyway, i.e. ListView.Center vs. Item.Center.
- // However if a class defines two enums with the same value, then that must produce a warning
- // because it represents a valid conflict.
- if (e.enclosingMetaObject() != localMetaObject) {
- localEnums.clear();
- localMetaObject = e.enclosingMetaObject();
- }
-
- for (int jj = 0; jj < e.keyCount(); ++jj) {
- const QString key = QString::fromUtf8(e.key(jj));
- const int value = e.value(jj);
- if (!isScoped || (regType == QQmlType::CppType && extraData.cd->registerEnumClassesUnscoped)) {
- if (localEnums.contains(key)) {
- auto existingEntry = enums.find(key);
- if (existingEntry != enums.end() && existingEntry.value() != value) {
- qWarning("Previously registered enum will be overwritten due to name clash: %s.%s", metaObject->className(), key.toUtf8().constData());
- createEnumConflictReport(metaObject, key);
- }
- } else {
- localEnums.insert(key);
- }
- enums.insert(key, value);
- }
- if (isScoped)
- scoped->insert(key, value);
- }
-
- if (isScoped) {
- scopedEnums << scoped;
- scopedEnumIndex.insert(QString::fromUtf8(e.name()), scopedEnums.count()-1);
- }
- }
-}
-
-void QQmlTypePrivate::createListOfPossibleConflictingItems(const QMetaObject *metaObject, QList<EnumInfo> &enumInfoList, QStringList path) const
-{
- path.append(QString::fromUtf8(metaObject->className()));
-
- if (metaObject->d.relatedMetaObjects) {
- const auto *related = metaObject->d.relatedMetaObjects;
- if (related) {
- while (*related)
- createListOfPossibleConflictingItems(*related++, enumInfoList, path);
- }
- }
-
- for (int ii = 0; ii < metaObject->enumeratorCount(); ++ii) {
- const auto e = metaObject->enumerator(ii);
-
- for (int jj = 0; jj < e.keyCount(); ++jj) {
- const QString key = QString::fromUtf8(e.key(jj));
-
- EnumInfo enumInfo;
- enumInfo.metaObjectName = QString::fromUtf8(metaObject->className());
- enumInfo.enumName = QString::fromUtf8(e.name());
- enumInfo.enumKey = key;
- enumInfo.scoped = e.isScoped();
- enumInfo.path = path;
- enumInfo.metaEnumScope = QString::fromUtf8(e.scope());
- enumInfoList.append(enumInfo);
- }
- }
-}
-
-void QQmlTypePrivate::createEnumConflictReport(const QMetaObject *metaObject, const QString &conflictingKey) const
-{
- QList<EnumInfo> enumInfoList;
-
- if (baseMetaObject) // prefer baseMetaObject if available
- metaObject = baseMetaObject;
-
- if (!metaObject) { // If there is no metaObject at all return early
- qWarning() << "No meta object information available. Skipping conflict analysis.";
- return;
- }
-
- createListOfPossibleConflictingItems(metaObject, enumInfoList, QStringList());
-
- qWarning().noquote() << QLatin1String("Possible conflicting items:");
- // find items with conflicting key
- for (const auto i : enumInfoList) {
- if (i.enumKey == conflictingKey)
- qWarning().noquote().nospace() << " " << i.metaObjectName << "." << i.enumName << "." << i.enumKey << " from scope "
- << i.metaEnumScope << " injected by " << i.path.join(QLatin1String("->"));
- }
-}
-
-void QQmlTypePrivate::insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const
-{
- const QMetaObject *cppMetaObject = cache->firstCppMetaObject();
-
- while (cache && cache->metaObject() != cppMetaObject) {
-
- int count = cache->qmlEnumCount();
- for (int ii = 0; ii < count; ++ii) {
- QStringHash<int> *scoped = new QStringHash<int>();
- QQmlEnumData *enumData = cache->qmlEnum(ii);
-
- for (int jj = 0; jj < enumData->values.count(); ++jj) {
- const QQmlEnumValue &value = enumData->values.at(jj);
- enums.insert(value.namedValue, value.value);
- scoped->insert(value.namedValue, value.value);
- }
- scopedEnums << scoped;
- scopedEnumIndex.insert(enumData->name, scopedEnums.count()-1);
- }
- cache = cache->parent();
- }
- insertEnums(cppMetaObject);
-}
-
-
-QQmlPropertyCache *QQmlTypePrivate::propertyCacheForMinorVersion(int minorVersion) const
-{
- for (int i = 0; i < propertyCaches.count(); ++i)
- if (propertyCaches.at(i).minorVersion == minorVersion)
- return propertyCaches.at(i).cache.data();
- return nullptr;
-}
-
-void QQmlTypePrivate::setPropertyCacheForMinorVersion(int minorVersion, QQmlPropertyCache *cache)
-{
- for (int i = 0; i < propertyCaches.count(); ++i) {
- if (propertyCaches.at(i).minorVersion == minorVersion) {
- propertyCaches[i].cache = cache;
- return;
- }
- }
- propertyCaches.append(PropertyCacheByMinorVersion(cache, minorVersion));
-}
-
-QByteArray QQmlType::typeName() const
-{
- if (d) {
- if (d->regType == SingletonType || d->regType == CompositeSingletonType)
- return d->extraData.sd->singletonInstanceInfo->typeName.toUtf8();
- else if (d->baseMetaObject)
- return d->baseMetaObject->className();
- }
- return QByteArray();
-}
-
-QString QQmlType::elementName() const
-{
- if (!d)
- return QString();
- return d->elementName;
-}
-
-QString QQmlType::qmlTypeName() const
-{
- if (!d)
- return QString();
- if (d->name.isEmpty()) {
- if (!d->module.isEmpty())
- d->name = static_cast<QString>(d->module) + QLatin1Char('/') + d->elementName;
- else
- d->name = d->elementName;
- }
-
- return d->name;
-}
-
-QObject *QQmlType::create() const
-{
- if (!d || !isCreatable())
- return nullptr;
-
- d->init();
-
- QObject *rv = (QObject *)operator new(d->extraData.cd->allocationSize);
- d->extraData.cd->newFunc(rv);
-
- if (rv && !d->metaObjects.isEmpty())
- (void)new QQmlProxyMetaObject(rv, &d->metaObjects);
-
- return rv;
-}
-
-void QQmlType::create(QObject **out, void **memory, size_t additionalMemory) const
-{
- if (!d || !isCreatable())
- return;
-
- d->init();
-
- QObject *rv = (QObject *)operator new(d->extraData.cd->allocationSize + additionalMemory);
- d->extraData.cd->newFunc(rv);
-
- if (rv && !d->metaObjects.isEmpty())
- (void)new QQmlProxyMetaObject(rv, &d->metaObjects);
-
- *out = rv;
- *memory = ((char *)rv) + d->extraData.cd->allocationSize;
-}
-
-QQmlType::SingletonInstanceInfo *QQmlType::singletonInstanceInfo() const
-{
- if (!d)
- return nullptr;
- if (d->regType != SingletonType && d->regType != CompositeSingletonType)
- return nullptr;
- return d->extraData.sd->singletonInstanceInfo;
-}
-
-QQmlCustomParser *QQmlType::customParser() const
-{
- if (!d)
- return nullptr;
- if (d->regType != CppType)
- return nullptr;
- return d->extraData.cd->customParser;
-}
-
-QQmlType::CreateFunc QQmlType::createFunction() const
-{
- if (!d || d->regType != CppType)
- return nullptr;
- return d->extraData.cd->newFunc;
-}
-
-QString QQmlType::noCreationReason() const
-{
- if (!d || d->regType != CppType)
- return QString();
- return d->extraData.cd->noCreationReason;
-}
-
-bool QQmlType::isCreatable() const
-{
- return d && d->regType == CppType && d->extraData.cd->newFunc;
-}
-
-QQmlType::ExtensionFunc QQmlType::extensionFunction() const
-{
- if (!d || d->regType != CppType)
- return nullptr;
- return d->extraData.cd->extFunc;
-}
-
-bool QQmlType::isExtendedType() const
-{
- if (!d)
- return false;
- d->init();
-
- return !d->metaObjects.isEmpty();
-}
-
-bool QQmlType::isSingleton() const
-{
- return d && (d->regType == SingletonType || d->regType == CompositeSingletonType);
-}
-
-bool QQmlType::isInterface() const
-{
- return d && d->regType == InterfaceType;
-}
-
-bool QQmlType::isComposite() const
-{
- return d && (d->regType == CompositeType || d->regType == CompositeSingletonType);
-}
-
-bool QQmlType::isCompositeSingleton() const
-{
- return d && d->regType == CompositeSingletonType;
-}
-
-int QQmlType::typeId() const
-{
- return d ? d->typeId : -1;
-}
-
-int QQmlType::qListTypeId() const
-{
- return d ? d->listId : -1;
-}
-
-const QMetaObject *QQmlType::metaObject() const
-{
- if (!d)
- return nullptr;
- d->init();
-
- if (d->metaObjects.isEmpty())
- return d->baseMetaObject;
- else
- return d->metaObjects.constFirst().metaObject;
-
-}
-
-const QMetaObject *QQmlType::baseMetaObject() const
-{
- return d ? d->baseMetaObject : nullptr;
-}
-
-bool QQmlType::containsRevisionedAttributes() const
-{
- if (!d)
- return false;
- d->init();
-
- return d->containsRevisionedAttributes;
-}
-
-int QQmlType::metaObjectRevision() const
-{
- return d ? d->revision : -1;
-}
-
-QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction(QQmlEnginePrivate *engine) const
-{
- if (!d)
- return nullptr;
- if (d->regType == CppType)
- return d->extraData.cd->attachedPropertiesFunc;
-
- QQmlType base;
- if (d->regType == CompositeType)
- base = resolveCompositeBaseType(engine);
- return base.attachedPropertiesFunction(engine);
-}
-
-const QMetaObject *QQmlType::attachedPropertiesType(QQmlEnginePrivate *engine) const
-{
- if (!d)
- return nullptr;
- if (d->regType == CppType)
- return d->extraData.cd->attachedPropertiesType;
-
- QQmlType base;
- if (d->regType == CompositeType)
- base = resolveCompositeBaseType(engine);
- return base.attachedPropertiesType(engine);
-}
-
-/*
-This is the id passed to qmlAttachedPropertiesById(). This is different from the index
-for the case that a single class is registered under two or more names (eg. Item in
-Qt 4.7 and QtQuick 1.0).
-*/
-int QQmlType::attachedPropertiesId(QQmlEnginePrivate *engine) const
-{
- if (!d)
- return -1;
- if (d->regType == CppType)
- return d->extraData.cd->attachedPropertiesType ? d->index : -1;
-
- QQmlType base;
- if (d->regType == CompositeType)
- base = resolveCompositeBaseType(engine);
- return base.attachedPropertiesId(engine);
-}
-
-int QQmlType::parserStatusCast() const
-{
- if (!d || d->regType != CppType)
- return -1;
- return d->extraData.cd->parserStatusCast;
-}
-
-int QQmlType::propertyValueSourceCast() const
-{
- if (!d || d->regType != CppType)
- return -1;
- return d->extraData.cd->propertyValueSourceCast;
-}
-
-int QQmlType::propertyValueInterceptorCast() const
-{
- if (!d || d->regType != CppType)
- return -1;
- return d->extraData.cd->propertyValueInterceptorCast;
-}
-
-const char *QQmlType::interfaceIId() const
-{
- if (!d || d->regType != InterfaceType)
- return nullptr;
- return d->iid;
-}
-
-int QQmlType::index() const
-{
- return d ? d->index : -1;
-}
-
-QUrl QQmlType::sourceUrl() const
-{
- if (d) {
- if (d->regType == CompositeType)
- return d->extraData.fd->url;
- else if (d->regType == CompositeSingletonType)
- return d->extraData.sd->singletonInstanceInfo->url;
- }
- return QUrl();
-}
-
-int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, bool *ok) const
-{
- Q_ASSERT(ok);
- if (d) {
- const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr;
-
- *ok = true;
-
- d->initEnums(cache);
-
- int *rv = d->enums.value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
-}
-
-int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &name, bool *ok) const
-{
- Q_ASSERT(ok);
- if (d) {
- const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr;
-
- *ok = true;
-
- d->initEnums(cache);
-
- int *rv = d->enums.value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
-}
-
-int QQmlType::enumValue(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const
-{
- Q_ASSERT(ok);
- if (d) {
- const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr;
- *ok = true;
-
- d->initEnums(cache);
-
- int *rv = d->enums.value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
-}
-
-int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const
-{
- Q_ASSERT(ok);
- if (d) {
- const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr;
- *ok = true;
-
- d->initEnums(cache);
-
- int *rv = d->scopedEnumIndex.value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
-}
-
-int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QString &name, bool *ok) const
-{
- Q_ASSERT(ok);
- if (d) {
- const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr;
- *ok = true;
-
- d->initEnums(cache);
-
- int *rv = d->scopedEnumIndex.value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
-}
-
-int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *name, bool *ok) const
-{
- Q_UNUSED(engine)
- Q_ASSERT(ok);
- *ok = true;
-
- if (d) {
- Q_ASSERT(index > -1 && index < d->scopedEnums.count());
- int *rv = d->scopedEnums.at(index)->value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
-}
-
-int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &name, bool *ok) const
-{
- Q_UNUSED(engine)
- Q_ASSERT(ok);
- *ok = true;
-
- if (d) {
- Q_ASSERT(index > -1 && index < d->scopedEnums.count());
- int *rv = d->scopedEnums.at(index)->value(name);
- if (rv)
- return *rv;
- }
-
- *ok = false;
- return -1;
-}
-
-int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &scopedEnumName, const QByteArray &name, bool *ok) const
-{
- Q_ASSERT(ok);
- if (d) {
- const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr;
- *ok = true;
-
- d->initEnums(cache);
-
- int *rv = d->scopedEnumIndex.value(QHashedCStringRef(scopedEnumName.constData(), scopedEnumName.length()));
- if (rv) {
- int index = *rv;
- Q_ASSERT(index > -1 && index < d->scopedEnums.count());
- rv = d->scopedEnums.at(index)->value(QHashedCStringRef(name.constData(), name.length()));
- if (rv)
- return *rv;
- }
- }
-
- *ok = false;
- return -1;
-}
-
-int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &scopedEnumName, const QStringRef &name, bool *ok) const
-{
- Q_ASSERT(ok);
- if (d) {
- const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr;
- *ok = true;
-
- d->initEnums(cache);
-
- int *rv = d->scopedEnumIndex.value(QHashedStringRef(scopedEnumName));
- if (rv) {
- int index = *rv;
- Q_ASSERT(index > -1 && index < d->scopedEnums.count());
- rv = d->scopedEnums.at(index)->value(QHashedStringRef(name));
- if (rv)
- return *rv;
- }
- }
-
- *ok = false;
- return -1;
-}
-
-void QQmlType::refHandle(QQmlTypePrivate *priv)
-{
- if (priv)
- priv->refCount.ref();
-}
-
-void QQmlType::derefHandle(QQmlTypePrivate *priv)
-{
- if (priv && !priv->refCount.deref())
- delete priv;
-}
-
-int QQmlType::refCount(QQmlTypePrivate *priv)
-{
- if (priv)
- return priv->refCount;
- return -1;
-}
-
-namespace {
-template <typename QQmlTypeContainer>
-void removeQQmlTypePrivate(QQmlTypeContainer &container, const QQmlTypePrivate *reference)
-{
- for (typename QQmlTypeContainer::iterator it = container.begin(); it != container.end();) {
- if (*it == reference)
- it = container.erase(it);
- else
- ++it;
- }
-}
-
-struct IsQQmlTypePrivate
-{
- const QQmlTypePrivate *reference;
- explicit IsQQmlTypePrivate(const QQmlTypePrivate *ref) : reference(ref) {}
-
- bool operator()(const QQmlTypePrivate *priv) const { return reference == priv; }
-};
-}
-
-QQmlTypeModule::QQmlTypeModule()
-: d(new QQmlTypeModulePrivate)
-{
-}
-
-QQmlTypeModule::~QQmlTypeModule()
-{
- delete d; d = nullptr;
-}
-
-QString QQmlTypeModule::module() const
-{
- return d->uri.uri;
-}
-
-int QQmlTypeModule::majorVersion() const
-{
- return d->uri.majorVersion;
-}
-
-int QQmlTypeModule::minimumMinorVersion() const
-{
- return d->minMinorVersion;
-}
-
-int QQmlTypeModule::maximumMinorVersion() const
-{
- return d->maxMinorVersion;
-}
-
-void QQmlTypeModulePrivate::add(QQmlTypePrivate *type)
-{
- int minVersion = type->version_min;
- minMinorVersion = qMin(minMinorVersion, minVersion);
- maxMinorVersion = qMax(maxMinorVersion, minVersion);
-
- QList<QQmlTypePrivate *> &list = typeHash[type->elementName];
- for (int ii = 0; ii < list.count(); ++ii) {
- Q_ASSERT(list.at(ii));
- if (list.at(ii)->version_min < minVersion) {
- list.insert(ii, type);
- return;
- }
- }
- list.append(type);
-}
-
-void QQmlTypeModulePrivate::remove(const QQmlTypePrivate *type)
-{
- for (TypeHash::ConstIterator elementIt = typeHash.begin(); elementIt != typeHash.end();) {
- QList<QQmlTypePrivate *> &list = const_cast<QList<QQmlTypePrivate *> &>(elementIt.value());
-
- removeQQmlTypePrivate(list, type);
-
-#if 0
- if (list.isEmpty())
- elementIt = typeHash.erase(elementIt);
- else
- ++elementIt;
-#else
- ++elementIt;
-#endif
- }
-}
-
-QQmlType QQmlTypeModule::type(const QHashedStringRef &name, int minor) const
-{
- QMutexLocker lock(metaTypeDataLock());
-
- QList<QQmlTypePrivate *> *types = d->typeHash.value(name);
- if (types) {
- for (int ii = 0; ii < types->count(); ++ii)
- if (types->at(ii)->version_min <= minor)
- return QQmlType(types->at(ii));
- }
-
- return QQmlType();
-}
-
-QQmlType QQmlTypeModule::type(const QV4::String *name, int minor) const
-{
- QMutexLocker lock(metaTypeDataLock());
-
- QList<QQmlTypePrivate *> *types = d->typeHash.value(name);
- if (types) {
- for (int ii = 0; ii < types->count(); ++ii)
- if (types->at(ii)->version_min <= minor)
- return QQmlType(types->at(ii));
- }
-
- return QQmlType();
-}
-
-void QQmlTypeModule::walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const
-{
- QMutexLocker lock(metaTypeDataLock());
- for (auto typeCandidates = d->typeHash.begin(), end = d->typeHash.end();
- typeCandidates != end; ++typeCandidates) {
- for (auto type: typeCandidates.value()) {
- if (type->regType == QQmlType::CompositeSingletonType)
- callback(QQmlType(type));
- }
- }
-}
-
-QQmlTypeModuleVersion::QQmlTypeModuleVersion()
-: m_module(nullptr), m_minor(0)
-{
-}
-
-QQmlTypeModuleVersion::QQmlTypeModuleVersion(QQmlTypeModule *module, int minor)
-: m_module(module), m_minor(minor)
-{
- Q_ASSERT(m_module);
- Q_ASSERT(m_minor >= 0);
-}
-
-QQmlTypeModuleVersion::QQmlTypeModuleVersion(const QQmlTypeModuleVersion &o)
-: m_module(o.m_module), m_minor(o.m_minor)
-{
-}
-
-QQmlTypeModuleVersion &QQmlTypeModuleVersion::operator=(const QQmlTypeModuleVersion &o)
-{
- m_module = o.m_module;
- m_minor = o.m_minor;
- return *this;
-}
-
-QQmlTypeModule *QQmlTypeModuleVersion::module() const
-{
- return m_module;
-}
-
-int QQmlTypeModuleVersion::minorVersion() const
-{
- return m_minor;
-}
-
-QQmlType QQmlTypeModuleVersion::type(const QHashedStringRef &name) const
-{
- if (!m_module)
- return QQmlType();
- return m_module->type(name, m_minor);
-}
-
-QQmlType QQmlTypeModuleVersion::type(const QV4::String *name) const
-{
- if (!m_module)
- return QQmlType();
- return m_module->type(name, m_minor);
-}
-
-void qmlClearTypeRegistrations() // Declared in qqml.h
+void QQmlMetaType::clearTypeRegistrations()
{
//Only cleans global static, assumed no running engine
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
for (QQmlMetaTypeData::TypeModules::const_iterator i = data->uriToModule.constBegin(), cend = data->uriToModule.constEnd(); i != cend; ++i)
delete *i;
@@ -1630,44 +279,35 @@ void qmlClearTypeRegistrations() // Declared in qqml.h
data->idToType.clear();
data->nameToType.clear();
data->urlToType.clear();
+ data->typePropertyCaches.clear();
data->urlToNonFileImportType.clear();
data->metaObjectToType.clear();
data->uriToModule.clear();
data->undeletableTypes.clear();
-
- QQmlEnginePrivate::baseModulesUninitialized = true; //So the engine re-registers its types
-#if QT_CONFIG(library)
- qmlClearEnginePlugins();
-#endif
-}
-
-static void unregisterAutoParentFunction(const QQmlPrivate::AutoParentFunction &function)
-{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
- data->parentFunctions.removeOne(function);
}
-static int registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &autoparent)
+int QQmlMetaType::registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &autoparent)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
data->parentFunctions.append(autoparent.function);
return data->parentFunctions.count() - 1;
}
-QQmlType registerInterface(const QQmlPrivate::RegisterInterface &interface)
+void QQmlMetaType::unregisterAutoParentFunction(const QQmlPrivate::AutoParentFunction &function)
{
- if (interface.version > 0)
- qFatal("qmlRegisterType(): Cannot mix incompatible QML versions.");
+ QQmlMetaTypeDataPtr data;
+ data->parentFunctions.removeOne(function);
+}
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+QQmlType QQmlMetaType::registerInterface(const QQmlPrivate::RegisterInterface &type)
+{
+ if (type.version > 0)
+ qFatal("qmlRegisterType(): Cannot mix incompatible QML versions.");
- QQmlType type(data, interface);
- QQmlTypePrivate *priv = type.priv();
+ QQmlMetaTypeDataPtr data;
+ QQmlTypePrivate *priv = createQQmlType(data, type);
Q_ASSERT(priv);
data->idToType.insert(priv->typeId, priv);
@@ -1676,14 +316,14 @@ QQmlType registerInterface(const QQmlPrivate::RegisterInterface &interface)
if (!priv->elementName.isEmpty())
data->nameToType.insert(priv->elementName, priv);
- if (data->interfaces.size() <= interface.typeId)
- data->interfaces.resize(interface.typeId + 16);
- if (data->lists.size() <= interface.listId)
- data->lists.resize(interface.listId + 16);
- data->interfaces.setBit(interface.typeId, true);
- data->lists.setBit(interface.listId, true);
+ if (data->interfaces.size() <= type.typeId)
+ data->interfaces.resize(type.typeId + 16);
+ if (data->lists.size() <= type.listId)
+ data->lists.resize(type.listId + 16);
+ data->interfaces.setBit(type.typeId, true);
+ data->lists.setBit(type.listId, true);
- return type;
+ return QQmlType(priv);
}
QString registrationTypeString(QQmlType::RegistrationType typeType)
@@ -1701,7 +341,8 @@ QString registrationTypeString(QQmlType::RegistrationType typeType)
}
// NOTE: caller must hold a QMutexLocker on "data"
-bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *data, const char *uri, const QString &typeName, int majorVersion = -1)
+bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *data,
+ const char *uri, const QString &typeName, int majorVersion = -1)
{
if (!typeName.isEmpty()) {
if (typeName.at(0).isLower()) {
@@ -1736,7 +377,7 @@ bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *da
versionedUri.uri = nameSpace;
versionedUri.majorVersion = majorVersion;
if (QQmlTypeModule* qqtm = data->uriToModule.value(versionedUri, 0)){
- if (QQmlTypeModulePrivate::get(qqtm)->locked){
+ if (qqtm->isLocked()){
QString failure(QCoreApplication::translate("qmlRegisterType",
"Cannot install %1 '%2' into protected module '%3' version '%4'"));
data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType)).arg(typeName).arg(nameSpace).arg(majorVersion));
@@ -1755,8 +396,7 @@ QQmlTypeModule *getTypeModule(const QHashedString &uri, int majorVersion, QQmlMe
QQmlMetaTypeData::VersionedUri versionedUri(uri, majorVersion);
QQmlTypeModule *module = data->uriToModule.value(versionedUri);
if (!module) {
- module = new QQmlTypeModule;
- module->d->uri = versionedUri;
+ module = new QQmlTypeModule(versionedUri.uri, versionedUri.majorVersion);
data->uriToModule.insert(versionedUri, module);
}
return module;
@@ -1792,47 +432,47 @@ void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data)
QQmlTypeModule *module = getTypeModule(mod, type->version_maj, data);
Q_ASSERT(module);
- module->d->add(type);
+ module->add(type);
}
}
-QQmlType registerType(const QQmlPrivate::RegisterType &type)
+QQmlType QQmlMetaType::registerType(const QQmlPrivate::RegisterType &type)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
+
QString elementName = QString::fromUtf8(type.elementName);
if (!checkRegistration(QQmlType::CppType, data, type.uri, elementName, type.versionMajor))
return QQmlType();
- QQmlType dtype(data, elementName, type);
+ QQmlTypePrivate *priv = createQQmlType(data, elementName, type);
- addTypeToData(dtype.priv(), data);
+ addTypeToData(priv, data);
if (!type.typeId)
- data->idToType.insert(dtype.typeId(), dtype.priv());
+ data->idToType.insert(priv->typeId, priv);
- return dtype;
+ return QQmlType(priv);
}
-QQmlType registerSingletonType(const QQmlPrivate::RegisterSingletonType &type)
+QQmlType QQmlMetaType::registerSingletonType(const QQmlPrivate::RegisterSingletonType &type)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
+
QString typeName = QString::fromUtf8(type.typeName);
if (!checkRegistration(QQmlType::SingletonType, data, type.uri, typeName, type.versionMajor))
return QQmlType();
- QQmlType dtype(data, typeName, type);
+ QQmlTypePrivate *priv = createQQmlType(data, typeName, type);
- addTypeToData(dtype.priv(), data);
+ addTypeToData(priv, data);
- return dtype;
+ return QQmlType(priv);
}
QQmlType QQmlMetaType::registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &type)
{
// Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type.
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
+
QString typeName = QString::fromUtf8(type.typeName);
bool fileImport = false;
if (*(type.uri) == '\0')
@@ -1840,21 +480,20 @@ QQmlType QQmlMetaType::registerCompositeSingletonType(const QQmlPrivate::Registe
if (!checkRegistration(QQmlType::CompositeSingletonType, data, fileImport ? nullptr : type.uri, typeName))
return QQmlType();
- QQmlType dtype(data, typeName, type);
-
- addTypeToData(dtype.priv(), data);
+ QQmlTypePrivate *priv = createQQmlType(data, typeName, type);
+ addTypeToData(priv, data);
QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType);
- files->insertMulti(QQmlTypeLoader::normalize(type.url), dtype.priv());
+ files->insertMulti(QQmlTypeLoader::normalize(type.url), priv);
- return dtype;
+ return QQmlType(priv);
}
QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterCompositeType &type)
{
// Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type.
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
+
QString typeName = QString::fromUtf8(type.typeName);
bool fileImport = false;
if (*(type.uri) == '\0')
@@ -1862,16 +501,16 @@ QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterComposit
if (!checkRegistration(QQmlType::CompositeType, data, fileImport?nullptr:type.uri, typeName, type.versionMajor))
return QQmlType();
- QQmlType dtype(data, typeName, type);
- addTypeToData(dtype.priv(), data);
+ QQmlTypePrivate *priv = createQQmlType(data, typeName, type);
+ addTypeToData(priv, data);
QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType);
- files->insertMulti(QQmlTypeLoader::normalize(type.url), dtype.priv());
+ files->insertMulti(QQmlTypeLoader::normalize(type.url), priv);
- return dtype;
+ return QQmlType(priv);
}
-void QQmlMetaType::registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit)
+void QQmlMetaType::registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit)
{
QByteArray name = compilationUnit->rootPropertyCache()->className();
@@ -1894,125 +533,61 @@ void QQmlMetaType::registerInternalCompositeType(QV4::CompiledData::CompilationU
compilationUnit->metaTypeId = ptr_type;
compilationUnit->listMetaTypeId = lst_type;
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *d = metaTypeData();
- d->qmlLists.insert(lst_type, ptr_type);
+ QQmlMetaTypeDataPtr data;
+ data->qmlLists.insert(lst_type, ptr_type);
}
-void QQmlMetaType::unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit)
+void QQmlMetaType::unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit)
{
int ptr_type = compilationUnit->metaTypeId;
int lst_type = compilationUnit->listMetaTypeId;
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *d = metaTypeData();
- d->qmlLists.remove(lst_type);
+ QQmlMetaTypeDataPtr data;
+ data->qmlLists.remove(lst_type);
QMetaType::unregisterType(ptr_type);
QMetaType::unregisterType(lst_type);
}
-int registerQmlUnitCacheHook(const QQmlPrivate::RegisterQmlUnitCacheHook &hookRegistration)
+int QQmlMetaType::registerUnitCacheHook(
+ const QQmlPrivate::RegisterQmlUnitCacheHook &hookRegistration)
{
if (hookRegistration.version > 0)
qFatal("qmlRegisterType(): Cannot mix incompatible QML versions.");
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+
+ QQmlMetaTypeDataPtr data;
data->lookupCachedQmlUnit << hookRegistration.lookupCachedQmlUnit;
return 0;
}
-/*
-This method is "over generalized" to allow us to (potentially) register more types of things in
-the future without adding exported symbols.
-*/
-int QQmlPrivate::qmlregister(RegistrationType type, void *data)
-{
- if (type == AutoParentRegistration)
- return registerAutoParentFunction(*reinterpret_cast<RegisterAutoParent *>(data));
- else if (type == QmlUnitCacheHookRegistration)
- return registerQmlUnitCacheHook(*reinterpret_cast<RegisterQmlUnitCacheHook *>(data));
-
- QQmlType dtype;
- if (type == TypeRegistration)
- dtype = registerType(*reinterpret_cast<RegisterType *>(data));
- else if (type == InterfaceRegistration)
- dtype = registerInterface(*reinterpret_cast<RegisterInterface *>(data));
- else if (type == SingletonRegistration)
- dtype = registerSingletonType(*reinterpret_cast<RegisterSingletonType *>(data));
- else if (type == CompositeRegistration)
- dtype = QQmlMetaType::registerCompositeType(*reinterpret_cast<RegisterCompositeType *>(data));
- else if (type == CompositeSingletonRegistration)
- dtype = QQmlMetaType::registerCompositeSingletonType(*reinterpret_cast<RegisterCompositeSingletonType *>(data));
- else
- return -1;
-
- if (!dtype.isValid())
- return -1;
-
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *typeData = metaTypeData();
- typeData->undeletableTypes.insert(dtype);
-
- return dtype.index();
-}
-
-void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data)
-{
- switch (type) {
- case AutoParentRegistration:
- unregisterAutoParentFunction(reinterpret_cast<AutoParentFunction>(data));
- break;
- case QmlUnitCacheHookRegistration:
- QQmlMetaType::removeCachedUnitLookupFunction(
- reinterpret_cast<QmlUnitCacheLookupFunction>(data));
- break;
- case TypeRegistration:
- case InterfaceRegistration:
- case SingletonRegistration:
- case CompositeRegistration:
- case CompositeSingletonRegistration:
- qmlUnregisterType(data);
- break;
- }
-}
-
-//From qqml.h
-bool qmlProtectModule(const char *uri, int majVersion)
+bool QQmlMetaType::protectModule(const char *uri, int majVersion)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
QQmlMetaTypeData::VersionedUri versionedUri;
versionedUri.uri = QString::fromUtf8(uri);
versionedUri.majorVersion = majVersion;
if (QQmlTypeModule* qqtm = data->uriToModule.value(versionedUri, 0)) {
- QQmlTypeModulePrivate::get(qqtm)->locked = true;
+ qqtm->lock();
return true;
}
return false;
}
-//From qqml.h
-void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor)
+void QQmlMetaType::registerModule(const char *uri, int versionMajor, int versionMinor)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
QQmlTypeModule *module = getTypeModule(QString::fromUtf8(uri), versionMajor, data);
Q_ASSERT(module);
- QQmlTypeModulePrivate *p = QQmlTypeModulePrivate::get(module);
- p->minMinorVersion = qMin(p->minMinorVersion, versionMinor);
- p->maxMinorVersion = qMax(p->maxMinorVersion, versionMinor);
+ module->addMinorVersion(versionMinor);
}
-//From qqml.h
-int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
+int QQmlMetaType::typeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
QQmlTypeModule *module = getTypeModule(QString::fromUtf8(uri), versionMajor, data);
if (!module)
@@ -2025,34 +600,218 @@ int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *q
return type.index();
}
-bool QQmlMetaType::namespaceContainsRegistrations(const QString &uri, int majorVersion)
+void QQmlMetaType::registerUndeletableType(const QQmlType &dtype)
{
- const QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
+ data->undeletableTypes.insert(dtype);
+}
+static bool namespaceContainsRegistrations(const QQmlMetaTypeData *data, const QString &uri,
+ int majorVersion)
+{
// Has any type previously been installed to this namespace?
QHashedString nameSpace(uri);
- for (const QQmlType &type : data->types)
+ for (const QQmlType &type : data->types) {
if (type.module() == nameSpace && type.majorVersion() == majorVersion)
return true;
+ }
return false;
}
-void QQmlMetaType::protectNamespace(const QString &uri)
+class QQmlMetaTypeRegistrationFailureRecorder
+{
+ Q_DISABLE_COPY_MOVE(QQmlMetaTypeRegistrationFailureRecorder)
+public:
+ QQmlMetaTypeRegistrationFailureRecorder(QQmlMetaTypeData *data, QStringList *failures)
+ : data(data)
+ {
+ data->setTypeRegistrationFailures(failures);
+ }
+
+ ~QQmlMetaTypeRegistrationFailureRecorder()
+ {
+ data->setTypeRegistrationFailures(nullptr);
+ }
+
+ QQmlMetaTypeData *data = nullptr;
+};
+
+
+bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePath,
+ const QString &uri, const QString &typeNamespace, int vmaj,
+ QList<QQmlError> *errors)
{
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlTypesExtensionInterface *iface = qobject_cast<QQmlTypesExtensionInterface *>(instance);
+ if (!iface) {
+ if (errors) {
+ QQmlError error;
+ error.setDescription(QStringLiteral("Module loaded for URI '%1' does not implement "
+ "QQmlTypesExtensionInterface").arg(typeNamespace));
+ errors->prepend(error);
+ }
+ return false;
+ }
+
+ if (!typeNamespace.isEmpty() && typeNamespace != uri) {
+ // This is an 'identified' module
+ // The namespace for type registrations must match the URI for locating the module
+ if (errors) {
+ QQmlError error;
+ error.setDescription(
+ QStringLiteral("Module namespace '%1' does not match import URI '%2'")
+ .arg(typeNamespace).arg(uri));
+ errors->prepend(error);
+ }
+ return false;
+ }
+
+ QStringList failures;
+ QQmlMetaTypeDataPtr data;
+ {
+ QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures);
+ if (!typeNamespace.isEmpty()) {
+ // This is an 'identified' module
+ if (namespaceContainsRegistrations(data, typeNamespace, vmaj)) {
+ // Other modules have already installed to this namespace
+ if (errors) {
+ QQmlError error;
+ error.setDescription(QStringLiteral("Namespace '%1' has already been used "
+ "for type registration")
+ .arg(typeNamespace));
+ errors->prepend(error);
+ }
+ return false;
+ }
+
+ data->protectedNamespaces.insert(uri);
+ } else {
+ // This is not an identified module - provide a warning
+ qWarning().nospace() << qPrintable(
+ QStringLiteral("Module '%1' does not contain a module identifier directive - "
+ "it cannot be protected from external registrations.").arg(uri));
+ }
+
+ if (auto *plugin = qobject_cast<QQmlExtensionPlugin *>(instance)) {
+ // basepath should point to the directory of the module, not the plugin file itself:
+ QQmlExtensionPluginPrivate::get(plugin)->baseUrl
+ = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath);
+ }
+
+ data->typeRegistrationNamespace = typeNamespace;
+ const QByteArray bytes = uri.toUtf8();
+ const char *moduleId = bytes.constData();
+ iface->registerTypes(moduleId);
+ data->typeRegistrationNamespace.clear();
+ }
+
+ if (!failures.isEmpty()) {
+ if (errors) {
+ for (const QString &failure : qAsConst(failures)) {
+ QQmlError error;
+ error.setDescription(failure);
+ errors->prepend(error);
+ }
+ }
+ return false;
+ }
- data->protectedNamespaces.insert(uri);
+ return true;
}
-void QQmlMetaType::setTypeRegistrationNamespace(const QString &uri)
+/*
+ \internal
+
+ Fetches the QQmlType instance registered for \a urlString, creating a
+ registration for it if it is not already registered, using the associated
+ \a typeName, \a isCompositeSingleton, \a majorVersion and \a minorVersion
+ details.
+
+ Errors (if there are any) are placed into \a errors, if it is nonzero.
+ Otherwise errors are printed as warnings.
+*/
+QQmlType QQmlMetaType::typeForUrl(const QString &urlString,
+ const QHashedStringRef &qualifiedType,
+ bool isCompositeSingleton, QList<QQmlError> *errors,
+ int majorVersion, int minorVersion)
{
- QQmlMetaTypeData *data = metaTypeData();
+ // ### unfortunate (costly) conversion
+ const QUrl url = QQmlTypeLoader::normalize(QUrl(urlString));
- data->typeRegistrationNamespace = uri;
+ QQmlMetaTypeDataPtr data;
+ {
+ QQmlType ret(data->urlToType.value(url));
+ if (ret.isValid() && ret.sourceUrl() == url)
+ return ret;
+ }
+ {
+ QQmlType ret(data->urlToNonFileImportType.value(url));
+ if (ret.isValid() && ret.sourceUrl() == url)
+ return ret;
+ }
+
+ const int dot = qualifiedType.indexOf(QLatin1Char('.'));
+ const QString typeName = dot < 0
+ ? qualifiedType.toString()
+ : QString(qualifiedType.constData() + dot + 1, qualifiedType.length() - dot - 1);
+
+ QStringList failures;
+ QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures);
+
+ // Register the type. Note that the URI parameters here are empty; for
+ // file type imports, we do not place them in a URI as we don't
+ // necessarily have a good and unique one (picture a library import,
+ // which may be found in multiple plugin locations on disk), but there
+ // are other reasons for this too.
+ //
+ // By not putting them in a URI, we prevent the types from being
+ // registered on a QQmlTypeModule; this is important, as once types are
+ // placed on there, they cannot be easily removed, meaning if the
+ // developer subsequently loads a different import (meaning different
+ // types) with the same URI (using, say, a different plugin path), it is
+ // very undesirable that we continue to associate the types from the
+ // "old" URI with that new module.
+ //
+ // Not having URIs also means that the types cannot be found by name
+ // etc, the only way to look them up is through QQmlImports -- for
+ // better or worse.
+ const QQmlType::RegistrationType registrationType = isCompositeSingleton
+ ? QQmlType::CompositeSingletonType
+ : QQmlType::CompositeType;
+ if (checkRegistration(registrationType, data, nullptr, typeName, majorVersion)) {
+ auto *priv = new QQmlTypePrivate(registrationType);
+ priv->setName(QString(), typeName);
+ priv->version_maj = majorVersion;
+ priv->version_min = minorVersion;
+
+ if (isCompositeSingleton) {
+ priv->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo;
+ priv->extraData.sd->singletonInstanceInfo->url = url;
+ priv->extraData.sd->singletonInstanceInfo->typeName = typeName;
+ } else {
+ priv->extraData.fd->url = url;
+ }
+
+ data->registerType(priv);
+ addTypeToData(priv, data);
+ data->urlToType.insertMulti(url, priv);
+ return QQmlType(priv);
+ }
+
+ // This means that the type couldn't be found by URL, but could not be
+ // registered either, meaning we most likely were passed some kind of bad
+ // data.
+ if (errors) {
+ QQmlError error;
+ error.setDescription(failures.join('\n'));
+ errors->prepend(error);
+ } else {
+ qWarning("%s", failures.join('\n').toLatin1().constData());
+ }
+ return QQmlType();
}
-QMutex *QQmlMetaType::typeRegistrationLock()
+QRecursiveMutex *QQmlMetaType::typeRegistrationLock()
{
return metaTypeDataLock();
}
@@ -2062,8 +821,7 @@ QMutex *QQmlMetaType::typeRegistrationLock()
*/
bool QQmlMetaType::isAnyModule(const QString &uri)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
for (QQmlMetaTypeData::TypeModules::ConstIterator iter = data->uriToModule.cbegin();
iter != data->uriToModule.cend(); ++iter) {
@@ -2079,14 +837,13 @@ bool QQmlMetaType::isAnyModule(const QString &uri)
*/
bool QQmlMetaType::isLockedModule(const QString &uri, int majVersion)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
QQmlMetaTypeData::VersionedUri versionedUri;
versionedUri.uri = uri;
versionedUri.majorVersion = majVersion;
if (QQmlTypeModule* qqtm = data->uriToModule.value(versionedUri, 0))
- return QQmlTypeModulePrivate::get(qqtm)->locked;
+ return qqtm->isLocked();
return false;
}
@@ -2100,9 +857,7 @@ bool QQmlMetaType::isLockedModule(const QString &uri, int majVersion)
bool QQmlMetaType::isModule(const QString &module, int versionMajor, int versionMinor)
{
Q_ASSERT(versionMajor >= 0 && versionMinor >= 0);
- QMutexLocker lock(metaTypeDataLock());
-
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
// first, check Types
QQmlTypeModule *tm =
@@ -2115,15 +870,13 @@ bool QQmlMetaType::isModule(const QString &module, int versionMajor, int version
QQmlTypeModule *QQmlMetaType::typeModule(const QString &uri, int majorVersion)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
return data->uriToModule.value(QQmlMetaTypeData::VersionedUri(uri, majorVersion));
}
QList<QQmlPrivate::AutoParentFunction> QQmlMetaType::parentFunctions()
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
return data->parentFunctions;
}
@@ -2144,8 +897,7 @@ bool QQmlMetaType::isQObject(int userType)
if (userType == QMetaType::QObjectStar)
return true;
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
return userType >= 0 && userType < data->objects.size() && data->objects.testBit(userType);
}
@@ -2154,8 +906,7 @@ bool QQmlMetaType::isQObject(int userType)
*/
int QQmlMetaType::listType(int id)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
QHash<int, int>::ConstIterator iter = data->qmlLists.constFind(id);
if (iter != data->qmlLists.cend())
return *iter;
@@ -2166,10 +917,10 @@ int QQmlMetaType::listType(int id)
return 0;
}
+#if QT_DEPRECATED_SINCE(5, 14)
int QQmlMetaType::attachedPropertiesFuncId(QQmlEnginePrivate *engine, const QMetaObject *mo)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
for (auto it = data->metaObjectToType.constFind(mo), end = data->metaObjectToType.constEnd();
it != end && it.key() == mo; ++it) {
@@ -2185,16 +936,15 @@ QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFuncById(QQmlEnginePr
{
if (id < 0)
return nullptr;
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
return data->types.at(id).attachedPropertiesFunction(engine);
}
+#endif
QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFunc(QQmlEnginePrivate *engine,
const QMetaObject *mo)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
QQmlType type(data->metaObjectToType.value(mo));
return type.attachedPropertiesFunction(engine);
@@ -2259,8 +1009,7 @@ QQmlMetaType::TypeCategory QQmlMetaType::typeCategory(int userType)
if (userType == QMetaType::QObjectStar)
return Object;
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
if (data->qmlLists.contains(userType))
return List;
else if (userType < data->objects.size() && data->objects.testBit(userType))
@@ -2276,17 +1025,20 @@ QQmlMetaType::TypeCategory QQmlMetaType::typeCategory(int userType)
*/
bool QQmlMetaType::isInterface(int userType)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ const QQmlMetaTypeDataPtr data;
return userType >= 0 && userType < data->interfaces.size() && data->interfaces.testBit(userType);
}
const char *QQmlMetaType::interfaceIId(int userType)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
- QQmlType type(data->idToType.value(userType));
- lock.unlock();
+
+ QQmlTypePrivate *typePrivate = nullptr;
+ {
+ QQmlMetaTypeDataPtr data;
+ typePrivate = data->idToType.value(userType);
+ }
+
+ QQmlType type(typePrivate);
if (type.isInterface() && type.typeId() == userType)
return type.interfaceIId();
else
@@ -2295,8 +1047,7 @@ const char *QQmlMetaType::interfaceIId(int userType)
bool QQmlMetaType::isList(int userType)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ const QQmlMetaTypeDataPtr data;
if (data->qmlLists.contains(userType))
return true;
return userType >= 0 && userType < data->lists.size() && data->lists.testBit(userType);
@@ -2319,9 +1070,7 @@ bool QQmlMetaType::isList(int userType)
*/
void QQmlMetaType::registerCustomStringConverter(int type, StringConverter converter)
{
- QMutexLocker lock(metaTypeDataLock());
-
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
if (data->stringConverters.contains(type))
return;
data->stringConverters.insert(type, converter);
@@ -2333,9 +1082,7 @@ void QQmlMetaType::registerCustomStringConverter(int type, StringConverter conve
*/
QQmlMetaType::StringConverter QQmlMetaType::customStringConverter(int type)
{
- QMutexLocker lock(metaTypeDataLock());
-
- QQmlMetaTypeData *data = metaTypeData();
+ const QQmlMetaTypeDataPtr data;
return data->stringConverters.value(type);
}
@@ -2362,8 +1109,7 @@ QQmlType QQmlMetaType::qmlType(const QString &qualifiedName, int version_major,
QQmlType QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedStringRef &module, int version_major, int version_minor)
{
Q_ASSERT(version_major >= 0 && version_minor >= 0);
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ const QQmlMetaTypeDataPtr data;
QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.constFind(name);
while (it != data->nameToType.cend() && it.key() == name) {
@@ -2383,9 +1129,7 @@ QQmlType QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedString
*/
QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
-
+ const QQmlMetaTypeDataPtr data;
return QQmlType(data->metaObjectToType.value(metaObject));
}
@@ -2397,8 +1141,7 @@ QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject)
QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStringRef &module, int version_major, int version_minor)
{
Q_ASSERT(version_major >= 0 && version_minor >= 0);
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ const QQmlMetaTypeDataPtr data;
QQmlMetaTypeData::MetaObjects::const_iterator it = data->metaObjectToType.constFind(metaObject);
while (it != data->metaObjectToType.cend() && it.key() == metaObject) {
@@ -2418,8 +1161,7 @@ QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStrin
*/
QQmlType QQmlMetaType::qmlType(int typeId, TypeIdCategory category)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ const QQmlMetaTypeDataPtr data;
if (category == TypeIdCategory::MetaType) {
QQmlTypePrivate *type = data->idToType.value(typeId);
@@ -2442,8 +1184,7 @@ QQmlType QQmlMetaType::qmlType(int typeId, TypeIdCategory category)
QQmlType QQmlMetaType::qmlType(const QUrl &unNormalizedUrl, bool includeNonFileImports /* = false */)
{
const QUrl url = QQmlTypeLoader::normalize(unNormalizedUrl);
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ const QQmlMetaTypeDataPtr data;
QQmlType type(data->urlToType.value(url));
if (!type.isValid() && includeNonFileImports)
@@ -2455,221 +1196,87 @@ QQmlType QQmlMetaType::qmlType(const QUrl &unNormalizedUrl, bool includeNonFileI
return QQmlType();
}
-QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QMetaObject *metaObject, int minorVersion)
-{
- if (QQmlPropertyCache *rv = propertyCaches.value(metaObject))
- return rv;
-
- if (!metaObject->superClass()) {
- QQmlPropertyCache *rv = new QQmlPropertyCache(metaObject);
- propertyCaches.insert(metaObject, rv);
- return rv;
- }
- QQmlPropertyCache *super = propertyCache(metaObject->superClass(), minorVersion);
- QQmlPropertyCache *rv = super->copyAndAppend(metaObject, minorVersion);
- propertyCaches.insert(metaObject, rv);
- return rv;
-}
-
QQmlPropertyCache *QQmlMetaType::propertyCache(const QMetaObject *metaObject, int minorVersion)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data; // not const: the cache is created on demand
return data->propertyCache(metaObject, minorVersion);
}
-QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QQmlType &type, int minorVersion)
-{
- Q_ASSERT(type.isValid());
-
- if (QQmlPropertyCache *pc = type.key()->propertyCacheForMinorVersion(minorVersion))
- return pc;
-
- QVector<QQmlType> types;
-
- int maxMinorVersion = 0;
-
- const QMetaObject *metaObject = type.metaObject();
-
- while (metaObject) {
- QQmlType t = QQmlMetaType::qmlType(metaObject, type.module(), type.majorVersion(), minorVersion);
- if (t.isValid()) {
- maxMinorVersion = qMax(maxMinorVersion, t.minorVersion());
- types << t;
- } else {
- types << QQmlType();
- }
-
- metaObject = metaObject->superClass();
- }
-
- if (QQmlPropertyCache *pc = type.key()->propertyCacheForMinorVersion(maxMinorVersion)) {
- const_cast<QQmlTypePrivate*>(type.key())->setPropertyCacheForMinorVersion(minorVersion, pc);
- return pc;
- }
-
- QQmlPropertyCache *raw = propertyCache(type.metaObject(), minorVersion);
-
- bool hasCopied = false;
-
- for (int ii = 0; ii < types.count(); ++ii) {
- QQmlType currentType = types.at(ii);
- if (!currentType.isValid())
- continue;
-
- int rev = currentType.metaObjectRevision();
- int moIndex = types.count() - 1 - ii;
-
- if (raw->allowedRevisionCache[moIndex] != rev) {
- if (!hasCopied) {
- raw = raw->copy();
- hasCopied = true;
- }
- raw->allowedRevisionCache[moIndex] = rev;
- }
- }
-
- // Test revision compatibility - the basic rule is:
- // * Anything that is excluded, cannot overload something that is not excluded *
-
- // Signals override:
- // * other signals and methods of the same name.
- // * properties named on<Signal Name>
- // * automatic <property name>Changed notify signals
-
- // Methods override:
- // * other methods of the same name
-
- // Properties override:
- // * other elements of the same name
-
-#if 0
- bool overloadError = false;
- QString overloadName;
-
- for (QQmlPropertyCache::StringCache::ConstIterator iter = raw->stringCache.begin();
- !overloadError && iter != raw->stringCache.end();
- ++iter) {
-
- QQmlPropertyData *d = *iter;
- if (raw->isAllowedInRevision(d))
- continue; // Not excluded - no problems
-
- // check that a regular "name" overload isn't happening
- QQmlPropertyData *current = d;
- while (!overloadError && current) {
- current = d->overrideData(current);
- if (current && raw->isAllowedInRevision(current))
- overloadError = true;
- }
- }
-
- if (overloadError) {
- if (hasCopied) raw->release();
-
- error.setDescription(QLatin1String("Type ") + type.qmlTypeName() + QLatin1Char(' ') + QString::number(type.majorVersion()) + QLatin1Char('.') + QString::number(minorVersion) + QLatin1String(" contains an illegal property \"") + overloadName + QLatin1String("\". This is an error in the type's implementation."));
- return 0;
- }
-#endif
-
- const_cast<QQmlTypePrivate*>(type.key())->setPropertyCacheForMinorVersion(minorVersion, raw);
-
- if (hasCopied)
- raw->release();
-
- if (minorVersion != maxMinorVersion)
- const_cast<QQmlTypePrivate*>(type.key())->setPropertyCacheForMinorVersion(maxMinorVersion, raw);
-
- return raw;
-}
-
QQmlPropertyCache *QQmlMetaType::propertyCache(const QQmlType &type, int minorVersion)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data; // not const: the cache is created on demand
return data->propertyCache(type, minorVersion);
}
-void qmlUnregisterType(int typeIndex)
+void QQmlMetaType::unregisterType(int typeIndex)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
- {
- const QQmlType type = data->types.value(typeIndex);
- const QQmlTypePrivate *d = type.priv();
- if (d) {
- removeQQmlTypePrivate(data->idToType, d);
- removeQQmlTypePrivate(data->nameToType, d);
- removeQQmlTypePrivate(data->urlToType, d);
- removeQQmlTypePrivate(data->urlToNonFileImportType, d);
- removeQQmlTypePrivate(data->metaObjectToType, d);
- for (QQmlMetaTypeData::TypeModules::Iterator module = data->uriToModule.begin(); module != data->uriToModule.end(); ++module) {
- QQmlTypeModulePrivate *modulePrivate = (*module)->priv();
- modulePrivate->remove(d);
- }
- data->types[typeIndex] = QQmlType();
- data->undeletableTypes.remove(type);
- }
+ QQmlMetaTypeDataPtr data;
+ const QQmlType type = data->types.value(typeIndex);
+ if (const QQmlTypePrivate *d = type.priv()) {
+ removeQQmlTypePrivate(data->idToType, d);
+ removeQQmlTypePrivate(data->nameToType, d);
+ removeQQmlTypePrivate(data->urlToType, d);
+ removeQQmlTypePrivate(data->urlToNonFileImportType, d);
+ removeQQmlTypePrivate(data->metaObjectToType, d);
+ for (auto & module : data->uriToModule)
+ module->remove(d);
+ data->clearPropertyCachesForMinorVersion(typeIndex);
+ data->types[typeIndex] = QQmlType();
+ data->undeletableTypes.remove(type);
}
}
void QQmlMetaType::freeUnusedTypesAndCaches()
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
// in case this is being called during program exit, `data` might be destructed already
- if (!data)
+ if (!data.isValid())
return;
- {
- bool deletedAtLeastOneType;
- do {
- deletedAtLeastOneType = false;
- QList<QQmlType>::Iterator it = data->types.begin();
- while (it != data->types.end()) {
- const QQmlTypePrivate *d = (*it).priv();
- if (d && d->refCount == 1) {
- deletedAtLeastOneType = true;
-
- removeQQmlTypePrivate(data->idToType, d);
- removeQQmlTypePrivate(data->nameToType, d);
- removeQQmlTypePrivate(data->urlToType, d);
- removeQQmlTypePrivate(data->urlToNonFileImportType, d);
- removeQQmlTypePrivate(data->metaObjectToType, d);
-
- for (QQmlMetaTypeData::TypeModules::Iterator module = data->uriToModule.begin(); module != data->uriToModule.end(); ++module) {
- QQmlTypeModulePrivate *modulePrivate = (*module)->priv();
- modulePrivate->remove(d);
- }
-
- *it = QQmlType();
- } else {
- ++it;
- }
+ bool deletedAtLeastOneType;
+ do {
+ deletedAtLeastOneType = false;
+ QList<QQmlType>::Iterator it = data->types.begin();
+ while (it != data->types.end()) {
+ const QQmlTypePrivate *d = (*it).priv();
+ if (d && d->count() == 1) {
+ deletedAtLeastOneType = true;
+
+ removeQQmlTypePrivate(data->idToType, d);
+ removeQQmlTypePrivate(data->nameToType, d);
+ removeQQmlTypePrivate(data->urlToType, d);
+ removeQQmlTypePrivate(data->urlToNonFileImportType, d);
+ removeQQmlTypePrivate(data->metaObjectToType, d);
+
+ for (auto &module : data->uriToModule)
+ module->remove(d);
+
+ data->clearPropertyCachesForMinorVersion(d->index);
+ *it = QQmlType();
+ } else {
+ ++it;
}
- } while (deletedAtLeastOneType);
- }
-
- {
- bool deletedAtLeastOneCache;
- do {
- deletedAtLeastOneCache = false;
- QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator it = data->propertyCaches.begin();
- while (it != data->propertyCaches.end()) {
-
- if ((*it)->count() == 1) {
- QQmlPropertyCache *pc = nullptr;
- qSwap(pc, *it);
- it = data->propertyCaches.erase(it);
- pc->release();
- deletedAtLeastOneCache = true;
- } else {
- ++it;
- }
+ }
+ } while (deletedAtLeastOneType);
+
+ bool deletedAtLeastOneCache;
+ do {
+ deletedAtLeastOneCache = false;
+ QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator it = data->propertyCaches.begin();
+ while (it != data->propertyCaches.end()) {
+
+ if ((*it)->count() == 1) {
+ QQmlPropertyCache *pc = nullptr;
+ qSwap(pc, *it);
+ it = data->propertyCaches.erase(it);
+ pc->release();
+ deletedAtLeastOneCache = true;
+ } else {
+ ++it;
}
- } while (deletedAtLeastOneCache);
- }
+ }
+ } while (deletedAtLeastOneCache);
}
/*!
@@ -2677,8 +1284,7 @@ void QQmlMetaType::freeUnusedTypesAndCaches()
*/
QList<QString> QQmlMetaType::qmlTypeNames()
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ const QQmlMetaTypeDataPtr data;
QList<QString> names;
names.reserve(data->nameToType.count());
@@ -2697,8 +1303,7 @@ QList<QString> QQmlMetaType::qmlTypeNames()
*/
QList<QQmlType> QQmlMetaType::qmlTypes()
{
- QMutexLocker lock(metaTypeDataLock());
- const QQmlMetaTypeData *data = metaTypeData();
+ const QQmlMetaTypeDataPtr data;
QList<QQmlType> types;
for (QQmlTypePrivate *t : data->nameToType)
@@ -2712,9 +1317,7 @@ QList<QQmlType> QQmlMetaType::qmlTypes()
*/
QList<QQmlType> QQmlMetaType::qmlAllTypes()
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
-
+ const QQmlMetaTypeDataPtr data;
return data->types;
}
@@ -2723,8 +1326,7 @@ QList<QQmlType> QQmlMetaType::qmlAllTypes()
*/
QList<QQmlType> QQmlMetaType::qmlSingletonTypes()
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ const QQmlMetaTypeDataPtr data;
QList<QQmlType> retn;
for (const auto t : qAsConst(data->nameToType)) {
@@ -2737,13 +1339,12 @@ QList<QQmlType> QQmlMetaType::qmlSingletonTypes()
const QV4::CompiledData::Unit *QQmlMetaType::findCachedCompilationUnit(const QUrl &uri, CachedUnitLookupError *status)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ const QQmlMetaTypeDataPtr data;
for (const auto lookup : qAsConst(data->lookupCachedQmlUnit)) {
if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) {
QString error;
- if (!unit->qmlData->verifyHeader(QDateTime(), &error)) {
+ if (!QV4::ExecutableCompilationUnit::verifyHeader(unit->qmlData, QDateTime(), &error)) {
qCDebug(DBG_DISK_CACHE) << "Error loading pre-compiled file " << uri << ":" << error;
if (status)
*status = CachedUnitLookupError::VersionMismatch;
@@ -2763,15 +1364,13 @@ const QV4::CompiledData::Unit *QQmlMetaType::findCachedCompilationUnit(const QUr
void QQmlMetaType::prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
data->lookupCachedQmlUnit.prepend(handler);
}
void QQmlMetaType::removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler)
{
- QMutexLocker lock(metaTypeDataLock());
- QQmlMetaTypeData *data = metaTypeData();
+ QQmlMetaTypeDataPtr data;
data->lookupCachedQmlUnit.removeAll(handler);
}
@@ -2817,4 +1416,38 @@ QString QQmlMetaType::prettyTypeName(const QObject *object)
return typeName;
}
+QList<QQmlProxyMetaObject::ProxyData> QQmlMetaType::proxyData(const QMetaObject *mo,
+ const QMetaObject *baseMetaObject,
+ QMetaObject *lastMetaObject)
+{
+ QList<QQmlProxyMetaObject::ProxyData> metaObjects;
+ mo = mo->d.superdata;
+
+ const QQmlMetaTypeDataPtr data;
+
+ while (mo) {
+ QQmlTypePrivate *t = data->metaObjectToType.value(mo);
+ if (t) {
+ if (t->regType == QQmlType::CppType) {
+ if (t->extraData.cd->extFunc) {
+ QMetaObjectBuilder builder;
+ clone(builder, t->extraData.cd->extMetaObject, t->baseMetaObject, baseMetaObject);
+ builder.setFlags(QMetaObjectBuilder::DynamicMetaObject);
+ QMetaObject *mmo = builder.toMetaObject();
+ mmo->d.superdata = baseMetaObject;
+ if (!metaObjects.isEmpty())
+ metaObjects.constLast().metaObject->d.superdata = mmo;
+ else if (lastMetaObject)
+ lastMetaObject->d.superdata = mmo;
+ QQmlProxyMetaObject::ProxyData data = { mmo, t->extraData.cd->extFunc, 0, 0 };
+ metaObjects << data;
+ }
+ }
+ }
+ mo = mo->d.superdata;
+ }
+
+ return metaObjects;
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h
index abc79e50e2..6c2b0bb2a6 100644
--- a/src/qml/qml/qqmlmetatype_p.h
+++ b/src/qml/qml/qqmlmetatype_p.h
@@ -51,40 +51,44 @@
// We mean it.
//
-#include "qqml.h"
#include <private/qtqmlglobal_p.h>
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qvariant.h>
-#include <QtCore/qbitarray.h>
-#include <QtQml/qjsvalue.h>
+#include <private/qqmltype_p.h>
+#include <private/qqmlproxymetaobject_p.h>
QT_BEGIN_NAMESPACE
-class QQmlType;
-class QQmlEngine;
-class QQmlEnginePrivate;
-class QQmlCustomParser;
-class QQmlTypePrivate;
class QQmlTypeModule;
-class QHashedString;
-class QHashedStringRef;
-class QMutex;
-class QQmlPropertyCache;
-class QQmlCompiledData;
-
-namespace QV4 { struct String; }
+class QRecursiveMutex;
+class QQmlError;
-void Q_QML_PRIVATE_EXPORT qmlUnregisterType(int type);
+namespace QV4 { class ExecutableCompilationUnit; }
class Q_QML_PRIVATE_EXPORT QQmlMetaType
{
public:
+ static QQmlType registerType(const QQmlPrivate::RegisterType &type);
+ static QQmlType registerInterface(const QQmlPrivate::RegisterInterface &type);
+ static QQmlType registerSingletonType(const QQmlPrivate::RegisterSingletonType &type);
static QQmlType registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &type);
static QQmlType registerCompositeType(const QQmlPrivate::RegisterCompositeType &type);
+ static bool registerPluginTypes(QObject *instance, const QString &basePath,
+ const QString &uri, const QString &typeNamespace, int vmaj,
+ QList<QQmlError> *errors);
+ static QQmlType typeForUrl(const QString &urlString, const QHashedStringRef& typeName,
+ bool isCompositeSingleton, QList<QQmlError> *errors,
+ int majorVersion = -1, int minorVersion = -1);
+
+ static void unregisterType(int type);
+
+ static void registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit);
+ static void unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit);
- static void registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit);
- static void unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit);
+ static void registerModule(const char *uri, int versionMajor, int versionMinor);
+ static bool protectModule(const char *uri, int majVersion);
+
+ static int typeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
+
+ static void registerUndeletableType(const QQmlType &dtype);
static QList<QString> qmlTypeNames();
static QList<QQmlType> qmlTypes();
@@ -117,8 +121,12 @@ public:
static QObject *toQObject(const QVariant &, bool *ok = nullptr);
static int listType(int);
- static int attachedPropertiesFuncId(QQmlEnginePrivate *engine, const QMetaObject *);
- static QQmlAttachedPropertiesFunc attachedPropertiesFuncById(QQmlEnginePrivate *, int);
+#if QT_DEPRECATED_SINCE(5, 14)
+ static QT_DEPRECATED int attachedPropertiesFuncId(QQmlEnginePrivate *engine,
+ const QMetaObject *);
+ static QT_DEPRECATED QQmlAttachedPropertiesFunc attachedPropertiesFuncById(QQmlEnginePrivate *,
+ int);
+#endif
static QQmlAttachedPropertiesFunc attachedPropertiesFunc(QQmlEnginePrivate *,
const QMetaObject *);
@@ -152,228 +160,38 @@ public:
static void prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler);
static void removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler);
- static bool namespaceContainsRegistrations(const QString &, int majorVersion);
-
- static void protectNamespace(const QString &);
-
- static void setTypeRegistrationNamespace(const QString &);
-
- static QMutex *typeRegistrationLock();
+ static QRecursiveMutex *typeRegistrationLock();
static QString prettyTypeName(const QObject *object);
-};
-struct QQmlMetaTypeData;
-class QHashedCStringRef;
-class QQmlPropertyCache;
-class Q_QML_PRIVATE_EXPORT QQmlType
-{
-public:
- QQmlType();
- QQmlType(const QQmlType &other);
- QQmlType &operator =(const QQmlType &other);
- explicit QQmlType(QQmlTypePrivate *priv);
- ~QQmlType();
-
- bool operator ==(const QQmlType &other) const {
- return d == other.d;
+ template <typename QQmlTypeContainer>
+ static void removeQQmlTypePrivate(QQmlTypeContainer &container,
+ const QQmlTypePrivate *reference)
+ {
+ for (typename QQmlTypeContainer::iterator it = container.begin(); it != container.end();) {
+ if (*it == reference)
+ it = container.erase(it);
+ else
+ ++it;
+ }
}
- bool isValid() const { return d != nullptr; }
- const QQmlTypePrivate *key() const { return d; }
+ static int registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &autoparent);
+ static void unregisterAutoParentFunction(const QQmlPrivate::AutoParentFunction &function);
- QByteArray typeName() const;
- QString qmlTypeName() const;
- QString elementName() const;
+ static int registerUnitCacheHook(const QQmlPrivate::RegisterQmlUnitCacheHook &hookRegistration);
+ static void clearTypeRegistrations();
- QHashedString module() const;
- int majorVersion() const;
- int minorVersion() const;
-
- bool availableInVersion(int vmajor, int vminor) const;
- bool availableInVersion(const QHashedStringRef &module, int vmajor, int vminor) const;
-
- QObject *create() const;
- void create(QObject **, void **, size_t) const;
-
- typedef void (*CreateFunc)(void *);
- CreateFunc createFunction() const;
- QQmlCustomParser *customParser() const;
-
- bool isCreatable() const;
- typedef QObject *(*ExtensionFunc)(QObject *);
- ExtensionFunc extensionFunction() const;
- bool isExtendedType() const;
- QString noCreationReason() const;
-
- bool isSingleton() const;
- bool isInterface() const;
- bool isComposite() const;
- bool isCompositeSingleton() const;
-
- int typeId() const;
- int qListTypeId() const;
-
- const QMetaObject *metaObject() const;
- const QMetaObject *baseMetaObject() const;
- int metaObjectRevision() const;
- bool containsRevisionedAttributes() const;
-
- QQmlAttachedPropertiesFunc attachedPropertiesFunction(QQmlEnginePrivate *engine) const;
- const QMetaObject *attachedPropertiesType(QQmlEnginePrivate *engine) const;
- int attachedPropertiesId(QQmlEnginePrivate *engine) const;
-
- int parserStatusCast() const;
- const char *interfaceIId() const;
- int propertyValueSourceCast() const;
- int propertyValueInterceptorCast() const;
-
- int index() const;
-
- class Q_QML_PRIVATE_EXPORT SingletonInstanceInfo
- {
- public:
- SingletonInstanceInfo()
- : scriptCallback(nullptr), qobjectCallback(nullptr), instanceMetaObject(nullptr) {}
-
- QJSValue (*scriptCallback)(QQmlEngine *, QJSEngine *);
- QObject *(*qobjectCallback)(QQmlEngine *, QJSEngine *);
- const QMetaObject *instanceMetaObject;
- QString typeName;
- QUrl url; // used by composite singletons
-
- void setQObjectApi(QQmlEngine *, QObject *);
- QObject *qobjectApi(QQmlEngine *) const;
- void setScriptApi(QQmlEngine *, const QJSValue &);
- QJSValue scriptApi(QQmlEngine *) const;
-
- void init(QQmlEngine *);
- void destroy(QQmlEngine *);
-
- QHash<QQmlEngine *, QJSValue> scriptApis;
- QHash<QQmlEngine *, QObject *> qobjectApis;
- };
- SingletonInstanceInfo *singletonInstanceInfo() const;
-
- QUrl sourceUrl() const;
-
- int enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &, bool *ok) const;
- int enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &, bool *ok) const;
- int enumValue(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const;
-
- int scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const;
- int scopedEnumIndex(QQmlEnginePrivate *engine, const QString &, bool *ok) const;
- int scopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *, bool *ok) const;
- int scopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &, bool *ok) const;
- int scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &, const QByteArray &, bool *ok) const;
- int scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &, const QStringRef &, bool *ok) const;
-
- QQmlTypePrivate *priv() const { return d; }
- static void refHandle(QQmlTypePrivate *priv);
- static void derefHandle(QQmlTypePrivate *priv);
- static int refCount(QQmlTypePrivate *priv);
-
- enum RegistrationType {
- CppType = 0,
- SingletonType = 1,
- InterfaceType = 2,
- CompositeType = 3,
- CompositeSingletonType = 4,
- AnyRegistrationType = 255
- };
+ static QList<QQmlProxyMetaObject::ProxyData> proxyData(const QMetaObject *mo,
+ const QMetaObject *baseMetaObject,
+ QMetaObject *lastMetaObject);
-private:
- QQmlType superType() const;
- QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const;
- int resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString &name, bool *ok) const;
- QQmlPropertyCache *compositePropertyCache(QQmlEnginePrivate *engine) const;
- friend class QQmlTypePrivate;
-
- friend QString registrationTypeString(RegistrationType);
- friend bool checkRegistration(RegistrationType, QQmlMetaTypeData *, const char *, const QString &, int);
- friend QQmlType registerType(const QQmlPrivate::RegisterType &);
- friend QQmlType registerSingletonType(const QQmlPrivate::RegisterSingletonType &);
- friend QQmlType registerInterface(const QQmlPrivate::RegisterInterface &);
- friend int registerQmlUnitCacheHook(const QQmlPrivate::RegisterQmlUnitCacheHook &);
- friend uint qHash(const QQmlType &t, uint seed);
- friend Q_QML_EXPORT void qmlClearTypeRegistrations();
- friend class QQmlMetaType;
-
- QQmlType(QQmlMetaTypeData *data, const QQmlPrivate::RegisterInterface &);
- QQmlType(QQmlMetaTypeData *data, const QString &, const QQmlPrivate::RegisterSingletonType &);
- QQmlType(QQmlMetaTypeData *data, const QString &, const QQmlPrivate::RegisterType &);
- QQmlType(QQmlMetaTypeData *data, const QString &, const QQmlPrivate::RegisterCompositeType &);
- QQmlType(QQmlMetaTypeData *data, const QString &, const QQmlPrivate::RegisterCompositeSingletonType &);
-
- QQmlTypePrivate *d;
+ static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
+ const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd);
};
Q_DECLARE_TYPEINFO(QQmlMetaType, Q_MOVABLE_TYPE);
-
-inline uint qHash(const QQmlType &t, uint seed = 0) { return qHash(reinterpret_cast<quintptr>(t.d), seed); }
-
-
-class QQmlTypeModulePrivate;
-class QQmlTypeModule
-{
-public:
- QString module() const;
- int majorVersion() const;
-
- int minimumMinorVersion() const;
- int maximumMinorVersion() const;
-
- QQmlType type(const QHashedStringRef &, int) const;
- QQmlType type(const QV4::String *, int) const;
-
- void walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const;
-
- QQmlTypeModulePrivate *priv() { return d; }
-private:
- //Used by register functions and creates the QQmlTypeModule for them
- friend QQmlTypeModule *getTypeModule(const QHashedString &uri, int majorVersion, QQmlMetaTypeData *data);
- friend void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data);
- friend struct QQmlMetaTypeData;
- friend Q_QML_EXPORT void qmlClearTypeRegistrations();
- friend class QQmlTypeModulePrivate;
-
- QQmlTypeModule();
- ~QQmlTypeModule();
- QQmlTypeModulePrivate *d;
-};
-
-class QQmlTypeModuleVersion
-{
-public:
- QQmlTypeModuleVersion();
- QQmlTypeModuleVersion(QQmlTypeModule *, int);
- QQmlTypeModuleVersion(const QQmlTypeModuleVersion &);
- QQmlTypeModuleVersion &operator=(const QQmlTypeModuleVersion &);
-
- QQmlTypeModule *module() const;
- int minorVersion() const;
-
- QQmlType type(const QHashedStringRef &) const;
- QQmlType type(const QV4::String *) const;
-
-private:
- QQmlTypeModule *m_module;
- int m_minor;
-};
-
-class Q_AUTOTEST_EXPORT QQmlMetaTypeRegistrationFailureRecorder
-{
- QStringList _failures;
-
-public:
- QQmlMetaTypeRegistrationFailureRecorder();
- ~QQmlMetaTypeRegistrationFailureRecorder();
-
- QStringList failures() const
- { return _failures; }
-};
-
QT_END_NAMESPACE
#endif // QQMLMETATYPE_P_H
diff --git a/src/qml/qml/qqmlmetatypedata.cpp b/src/qml/qml/qqmlmetatypedata.cpp
new file mode 100644
index 0000000000..775bc8bdb4
--- /dev/null
+++ b/src/qml/qml/qqmlmetatypedata.cpp
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlmetatypedata_p.h"
+
+#include <private/qqmltype_p_p.h>
+#include <private/qqmltypemodule_p.h>
+#include <private/qqmlpropertycache_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlMetaTypeData::QQmlMetaTypeData()
+{
+}
+
+QQmlMetaTypeData::~QQmlMetaTypeData()
+{
+ for (TypeModules::const_iterator i = uriToModule.constBegin(), cend = uriToModule.constEnd(); i != cend; ++i)
+ delete *i;
+ for (QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator it = propertyCaches.begin(), end = propertyCaches.end();
+ it != end; ++it)
+ (*it)->release();
+
+ // Do this before the attached properties disappear.
+ types.clear();
+ undeletableTypes.clear();
+}
+
+// This expects a "fresh" QQmlTypePrivate and adopts its reference.
+void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv)
+{
+ for (int i = 0; i < types.count(); ++i) {
+ if (!types.at(i).isValid()) {
+ types[i] = QQmlType(priv);
+ priv->index = i;
+ priv->release();
+ return;
+ }
+ }
+ types.append(QQmlType(priv));
+ priv->index = types.count() - 1;
+ priv->release();
+}
+
+QQmlPropertyCache *QQmlMetaTypeData::propertyCacheForMinorVersion(int index, int minorVersion) const
+{
+ return (index < typePropertyCaches.length())
+ ? typePropertyCaches.at(index).value(minorVersion).data()
+ : nullptr;
+}
+
+void QQmlMetaTypeData::setPropertyCacheForMinorVersion(int index, int minorVersion,
+ QQmlPropertyCache *cache)
+{
+ if (index >= typePropertyCaches.length())
+ typePropertyCaches.resize(index + 1);
+ typePropertyCaches[index][minorVersion] = cache;
+}
+
+void QQmlMetaTypeData::clearPropertyCachesForMinorVersion(int index)
+{
+ if (index < typePropertyCaches.length())
+ typePropertyCaches[index].clear();
+}
+
+QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QMetaObject *metaObject, int minorVersion)
+{
+ if (QQmlPropertyCache *rv = propertyCaches.value(metaObject))
+ return rv;
+
+ if (!metaObject->superClass()) {
+ QQmlPropertyCache *rv = new QQmlPropertyCache(metaObject);
+ propertyCaches.insert(metaObject, rv);
+ return rv;
+ }
+ QQmlPropertyCache *super = propertyCache(metaObject->superClass(), minorVersion);
+ QQmlPropertyCache *rv = super->copyAndAppend(metaObject, minorVersion);
+ propertyCaches.insert(metaObject, rv);
+ return rv;
+}
+
+QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QQmlType &type, int minorVersion)
+{
+ Q_ASSERT(type.isValid());
+
+ if (QQmlPropertyCache *pc = propertyCacheForMinorVersion(type.index(), minorVersion))
+ return pc;
+
+ QVector<QQmlType> types;
+
+ int maxMinorVersion = 0;
+
+ const QMetaObject *metaObject = type.metaObject();
+
+ while (metaObject) {
+ QQmlType t = QQmlMetaType::qmlType(metaObject, type.module(), type.majorVersion(), minorVersion);
+ if (t.isValid()) {
+ maxMinorVersion = qMax(maxMinorVersion, t.minorVersion());
+ types << t;
+ } else {
+ types << QQmlType();
+ }
+
+ metaObject = metaObject->superClass();
+ }
+
+ if (QQmlPropertyCache *pc = propertyCacheForMinorVersion(type.index(), maxMinorVersion)) {
+ setPropertyCacheForMinorVersion(type.index(), minorVersion, pc);
+ return pc;
+ }
+
+ QQmlPropertyCache *raw = propertyCache(type.metaObject(), minorVersion);
+
+ bool hasCopied = false;
+
+ for (int ii = 0; ii < types.count(); ++ii) {
+ const QQmlType &currentType = types.at(ii);
+ if (!currentType.isValid())
+ continue;
+
+ int rev = currentType.metaObjectRevision();
+ int moIndex = types.count() - 1 - ii;
+
+ if (raw->allowedRevision(moIndex) != rev) {
+ if (!hasCopied) {
+ // TODO: The copy should be mutable, and the original should be const
+ // Considering this, the setAllowedRevision() below does not violate
+ // the immutability of already published property caches.
+ raw = raw->copy();
+ hasCopied = true;
+ }
+ raw->setAllowedRevision(moIndex, rev);
+ }
+ }
+
+ // Test revision compatibility - the basic rule is:
+ // * Anything that is excluded, cannot overload something that is not excluded *
+
+ // Signals override:
+ // * other signals and methods of the same name.
+ // * properties named on<Signal Name>
+ // * automatic <property name>Changed notify signals
+
+ // Methods override:
+ // * other methods of the same name
+
+ // Properties override:
+ // * other elements of the same name
+
+#if 0
+ bool overloadError = false;
+ QString overloadName;
+
+ for (QQmlPropertyCache::StringCache::ConstIterator iter = raw->stringCache.begin();
+ !overloadError && iter != raw->stringCache.end();
+ ++iter) {
+
+ QQmlPropertyData *d = *iter;
+ if (raw->isAllowedInRevision(d))
+ continue; // Not excluded - no problems
+
+ // check that a regular "name" overload isn't happening
+ QQmlPropertyData *current = d;
+ while (!overloadError && current) {
+ current = d->overrideData(current);
+ if (current && raw->isAllowedInRevision(current))
+ overloadError = true;
+ }
+ }
+
+ if (overloadError) {
+ if (hasCopied) raw->release();
+
+ error.setDescription(QLatin1String("Type ") + type.qmlTypeName() + QLatin1Char(' ') + QString::number(type.majorVersion()) + QLatin1Char('.') + QString::number(minorVersion) + QLatin1String(" contains an illegal property \"") + overloadName + QLatin1String("\". This is an error in the type's implementation."));
+ return 0;
+ }
+#endif
+
+ setPropertyCacheForMinorVersion(type.index(), minorVersion, raw);
+
+ if (hasCopied)
+ raw->release();
+
+ if (minorVersion != maxMinorVersion)
+ setPropertyCacheForMinorVersion(type.index(), maxMinorVersion, raw);
+
+ return raw;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlmetatypedata_p.h b/src/qml/qml/qqmlmetatypedata_p.h
new file mode 100644
index 0000000000..5239b635ce
--- /dev/null
+++ b/src/qml/qml/qqmlmetatypedata_p.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLMETATYPEDATA_P_H
+#define QQMLMETATYPEDATA_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/qqmltype_p.h>
+#include <private/qqmlmetatype_p.h>
+#include <private/qhashedstring_p.h>
+
+#include <QtCore/qset.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qbitarray.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlTypePrivate;
+struct QQmlMetaTypeData
+{
+ QQmlMetaTypeData();
+ ~QQmlMetaTypeData();
+ void registerType(QQmlTypePrivate *priv);
+ QList<QQmlType> types;
+ QSet<QQmlType> undeletableTypes;
+ typedef QHash<int, QQmlTypePrivate *> Ids;
+ Ids idToType;
+ typedef QHash<QHashedStringRef, QQmlTypePrivate *> Names;
+ Names nameToType;
+ typedef QHash<QUrl, QQmlTypePrivate *> Files; //For file imported composite types only
+ Files urlToType;
+ Files urlToNonFileImportType; // For non-file imported composite and composite
+ // singleton types. This way we can locate any
+ // of them by url, even if it was registered as
+ // a module via QQmlPrivate::RegisterCompositeType
+ typedef QHash<const QMetaObject *, QQmlTypePrivate *> MetaObjects;
+ MetaObjects metaObjectToType;
+ typedef QHash<int, QQmlMetaType::StringConverter> StringConverters;
+ StringConverters stringConverters;
+ QVector<QHash<int, QQmlRefPointer<QQmlPropertyCache>>> typePropertyCaches;
+
+ struct VersionedUri {
+ VersionedUri()
+ : majorVersion(0) {}
+ VersionedUri(const QHashedString &uri, int majorVersion)
+ : uri(uri), majorVersion(majorVersion) {}
+ bool operator==(const VersionedUri &other) const {
+ return other.majorVersion == majorVersion && other.uri == uri;
+ }
+ QHashedString uri;
+ int majorVersion;
+ };
+ typedef QHash<VersionedUri, QQmlTypeModule *> TypeModules;
+ TypeModules uriToModule;
+
+ QBitArray objects;
+ QBitArray interfaces;
+ QBitArray lists;
+
+ QList<QQmlPrivate::AutoParentFunction> parentFunctions;
+ QVector<QQmlPrivate::QmlUnitCacheLookupFunction> lookupCachedQmlUnit;
+
+ QSet<QString> protectedNamespaces;
+
+ QString typeRegistrationNamespace;
+
+ QHash<int, int> qmlLists;
+
+ QHash<const QMetaObject *, QQmlPropertyCache *> propertyCaches;
+
+ QQmlPropertyCache *propertyCacheForMinorVersion(int index, int minorVersion) const;
+ void setPropertyCacheForMinorVersion(int index, int minorVersion, QQmlPropertyCache *cache);
+ void clearPropertyCachesForMinorVersion(int index);
+
+ QQmlPropertyCache *propertyCache(const QMetaObject *metaObject, int minorVersion);
+ QQmlPropertyCache *propertyCache(const QQmlType &type, int minorVersion);
+
+ void setTypeRegistrationFailures(QStringList *failures)
+ {
+ m_typeRegistrationFailures = failures;
+ }
+
+ void recordTypeRegFailure(const QString &message)
+ {
+ if (m_typeRegistrationFailures)
+ m_typeRegistrationFailures->append(message);
+ else
+ qWarning("%s", message.toUtf8().constData());
+ }
+
+private:
+ QStringList *m_typeRegistrationFailures = nullptr;
+};
+
+inline uint qHash(const QQmlMetaTypeData::VersionedUri &v)
+{
+ return v.uri.hash() ^ qHash(v.majorVersion);
+}
+
+QT_END_NAMESPACE
+
+#endif // QQMLMETATYPEDATA_P_H
diff --git a/src/qml/qml/qqmlnotifier.cpp b/src/qml/qml/qqmlnotifier.cpp
index 0706b8c0cf..1359586b31 100644
--- a/src/qml/qml/qqmlnotifier.cpp
+++ b/src/qml/qml/qqmlnotifier.cpp
@@ -118,8 +118,8 @@ void QQmlNotifierEndpoint::connect(QObject *source, int sourceSignal, QQmlEngine
disconnect();
Q_ASSERT(engine);
- if (QObjectPrivate::get(source)->threadData->threadId.load() !=
- QObjectPrivate::get(engine)->threadData->threadId.load()) {
+ if (QObjectPrivate::get(source)->threadData->threadId.loadRelaxed() !=
+ QObjectPrivate::get(engine)->threadData->threadId.loadRelaxed()) {
QString sourceName;
QDebug(&sourceName) << source;
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index e45515fbcf..a4270628e8 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -55,7 +55,9 @@
#include <private/qqmlvaluetypeproxybinding_p.h>
#include <private/qqmldebugconnector_p.h>
#include <private/qqmldebugserviceinterfaces_p.h>
+#include <private/qqmlscriptdata_p.h>
#include <private/qjsvalue_p.h>
+#include <private/qv4generatorobject_p.h>
#include <qtqml_tracepoints_p.h>
@@ -73,7 +75,7 @@ struct ActiveOCRestorer
};
}
-QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlContextData *creationContext,
+QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlContextData *creationContext,
QQmlIncubatorPrivate *incubator)
: phase(Startup)
, compilationUnit(compilationUnit)
@@ -100,7 +102,7 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlR
}
}
-QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState)
+QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState)
: phase(Startup)
, compilationUnit(compilationUnit)
, propertyCaches(&compilationUnit->propertyCaches)
@@ -373,7 +375,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
propertyType = QMetaType::Int;
} else {
// ### This should be resolved earlier at compile time and the binding value should be changed accordingly.
- QVariant value = binding->valueAsString(compilationUnit.data());
+ QVariant value = compilationUnit->bindingValueAsString(binding);
bool ok = QQmlPropertyPrivate::write(_qobject, *property, value, context);
Q_ASSERT(ok);
Q_UNUSED(ok);
@@ -406,7 +408,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
switch (propertyType) {
case QMetaType::QVariant: {
if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- double n = binding->valueAsNumber(compilationUnit->constants);
+ double n = compilationUnit->bindingValueAsNumber(binding);
if (double(int(n)) == n) {
if (property->isVarProperty()) {
_vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Value::fromInt32(int(n)));
@@ -438,11 +440,13 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
property->writeProperty(_qobject, &nullValue, propertyWriteFlags);
}
} else {
- QString stringValue = binding->valueAsString(compilationUnit.data());
+ QString stringValue = compilationUnit->bindingValueAsString(binding);
if (property->isVarProperty()) {
QV4::ScopedString s(scope, v4->newString(stringValue));
_vmeMetaObject->setVMEProperty(property->coreIndex(), s);
} else {
+ // ### Qt 6: Doing the conversion here where we don't know the eventual target type is rather strange
+ // and caused for instance QTBUG-78943
QVariant value = QQmlStringConverters::variantFromString(stringValue);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
@@ -451,25 +455,25 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
case QVariant::String: {
assertOrNull(binding->evaluatesToString());
- QString value = binding->valueAsString(compilationUnit.data());
+ QString value = compilationUnit->bindingValueAsString(binding);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::StringList: {
assertOrNull(binding->evaluatesToString());
- QStringList value(binding->valueAsString(compilationUnit.data()));
+ QStringList value(compilationUnit->bindingValueAsString(binding));
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::ByteArray: {
assertType(QV4::CompiledData::Binding::Type_String);
- QByteArray value(binding->valueAsString(compilationUnit.data()).toUtf8());
+ QByteArray value(compilationUnit->bindingValueAsString(binding).toUtf8());
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Url: {
assertType(QV4::CompiledData::Binding::Type_String);
- QString string = binding->valueAsString(compilationUnit.data());
+ QString string = compilationUnit->bindingValueAsString(binding);
// Encoded dir-separators defeat QUrl processing - decode them first
string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive);
QUrl value = string.isEmpty() ? QUrl() : compilationUnit->finalUrl().resolved(QUrl(string));
@@ -481,7 +485,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
case QVariant::UInt: {
assertType(QV4::CompiledData::Binding::Type_Number);
- double d = binding->valueAsNumber(compilationUnit->constants);
+ double d = compilationUnit->bindingValueAsNumber(binding);
uint value = uint(d);
property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
@@ -489,7 +493,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
case QVariant::Int: {
assertType(QV4::CompiledData::Binding::Type_Number);
- double d = binding->valueAsNumber(compilationUnit->constants);
+ double d = compilationUnit->bindingValueAsNumber(binding);
int value = int(d);
property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
@@ -497,19 +501,19 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
case QMetaType::Float: {
assertType(QV4::CompiledData::Binding::Type_Number);
- float value = float(binding->valueAsNumber(compilationUnit->constants));
+ float value = float(compilationUnit->bindingValueAsNumber(binding));
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Double: {
assertType(QV4::CompiledData::Binding::Type_Number);
- double value = binding->valueAsNumber(compilationUnit->constants);
+ double value = compilationUnit->bindingValueAsNumber(binding);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Color: {
bool ok = false;
- uint colorValue = QQmlStringConverters::rgbaFromString(binding->valueAsString(compilationUnit.data()), &ok);
+ uint colorValue = QQmlStringConverters::rgbaFromString(compilationUnit->bindingValueAsString(binding), &ok);
assertOrNull(ok);
struct { void *data[4]; } buffer;
if (QQml_valueTypeProvider()->storeValueType(property->propType(), &colorValue, &buffer, sizeof(buffer))) {
@@ -520,21 +524,21 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
#if QT_CONFIG(datestring)
case QVariant::Date: {
bool ok = false;
- QDate value = QQmlStringConverters::dateFromString(binding->valueAsString(compilationUnit.data()), &ok);
+ QDate value = QQmlStringConverters::dateFromString(compilationUnit->bindingValueAsString(binding), &ok);
assertOrNull(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Time: {
bool ok = false;
- QTime value = QQmlStringConverters::timeFromString(binding->valueAsString(compilationUnit.data()), &ok);
+ QTime value = QQmlStringConverters::timeFromString(compilationUnit->bindingValueAsString(binding), &ok);
assertOrNull(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::DateTime: {
bool ok = false;
- QDateTime value = QQmlStringConverters::dateTimeFromString(binding->valueAsString(compilationUnit.data()), &ok);
+ QDateTime value = QQmlStringConverters::dateTimeFromString(compilationUnit->bindingValueAsString(binding), &ok);
// ### VME compatibility :(
{
const qint64 date = value.date().toJulianDay();
@@ -548,42 +552,42 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
#endif // datestring
case QVariant::Point: {
bool ok = false;
- QPoint value = QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok).toPoint();
+ QPoint value = QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok).toPoint();
assertOrNull(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::PointF: {
bool ok = false;
- QPointF value = QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok);
+ QPointF value = QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok);
assertOrNull(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Size: {
bool ok = false;
- QSize value = QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok).toSize();
+ QSize value = QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok).toSize();
assertOrNull(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::SizeF: {
bool ok = false;
- QSizeF value = QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok);
+ QSizeF value = QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok);
assertOrNull(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Rect: {
bool ok = false;
- QRect value = QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok).toRect();
+ QRect value = QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok).toRect();
assertOrNull(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::RectF: {
bool ok = false;
- QRectF value = QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok);
+ QRectF value = QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok);
assertOrNull(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
@@ -599,7 +603,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
float xp;
float yp;
} vec;
- bool ok = QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec));
+ bool ok = QQmlStringConverters::createFromString(QMetaType::QVector2D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec));
assertOrNull(ok);
Q_UNUSED(ok);
property->writeProperty(_qobject, &vec, propertyWriteFlags);
@@ -611,7 +615,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
float yp;
float zy;
} vec;
- bool ok = QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec));
+ bool ok = QQmlStringConverters::createFromString(QMetaType::QVector3D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec));
assertOrNull(ok);
Q_UNUSED(ok);
property->writeProperty(_qobject, &vec, propertyWriteFlags);
@@ -624,7 +628,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
float zy;
float wp;
} vec;
- bool ok = QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec));
+ bool ok = QQmlStringConverters::createFromString(QMetaType::QVector4D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec));
assertOrNull(ok);
Q_UNUSED(ok);
property->writeProperty(_qobject, &vec, propertyWriteFlags);
@@ -637,7 +641,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
float yp;
float zp;
} vec;
- bool ok = QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec));
+ bool ok = QQmlStringConverters::createFromString(QMetaType::QQuaternion, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec));
assertOrNull(ok);
Q_UNUSED(ok);
property->writeProperty(_qobject, &vec, propertyWriteFlags);
@@ -651,12 +655,12 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
if (property->propType() == qMetaTypeId<QList<qreal> >()) {
assertType(QV4::CompiledData::Binding::Type_Number);
QList<qreal> value;
- value.append(binding->valueAsNumber(compilationUnit->constants));
+ value.append(compilationUnit->bindingValueAsNumber(binding));
property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
} else if (property->propType() == qMetaTypeId<QList<int> >()) {
assertType(QV4::CompiledData::Binding::Type_Number);
- double n = binding->valueAsNumber(compilationUnit->constants);
+ double n = compilationUnit->bindingValueAsNumber(binding);
QList<int> value;
value.append(int(n));
property->writeProperty(_qobject, &value, propertyWriteFlags);
@@ -669,7 +673,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
} else if (property->propType() == qMetaTypeId<QList<QUrl> >()) {
assertType(QV4::CompiledData::Binding::Type_String);
- QString urlString = binding->valueAsString(compilationUnit.data());
+ QString urlString = compilationUnit->bindingValueAsString(binding);
QUrl u = urlString.isEmpty() ? QUrl()
: compilationUnit->finalUrl().resolved(QUrl(urlString));
QList<QUrl> value;
@@ -679,7 +683,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
} else if (property->propType() == qMetaTypeId<QList<QString> >()) {
assertOrNull(binding->evaluatesToString());
QList<QString> value;
- value.append(binding->valueAsString(compilationUnit.data()));
+ value.append(compilationUnit->bindingValueAsString(binding));
property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
} else if (property->propType() == qMetaTypeId<QJSValue>()) {
@@ -687,7 +691,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
if (binding->type == QV4::CompiledData::Binding::Type_Boolean) {
value = QJSValue(binding->valueAsBoolean());
} else if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- double n = binding->valueAsNumber(compilationUnit->constants);
+ double n = compilationUnit->bindingValueAsNumber(binding);
if (double(int(n)) == n) {
value = QJSValue(int(n));
} else
@@ -695,14 +699,14 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
} else if (binding->type == QV4::CompiledData::Binding::Type_Null) {
value = QJSValue::NullValue;
} else {
- value = QJSValue(binding->valueAsString(compilationUnit.data()));
+ value = QJSValue(compilationUnit->bindingValueAsString(binding));
}
property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
}
// otherwise, try a custom type assignment
- QString stringValue = binding->valueAsString(compilationUnit.data());
+ QString stringValue = compilationUnit->bindingValueAsString(binding);
QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType());
Q_ASSERT(converter);
QVariant value = (*converter)(stringValue);
@@ -735,7 +739,7 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings)
QQmlListProperty<void> savedList;
qSwap(_currentList, savedList);
- const QV4::CompiledData::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(_compiledObjectIndex);
+ const QV4::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(_compiledObjectIndex);
if (_compiledObject->idNameIndex) {
const QQmlPropertyData *idProperty = propertyData.last();
@@ -816,7 +820,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
{
if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
Q_ASSERT(stringAt(compilationUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty());
- QV4::CompiledData::ResolvedTypeReference *tr = resolvedType(binding->propertyNameIndex);
+ QV4::ResolvedTypeReference *tr = resolvedType(binding->propertyNameIndex);
Q_ASSERT(tr);
QQmlType attachedType = tr->type;
if (!attachedType.isValid()) {
@@ -835,13 +839,14 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
// ### resolve this at compile time
if (bindingProperty && bindingProperty->propType() == qMetaTypeId<QQmlScriptString>()) {
- QQmlScriptString ss(binding->valueAsScriptString(compilationUnit.data()), context->asQQmlContext(), _scopeObject);
+ QQmlScriptString ss(compilationUnit->bindingValueAsScriptString(binding),
+ context->asQQmlContext(), _scopeObject);
ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid;
ss.d.data()->lineNumber = binding->location.line;
ss.d.data()->columnNumber = binding->location.column;
ss.d.data()->isStringLiteral = binding->type == QV4::CompiledData::Binding::Type_String;
ss.d.data()->isNumberLiteral = binding->type == QV4::CompiledData::Binding::Type_Number;
- ss.d.data()->numberValue = binding->valueAsNumber(compilationUnit->constants);
+ ss.d.data()->numberValue = compilationUnit->bindingValueAsNumber(binding);
QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor |
QQmlPropertyData::RemoveBindingOnAliasWrite;
@@ -938,7 +943,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
auto bindingTarget = _bindingTarget;
auto valueTypeProperty = _valueTypeProperty;
- auto assignBinding = [qmlBinding, bindingTarget, targetProperty, subprop, bindingProperty, valueTypeProperty](QQmlObjectCreatorSharedState *sharedState) -> bool {
+ auto assignBinding = [qmlBinding, bindingTarget, targetProperty, subprop, bindingProperty, valueTypeProperty](QQmlObjectCreatorSharedState *sharedState) mutable -> bool {
if (!qmlBinding->setTarget(bindingTarget, *targetProperty, subprop) && targetProperty->isAlias())
return false;
@@ -1133,7 +1138,10 @@ void QQmlObjectCreator::setupFunctions()
if (!property->isVMEFunction())
continue;
- function = QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction);
+ if (runtimeFunction->isGenerator())
+ function = QV4::GeneratorFunction::create(qmlContext, runtimeFunction);
+ else
+ function = QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction);
_vmeMetaObject->setVmeMethod(property->coreIndex(), function);
}
}
@@ -1184,8 +1192,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
instance = component;
ddata = QQmlData::get(instance, /*create*/true);
} else {
- QV4::CompiledData::ResolvedTypeReference *typeRef
- = resolvedType(obj->inheritedTypeNameIndex);
+ QV4::ResolvedTypeReference *typeRef = resolvedType(obj->inheritedTypeNameIndex);
Q_ASSERT(typeRef);
installPropertyCache = !typeRef->isFullyDynamicType;
QQmlType type = typeRef->type;
@@ -1445,8 +1452,12 @@ void QQmlObjectCreator::clear()
return;
Q_ASSERT(phase != Startup);
- while (!sharedState->allCreatedObjects.isEmpty())
- delete sharedState->allCreatedObjects.pop();
+ while (!sharedState->allCreatedObjects.isEmpty()) {
+ auto object = sharedState->allCreatedObjects.pop();
+ if (engine->objectOwnership(object) != QQmlEngine::CppOwnership) {
+ delete object;
+ }
+ }
while (sharedState->componentAttached) {
QQmlComponentAttached *a = sharedState->componentAttached;
diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h
index 5aca60e2f0..ecdbcc56dd 100644
--- a/src/qml/qml/qqmlobjectcreator_p.h
+++ b/src/qml/qml/qqmlobjectcreator_p.h
@@ -53,17 +53,16 @@
#include <private/qqmlimport_p.h>
#include <private/qqmltypenamecache_p.h>
#include <private/qv4compileddata_p.h>
-#include <private/qqmltypecompiler_p.h>
#include <private/qfinitestack_p.h>
#include <private/qrecursionwatcher_p.h>
#include <private/qqmlprofiler_p.h>
+#include <private/qv4qmlcontext_p.h>
#include <qpointer.h>
QT_BEGIN_NAMESPACE
class QQmlAbstractBinding;
-struct QQmlTypeCompiler;
class QQmlInstantiationInterrupt;
class QQmlIncubatorPrivate;
@@ -85,7 +84,7 @@ class Q_QML_PRIVATE_EXPORT QQmlObjectCreator
{
Q_DECLARE_TR_FUNCTIONS(QQmlObjectCreator)
public:
- QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator = nullptr);
+ QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator = nullptr);
~QQmlObjectCreator();
QObject *create(int subComponentIndex = -1, QObject *parent = nullptr, QQmlInstantiationInterrupt *interrupt = nullptr);
@@ -94,17 +93,17 @@ public:
QQmlContextData *finalize(QQmlInstantiationInterrupt &interrupt);
void clear();
- QQmlComponentAttached **componentAttachment() const { return &sharedState->componentAttached; }
+ QQmlComponentAttached **componentAttachment() { return &sharedState->componentAttached; }
- QList<QQmlEnginePrivate::FinalizeCallback> *finalizeCallbacks() const { return &sharedState->finalizeCallbacks; }
+ QList<QQmlEnginePrivate::FinalizeCallback> *finalizeCallbacks() { return &sharedState->finalizeCallbacks; }
QList<QQmlError> errors;
QQmlContextData *parentContextData() const { return parentContext.contextData(); }
- QFiniteStack<QPointer<QObject> > &allCreatedObjects() const { return sharedState->allCreatedObjects; }
+ QFiniteStack<QPointer<QObject> > &allCreatedObjects() { return sharedState->allCreatedObjects; }
private:
- QQmlObjectCreator(QQmlContextData *contextData, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState);
+ QQmlObjectCreator(QQmlContextData *contextData, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState);
void init(QQmlContextData *parentContext);
@@ -125,7 +124,7 @@ private:
inline QV4::QmlContext *currentQmlContext();
Q_NEVER_INLINE void createQmlContext();
- QV4::CompiledData::ResolvedTypeReference *resolvedType(int id) const
+ QV4::ResolvedTypeReference *resolvedType(int id) const
{
return compilationUnit->resolvedType(id);
}
@@ -141,7 +140,7 @@ private:
QQmlEngine *engine;
QV4::ExecutionEngine *v4;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
const QV4::CompiledData::Unit *qmlUnit;
QQmlGuardedContextData parentContext;
QQmlContextData *context;
diff --git a/src/qml/qml/qqmlobjectorgadget.cpp b/src/qml/qml/qqmlobjectorgadget.cpp
new file mode 100644
index 0000000000..1d4916d7d1
--- /dev/null
+++ b/src/qml/qml/qqmlobjectorgadget.cpp
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlobjectorgadget_p.h"
+
+QT_BEGIN_NAMESPACE
+
+void QQmlObjectOrGadget::metacall(QMetaObject::Call type, int index, void **argv) const
+{
+ if (ptr.isNull()) {
+ const QMetaObject *metaObject = _m.asT2();
+ metaObject->d.static_metacall(nullptr, type, index, argv);
+ }
+ else if (ptr.isT1()) {
+ QMetaObject::metacall(ptr.asT1(), type, index, argv);
+ }
+ else {
+ const QMetaObject *metaObject = _m.asT1()->metaObject();
+ QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(type, &metaObject, &index);
+ metaObject->d.static_metacall(reinterpret_cast<QObject*>(ptr.asT2()), type, index, argv);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlobjectorgadget_p.h b/src/qml/qml/qqmlobjectorgadget_p.h
new file mode 100644
index 0000000000..c5f5f58a3a
--- /dev/null
+++ b/src/qml/qml/qqmlobjectorgadget_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLOBJECTORGADGET_P_H
+#define QQMLOBJECTORGADGET_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/qqmlmetaobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlObjectOrGadget: public QQmlMetaObject
+{
+public:
+ QQmlObjectOrGadget(QObject *obj)
+ : QQmlMetaObject(obj),
+ ptr(obj)
+ {}
+ QQmlObjectOrGadget(QQmlPropertyCache *propertyCache, void *gadget)
+ : QQmlMetaObject(propertyCache)
+ , ptr(gadget)
+ {}
+
+ void metacall(QMetaObject::Call type, int index, void **argv) const;
+
+private:
+ QBiPointer<QObject, void> ptr;
+
+protected:
+ QQmlObjectOrGadget(const QMetaObject* metaObject)
+ : QQmlMetaObject(metaObject)
+ {}
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLOBJECTORGADGET_P_H
diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp
index fc798a2c23..fe0946c6de 100644
--- a/src/qml/qml/qqmlopenmetaobject.cpp
+++ b/src/qml/qml/qqmlopenmetaobject.cpp
@@ -41,7 +41,6 @@
#include <private/qqmlpropertycache_p.h>
#include <private/qqmldata_p.h>
#include <private/qmetaobjectbuilder_p.h>
-#include <private/qv8engine_p.h>
#include <qqmlengine.h>
#include <qdebug.h>
diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h
index 8a45de9f76..6d7a2569bc 100644
--- a/src/qml/qml/qqmlprivate.h
+++ b/src/qml/qml/qqmlprivate.h
@@ -51,11 +51,14 @@
// We mean it.
//
+#include <functional>
+
#include <QtQml/qtqmlglobal.h>
#include <QtCore/qglobal.h>
#include <QtCore/qvariant.h>
#include <QtCore/qurl.h>
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
@@ -276,6 +279,7 @@ namespace QQmlPrivate
const QMetaObject *instanceMetaObject; // new in version 1
int typeId; // new in version 2
int revision; // new in version 2
+ std::function<QObject*(QQmlEngine *, QJSEngine *)> generalizedQobjectApi; // new in version 3
// If this is extended ensure "version" is bumped!!!
};
@@ -319,6 +323,13 @@ namespace QQmlPrivate
int Q_QML_EXPORT qmlregister(RegistrationType, void *);
void Q_QML_EXPORT qmlunregister(RegistrationType, quintptr);
+ struct Q_QML_EXPORT RegisterSingletonFunctor
+ {
+ QObject *operator()(QQmlEngine *, QJSEngine *);
+
+ QPointer<QObject> m_object;
+ bool alreadyCalled = false;
+ };
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp
index a394ed1ad9..f5aae8f462 100644
--- a/src/qml/qml/qqmlproperty.cpp
+++ b/src/qml/qml/qqmlproperty.cpp
@@ -1222,10 +1222,18 @@ bool QQmlPropertyPrivate::write(QObject *object,
if (propertyType == variantType && !isUrl && propertyType != qMetaTypeId<QList<QUrl>>() && !property.isQList()) {
return property.writeProperty(object, const_cast<void *>(value.constData()), flags);
} else if (property.isQObject()) {
- QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, variantType);
+ QVariant val = value;
+ int varType = variantType;
+ if (variantType == QMetaType::Nullptr) {
+ // This reflects the fact that you can assign a nullptr to a QObject pointer
+ // Without the change to QObjectStar, rawMetaObjectForType would not give us a QQmlMetaObject
+ varType = QMetaType::QObjectStar;
+ val = QVariant(QMetaType::QObjectStar, nullptr);
+ }
+ QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, varType);
if (valMo.isNull())
return false;
- QObject *o = *static_cast<QObject *const *>(value.constData());
+ QObject *o = *static_cast<QObject *const *>(val.constData());
QQmlMetaObject propMo = rawMetaObjectForType(enginePriv, propertyType);
if (o)
@@ -1371,8 +1379,9 @@ bool QQmlPropertyPrivate::write(QObject *object,
}
}
if (!ok) {
- // the only other option is that they are assigning a single value
+ // the only other options are that they are assigning a single value
// to a sequence type property (eg, an int to a QList<int> property).
+ // or that we encountered an interface type
// Note that we've already handled single-value assignment to QList<QUrl> properties.
if (variantType == QVariant::Int && propertyType == qMetaTypeId<QList<int> >()) {
QList<int> list;
@@ -1403,6 +1412,15 @@ bool QQmlPropertyPrivate::write(QObject *object,
}
}
+ if (!ok && QQmlMetaType::isInterface(propertyType)) {
+ auto valueAsQObject = qvariant_cast<QObject *>(value);
+ if (valueAsQObject && valueAsQObject->qt_metacast(QQmlMetaType::interfaceIId(propertyType))) {
+ // this case can occur when object has an interface type
+ // and the variant contains a type implementing the interface
+ return property.writeProperty(object, const_cast<void *>(value.constData()), flags);
+ }
+ }
+
if (ok) {
return property.writeProperty(object, const_cast<void *>(v.constData()), flags);
} else {
diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h
index 544eab4c7f..285c34d7fa 100644
--- a/src/qml/qml/qqmlproperty_p.h
+++ b/src/qml/qml/qqmlproperty_p.h
@@ -56,14 +56,17 @@
#include <private/qobject_p.h>
#include <private/qtqmlglobal_p.h>
-#include <private/qqmlpropertycache_p.h>
+#include <private/qqmlrefcount_p.h>
+#include <private/qqmlcontext_p.h>
#include <private/qqmlboundsignalexpressionpointer_p.h>
+#include <private/qqmlpropertydata_p.h>
QT_BEGIN_NAMESPACE
class QQmlContext;
class QQmlEnginePrivate;
class QQmlJavaScriptExpression;
+class QQmlMetaObject;
class Q_QML_PRIVATE_EXPORT QQmlPropertyPrivate : public QQmlRefCount
{
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 73bfd7bbaa..69957ab282 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -42,10 +42,10 @@
#include <private/qqmlengine_p.h>
#include <private/qqmlbinding_p.h>
#include <private/qqmlvmemetaobject_p.h>
-#include <private/qv8engine_p.h>
#include <private/qmetaobject_p.h>
#include <private/qmetaobjectbuilder_p.h>
+#include <private/qqmlpropertycachemethodarguments_p.h>
#include <private/qv4value_p.h>
@@ -65,21 +65,6 @@ QT_BEGIN_NAMESPACE
#define Q_INT16_MAX 32767
-class QQmlPropertyCacheMethodArguments
-{
-public:
- QQmlPropertyCacheMethodArguments *next;
-
- //for signal handler rewrites
- QString *signalParameterStringForJS;
- int parameterError:1;
- int argumentsValid:1;
-
- QList<QByteArray> *names;
-
- int arguments[1];
-};
-
// Flags that do *NOT* depend on the property's QMetaProperty::userType() and thus are quick
// to load
static QQmlPropertyData::Flags fastFlagsForProperty(const QMetaProperty &p)
@@ -113,8 +98,6 @@ static void flagsForPropertyType(int propType, QQmlPropertyData::Flags &flags)
flags.type = QQmlPropertyData::Flags::QmlBindingType;
} else if (propType == qMetaTypeId<QJSValue>()) {
flags.type = QQmlPropertyData::Flags::QJSValueType;
- } else if (propType == qMetaTypeId<QQmlV4Handle>()) {
- flags.type = QQmlPropertyData::Flags::V4HandleType;
} else {
QQmlMetaType::TypeCategory cat = QQmlMetaType::typeCategory(propType);
@@ -141,24 +124,27 @@ QQmlPropertyData::flagsForProperty(const QMetaProperty &p)
return flags;
}
-void QQmlPropertyData::lazyLoad(const QMetaProperty &p)
+static void populate(QQmlPropertyData *data, const QMetaProperty &p)
{
- setCoreIndex(p.propertyIndex());
- setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal()));
Q_ASSERT(p.revision() <= Q_INT16_MAX);
- setRevision(p.revision());
-
- setFlags(fastFlagsForProperty(p));
+ data->setCoreIndex(p.propertyIndex());
+ data->setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal()));
+ data->setFlags(fastFlagsForProperty(p));
+ data->setRevision(p.revision());
+}
+void QQmlPropertyData::lazyLoad(const QMetaProperty &p)
+{
+ populate(this, p);
int type = static_cast<int>(p.type());
if (type == QMetaType::QObjectStar) {
setPropType(type);
- _flags.type = Flags::QObjectDerivedType;
+ m_flags.type = Flags::QObjectDerivedType;
} else if (type == QMetaType::QVariant) {
setPropType(type);
- _flags.type = Flags::QVariantType;
+ m_flags.type = Flags::QVariantType;
} else if (type == QVariant::UserType || type == -1) {
- _flags.notFullyResolved = true;
+ m_flags.notFullyResolved = true;
} else {
setPropType(type);
}
@@ -166,13 +152,9 @@ void QQmlPropertyData::lazyLoad(const QMetaProperty &p)
void QQmlPropertyData::load(const QMetaProperty &p)
{
+ populate(this, p);
setPropType(p.userType());
- setCoreIndex(p.propertyIndex());
- setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal()));
- setFlags(fastFlagsForProperty(p));
- flagsForPropertyType(propType(), _flags);
- Q_ASSERT(p.revision() <= Q_INT16_MAX);
- setRevision(p.revision());
+ flagsForPropertyType(propType(), m_flags);
}
void QQmlPropertyData::load(const QMetaMethod &m)
@@ -182,23 +164,23 @@ void QQmlPropertyData::load(const QMetaMethod &m)
setPropType(m.returnType());
- _flags.type = Flags::FunctionType;
- if (m.methodType() == QMetaMethod::Signal)
- _flags.isSignal = true;
- else if (m.methodType() == QMetaMethod::Constructor) {
- _flags.isConstructor = true;
+ m_flags.type = Flags::FunctionType;
+ if (m.methodType() == QMetaMethod::Signal) {
+ m_flags.isSignal = true;
+ } else if (m.methodType() == QMetaMethod::Constructor) {
+ m_flags.isConstructor = true;
setPropType(QMetaType::QObjectStar);
}
- if (m.parameterCount()) {
- _flags.hasArguments = true;
- if ((m.parameterCount() == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*")) {
- _flags.isV4Function = true;
- }
+ const int paramCount = m.parameterCount();
+ if (paramCount) {
+ m_flags.hasArguments = true;
+ if ((paramCount == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*"))
+ m_flags.isV4Function = true;
}
if (m.attributes() & QMetaMethod::Cloned)
- _flags.isCloned = true;
+ m_flags.isCloned = true;
Q_ASSERT(m.revision() <= Q_INT16_MAX);
setRevision(m.revision());
@@ -206,37 +188,14 @@ void QQmlPropertyData::load(const QMetaMethod &m)
void QQmlPropertyData::lazyLoad(const QMetaMethod &m)
{
- setCoreIndex(m.methodIndex());
- setPropType(QMetaType::Void);
- setArguments(nullptr);
- _flags.type = Flags::FunctionType;
- if (m.methodType() == QMetaMethod::Signal)
- _flags.isSignal = true;
- else if (m.methodType() == QMetaMethod::Constructor) {
- _flags.isConstructor = true;
- setPropType(QMetaType::QObjectStar);
- }
+ load(m);
const char *returnType = m.typeName();
if (!returnType)
returnType = "\0";
if ((*returnType != 'v') || (qstrcmp(returnType+1, "oid") != 0)) {
- _flags.notFullyResolved = true;
- }
-
- const int paramCount = m.parameterCount();
- if (paramCount) {
- _flags.hasArguments = true;
- if ((paramCount == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*")) {
- _flags.isV4Function = true;
- }
+ m_flags.notFullyResolved = true;
}
-
- if (m.attributes() & QMetaMethod::Cloned)
- _flags.isCloned = true;
-
- Q_ASSERT(m.revision() <= Q_INT16_MAX);
- setRevision(m.revision());
}
/*!
@@ -361,7 +320,7 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag
data.setArguments(nullptr);
QQmlPropertyData handler = data;
- handler._flags.isSignalHandler = true;
+ handler.m_flags.isSignalHandler = true;
if (types) {
int argumentCount = *types;
@@ -389,17 +348,18 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag
}
void QQmlPropertyCache::appendMethod(const QString &name, QQmlPropertyData::Flags flags,
- int coreIndex, const QList<QByteArray> &names)
+ int coreIndex, int returnType, const QList<QByteArray> &names,
+ const QVector<int> &parameterTypes)
{
int argumentCount = names.count();
QQmlPropertyData data;
- data.setPropType(QMetaType::QVariant);
+ data.setPropType(returnType);
data.setCoreIndex(coreIndex);
QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names);
for (int ii = 0; ii < argumentCount; ++ii)
- args->arguments[ii + 1] = QMetaType::QVariant;
+ args->arguments[ii + 1] = parameterTypes.at(ii);
args->argumentsValid = true;
data.setArguments(args);
@@ -559,7 +519,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
data->setFlags(methodFlags);
data->lazyLoad(m);
- data->_flags.isDirect = !dynamicMetaObject;
+ data->m_flags.isDirect = !dynamicMetaObject;
Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX);
data->setMetaObjectOffset(allowedRevisionCache.count() - 1);
@@ -567,7 +527,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
if (data->isSignal()) {
sigdata = &signalHandlerIndexCache[signalHandlerIndex - signalHandlerIndexCacheStart];
*sigdata = *data;
- sigdata->_flags.isSignalHandler = true;
+ sigdata->m_flags.isSignalHandler = true;
}
QQmlPropertyData *old = nullptr;
@@ -609,7 +569,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
if (old) {
// We only overload methods in the same class, exactly like C++
if (old->isFunction() && old->coreIndex() >= methodOffset)
- data->_flags.isOverload = true;
+ data->m_flags.isOverload = true;
data->markAsOverrideOf(old);
}
@@ -640,7 +600,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
data->lazyLoad(p);
data->setTypeMinorVersion(typeMinorVersion);
- data->_flags.isDirect = !dynamicMetaObject;
+ data->m_flags.isDirect = !dynamicMetaObject;
Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX);
data->setMetaObjectOffset(allowedRevisionCache.count() - 1);
@@ -666,7 +626,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
}
if (isGadget) // always dispatch over a 'normal' meta-call so the QQmlValueType can intercept
- data->_flags.isDirect = false;
+ data->m_flags.isDirect = false;
else
data->trySetStaticMetaCallFunction(metaObject->d.static_metacall, ii - propOffset);
if (old)
@@ -677,7 +637,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
void QQmlPropertyCache::resolve(QQmlPropertyData *data) const
{
Q_ASSERT(data->notFullyResolved());
- data->_flags.notFullyResolved = false;
+ data->m_flags.notFullyResolved = false;
const QMetaObject *mo = firstCppMetaObject();
if (data->isFunction()) {
@@ -712,7 +672,7 @@ void QQmlPropertyCache::resolve(QQmlPropertyData *data) const
data->setPropType(registerResult == -1 ? QMetaType::UnknownType : registerResult);
}
}
- flagsForPropertyType(data->propType(), data->_flags);
+ flagsForPropertyType(data->propType(), data->m_flags);
}
}
@@ -889,52 +849,7 @@ void QQmlPropertyData::markAsOverrideOf(QQmlPropertyData *predecessor)
setOverrideIndexIsProperty(!predecessor->isFunction());
setOverrideIndex(predecessor->coreIndex());
- predecessor->_flags.isOverridden = true;
-}
-
-struct StaticQtMetaObject : public QObject
-{
- static const QMetaObject *get()
- { return &staticQtMetaObject; }
-};
-
-static bool isNamedEnumeratorInScope(const QMetaObject *resolvedMetaObject, const QByteArray &scope,
- const QByteArray &name)
-{
- for (int i = resolvedMetaObject->enumeratorCount() - 1; i >= 0; --i) {
- QMetaEnum m = resolvedMetaObject->enumerator(i);
- if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
- return true;
- }
- return false;
-}
-
-static bool isNamedEnumerator(const QMetaObject *metaObj, const QByteArray &scopedName)
-{
- QByteArray scope;
- QByteArray name;
- int scopeIdx = scopedName.lastIndexOf("::");
- if (scopeIdx != -1) {
- scope = scopedName.left(scopeIdx);
- name = scopedName.mid(scopeIdx + 2);
- } else {
- name = scopedName;
- }
-
- if (scope == "Qt")
- return isNamedEnumeratorInScope(StaticQtMetaObject::get(), scope, name);
-
- if (isNamedEnumeratorInScope(metaObj, scope, name))
- return true;
-
- if (metaObj->d.relatedMetaObjects && !scope.isEmpty()) {
- for (auto related = metaObj->d.relatedMetaObjects; *related; ++related) {
- if (isNamedEnumeratorInScope(*related, scope, name))
- return true;
- }
- }
-
- return false;
+ predecessor->m_flags.isOverridden = true;
}
QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int argc, const QList<QByteArray> &names)
@@ -954,7 +869,7 @@ QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int a
QString QQmlPropertyCache::signalParameterStringForJS(QV4::ExecutionEngine *engine, const QList<QByteArray> &parameterNameList, QString *errorString)
{
bool unnamedParameter = false;
- const QSet<QString> &illegalNames = engine->v8Engine->illegalNames();
+ const QSet<QString> &illegalNames = engine->illegalNames();
QString parameters;
for (int i = 0; i < parameterNameList.count(); ++i) {
@@ -1517,262 +1432,4 @@ QList<QByteArray> QQmlPropertyCache::signalParameterNames(int index) const
return QList<QByteArray>();
}
-// Returns true if \a from is assignable to a property of type \a to
-bool QQmlMetaObject::canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to)
-{
- Q_ASSERT(!from.isNull() && !to.isNull());
-
- struct I { static bool equal(const QMetaObject *lhs, const QMetaObject *rhs) {
- return lhs == rhs || (lhs && rhs && lhs->d.stringdata == rhs->d.stringdata);
- } };
-
- const QMetaObject *tom = to._m.isT1()?to._m.asT1()->metaObject():to._m.asT2();
- if (tom == &QObject::staticMetaObject) return true;
-
- if (from._m.isT1() && to._m.isT1()) { // QQmlPropertyCache -> QQmlPropertyCache
- QQmlPropertyCache *fromp = from._m.asT1();
- QQmlPropertyCache *top = to._m.asT1();
-
- while (fromp) {
- if (fromp == top) return true;
- fromp = fromp->parent();
- }
- } else if (from._m.isT1() && to._m.isT2()) { // QQmlPropertyCache -> QMetaObject
- QQmlPropertyCache *fromp = from._m.asT1();
-
- while (fromp) {
- const QMetaObject *fromm = fromp->metaObject();
- if (fromm && I::equal(fromm, tom)) return true;
- fromp = fromp->parent();
- }
- } else if (from._m.isT2() && to._m.isT1()) { // QMetaObject -> QQmlPropertyCache
- const QMetaObject *fromm = from._m.asT2();
-
- if (!tom) return false;
-
- while (fromm) {
- if (I::equal(fromm, tom)) return true;
- fromm = fromm->superClass();
- }
- } else { // QMetaObject -> QMetaObject
- const QMetaObject *fromm = from._m.asT2();
-
- while (fromm) {
- if (I::equal(fromm, tom)) return true;
- fromm = fromm->superClass();
- }
- }
-
- return false;
-}
-
-void QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index)
-{
- int offset;
-
- switch (type) {
- case QMetaObject::ReadProperty:
- case QMetaObject::WriteProperty:
- case QMetaObject::ResetProperty:
- case QMetaObject::QueryPropertyDesignable:
- case QMetaObject::QueryPropertyEditable:
- case QMetaObject::QueryPropertyScriptable:
- case QMetaObject::QueryPropertyStored:
- case QMetaObject::QueryPropertyUser:
- offset = (*metaObject)->propertyOffset();
- while (*index < offset) {
- *metaObject = (*metaObject)->superClass();
- offset = (*metaObject)->propertyOffset();
- }
- break;
- case QMetaObject::InvokeMetaMethod:
- offset = (*metaObject)->methodOffset();
- while (*index < offset) {
- *metaObject = (*metaObject)->superClass();
- offset = (*metaObject)->methodOffset();
- }
- break;
- default:
- offset = 0;
- Q_UNIMPLEMENTED();
- offset = INT_MAX;
- }
-
- *index -= offset;
-}
-
-QQmlPropertyCache *QQmlMetaObject::propertyCache(QQmlEnginePrivate *e) const
-{
- if (_m.isNull()) return nullptr;
- if (_m.isT1()) return _m.asT1();
- else return e->cache(_m.asT2());
-}
-
-int QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const
-{
- Q_ASSERT(!_m.isNull() && data.coreIndex() >= 0);
-
- int type = data.propType();
-
- const char *propTypeName = nullptr;
-
- if (type == QMetaType::UnknownType) {
- // Find the return type name from the method info
- QMetaMethod m;
-
- if (_m.isT1()) {
- QQmlPropertyCache *c = _m.asT1();
- Q_ASSERT(data.coreIndex() < c->methodIndexCacheStart + c->methodIndexCache.count());
-
- while (data.coreIndex() < c->methodIndexCacheStart)
- c = c->_parent;
-
- const QMetaObject *metaObject = c->createMetaObject();
- Q_ASSERT(metaObject);
- m = metaObject->method(data.coreIndex());
- } else {
- m = _m.asT2()->method(data.coreIndex());
- }
-
- type = m.returnType();
- propTypeName = m.typeName();
- }
-
- if (QMetaType::sizeOf(type) <= int(sizeof(int))) {
- if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration)
- return QMetaType::Int;
-
- if (isNamedEnumerator(metaObject(), propTypeName))
- return QMetaType::Int;
-
- if (type == QMetaType::UnknownType) {
- if (unknownTypeError)
- *unknownTypeError = propTypeName;
- }
- } // else we know that it's a known type, as sizeOf(UnknownType) == 0
-
- return type;
-}
-
-int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage,
- QByteArray *unknownTypeError) const
-{
- Q_ASSERT(!_m.isNull() && index >= 0);
-
- if (_m.isT1()) {
- typedef QQmlPropertyCacheMethodArguments A;
-
- QQmlPropertyCache *c = _m.asT1();
- Q_ASSERT(index < c->methodIndexCacheStart + c->methodIndexCache.count());
-
- while (index < c->methodIndexCacheStart)
- c = c->_parent;
-
- QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart));
-
- if (rv->arguments() && static_cast<A *>(rv->arguments())->argumentsValid)
- return static_cast<A *>(rv->arguments())->arguments;
-
- const QMetaObject *metaObject = c->createMetaObject();
- Q_ASSERT(metaObject);
- QMetaMethod m = metaObject->method(index);
-
- int argc = m.parameterCount();
- if (!rv->arguments()) {
- A *args = c->createArgumentsObject(argc, m.parameterNames());
- rv->setArguments(args);
- }
- A *args = static_cast<A *>(rv->arguments());
-
- QList<QByteArray> argTypeNames; // Only loaded if needed
-
- for (int ii = 0; ii < argc; ++ii) {
- int type = m.parameterType(ii);
-
- if (QMetaType::sizeOf(type) > int(sizeof(int))) {
- // Cannot be passed as int
- // We know that it's a known type, as sizeOf(UnknownType) == 0
- } else if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) {
- type = QMetaType::Int;
- } else {
- if (argTypeNames.isEmpty())
- argTypeNames = m.parameterTypes();
- if (isNamedEnumerator(metaObject, argTypeNames.at(ii))) {
- type = QMetaType::Int;
- } else if (type == QMetaType::UnknownType){
- if (unknownTypeError)
- *unknownTypeError = argTypeNames.at(ii);
- return nullptr;
- }
-
- }
- args->arguments[ii + 1] = type;
- }
- args->argumentsValid = true;
- return static_cast<A *>(rv->arguments())->arguments;
-
- } else {
- QMetaMethod m = _m.asT2()->method(index);
- return methodParameterTypes(m, argStorage, unknownTypeError);
-
- }
-}
-
-int *QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, ArgTypeStorage *argStorage,
- QByteArray *unknownTypeError) const
-{
- Q_ASSERT(argStorage);
-
- int argc = m.parameterCount();
- argStorage->resize(argc + 1);
- argStorage->operator[](0) = argc;
- QList<QByteArray> argTypeNames; // Only loaded if needed
-
- for (int ii = 0; ii < argc; ++ii) {
- int type = m.parameterType(ii);
- if (QMetaType::sizeOf(type) > int(sizeof(int))) {
- // Cannot be passed as int
- // We know that it's a known type, as sizeOf(UnknownType) == 0
- } else if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) {
- type = QMetaType::Int;
- } else {
- if (argTypeNames.isEmpty())
- argTypeNames = m.parameterTypes();
- if (isNamedEnumerator(_m.asT2(), argTypeNames.at(ii))) {
- type = QMetaType::Int;
- } else if (type == QMetaType::UnknownType) {
- if (unknownTypeError)
- *unknownTypeError = argTypeNames.at(ii);
- return nullptr;
- }
- }
- argStorage->operator[](ii + 1) = type;
- }
-
- return argStorage->data();
-}
-
-void QQmlObjectOrGadget::metacall(QMetaObject::Call type, int index, void **argv) const
-{
- if (ptr.isNull()) {
- const QMetaObject *metaObject = _m.asT2();
- metaObject->d.static_metacall(nullptr, type, index, argv);
- }
- else if (ptr.isT1()) {
- QMetaObject::metacall(ptr.asT1(), type, index, argv);
- }
- else {
- const QMetaObject *metaObject = _m.asT1()->metaObject();
- QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(type, &metaObject, &index);
- metaObject->d.static_metacall(reinterpret_cast<QObject*>(ptr.asT2()), type, index, argv);
- }
-}
-
-int *QQmlStaticMetaObject::constructorParameterTypes(int index, ArgTypeStorage *dummy,
- QByteArray *unknownTypeError) const
-{
- QMetaMethod m = _m.asT2()->constructor(index);
- return methodParameterTypes(m, dummy, unknownTypeError);
-}
-
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h
index c3c818eb77..bfd78eef88 100644
--- a/src/qml/qml/qqmlpropertycache_p.h
+++ b/src/qml/qml/qqmlpropertycache_p.h
@@ -57,339 +57,25 @@
#include "qqmlnotifier_p.h"
#include <private/qqmlpropertyindex_p.h>
-#include <private/qhashedstring_p.h>
+#include <private/qlinkedstringhash_p.h>
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qvector.h>
#include <private/qv4value_p.h>
+#include <private/qqmlpropertydata_p.h>
+#include <private/qqmlenumdata_p.h>
+#include <private/qqmlenumvalue_p.h>
#include <limits>
QT_BEGIN_NAMESPACE
class QCryptographicHash;
-class QMetaProperty;
-class QQmlEngine;
class QJSEngine;
-class QQmlPropertyData;
class QMetaObjectBuilder;
-class QQmlPropertyCacheMethodArguments;
class QQmlVMEMetaObject;
-template <typename T> class QQmlPropertyCacheCreator;
-template <typename T> class QQmlPropertyCacheAliasCreator;
-
-// We have this somewhat awful split between RawData and Data so that RawData can be
-// used in unions. In normal code, you should always use Data which initializes RawData
-// to an invalid state on construction.
-// ### We should be able to remove this split nowadays
-class QQmlPropertyRawData
-{
-public:
- typedef QObjectPrivate::StaticMetaCallFunction StaticMetaCallFunction;
-
- struct Flags {
- enum Types {
- OtherType = 0,
- FunctionType = 1, // Is an invokable
- QObjectDerivedType = 2, // Property type is a QObject* derived type
- EnumType = 3, // Property type is an enum
- QListType = 4, // Property type is a QML list
- QmlBindingType = 5, // Property type is a QQmlBinding*
- QJSValueType = 6, // Property type is a QScriptValue
- V4HandleType = 7, // Property type is a QQmlV4Handle
- VarPropertyType = 8, // Property type is a "var" property of VMEMO
- QVariantType = 9 // Property is a QVariant
- };
-
- // The _otherBits (which "pad" the Flags struct to align it nicely) are used
- // to store the relative property index. It will only get used when said index fits. See
- // trySetStaticMetaCallFunction for details.
- // (Note: this padding is done here, because certain compilers have surprising behavior
- // when an enum is declared in-between two bit fields.)
- enum { BitsLeftInFlags = 10 };
- unsigned _otherBits : BitsLeftInFlags; // align to 32 bits
-
- // Can apply to all properties, except IsFunction
- unsigned isConstant : 1; // Has CONST flag
- unsigned isWritable : 1; // Has WRITE function
- unsigned isResettable : 1; // Has RESET function
- unsigned isAlias : 1; // Is a QML alias to another property
- unsigned isFinal : 1; // Has FINAL flag
- unsigned isOverridden : 1; // Is overridden by a extension property
- unsigned isDirect : 1; // Exists on a C++ QMetaObject
-
- unsigned type : 4; // stores an entry of Types
-
- // Apply only to IsFunctions
- unsigned isVMEFunction : 1; // Function was added by QML
- unsigned hasArguments : 1; // Function takes arguments
- unsigned isSignal : 1; // Function is a signal
- unsigned isVMESignal : 1; // Signal was added by QML
- unsigned isV4Function : 1; // Function takes QQmlV4Function* args
- unsigned isSignalHandler : 1; // Function is a signal handler
- unsigned isOverload : 1; // Function is an overload of another function
- unsigned isCloned : 1; // The function was marked as cloned
- unsigned isConstructor : 1; // The function was marked is a constructor
-
- // Internal QQmlPropertyCache flags
- unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved
- unsigned overrideIndexIsProperty: 1;
-
- inline Flags();
- inline bool operator==(const Flags &other) const;
- inline void copyPropertyTypeFlags(Flags from);
- };
-
- Flags flags() const { return _flags; }
- void setFlags(Flags f)
- {
- unsigned otherBits = _flags._otherBits;
- _flags = f;
- _flags._otherBits = otherBits;
- }
-
- bool isValid() const { return coreIndex() != -1; }
-
- bool isConstant() const { return _flags.isConstant; }
- bool isWritable() const { return _flags.isWritable; }
- void setWritable(bool onoff) { _flags.isWritable = onoff; }
- bool isResettable() const { return _flags.isResettable; }
- bool isAlias() const { return _flags.isAlias; }
- bool isFinal() const { return _flags.isFinal; }
- bool isOverridden() const { return _flags.isOverridden; }
- bool isDirect() const { return _flags.isDirect; }
- bool hasStaticMetaCallFunction() const { return staticMetaCallFunction() != nullptr; }
- bool isFunction() const { return _flags.type == Flags::FunctionType; }
- bool isQObject() const { return _flags.type == Flags::QObjectDerivedType; }
- bool isEnum() const { return _flags.type == Flags::EnumType; }
- bool isQList() const { return _flags.type == Flags::QListType; }
- bool isQmlBinding() const { return _flags.type == Flags::QmlBindingType; }
- bool isQJSValue() const { return _flags.type == Flags::QJSValueType; }
- bool isV4Handle() const { return _flags.type == Flags::V4HandleType; }
- bool isVarProperty() const { return _flags.type == Flags::VarPropertyType; }
- bool isQVariant() const { return _flags.type == Flags::QVariantType; }
- bool isVMEFunction() const { return _flags.isVMEFunction; }
- bool hasArguments() const { return _flags.hasArguments; }
- bool isSignal() const { return _flags.isSignal; }
- bool isVMESignal() const { return _flags.isVMESignal; }
- bool isV4Function() const { return _flags.isV4Function; }
- bool isSignalHandler() const { return _flags.isSignalHandler; }
- bool isOverload() const { return _flags.isOverload; }
- void setOverload(bool onoff) { _flags.isOverload = onoff; }
- bool isCloned() const { return _flags.isCloned; }
- bool isConstructor() const { return _flags.isConstructor; }
-
- bool hasOverride() const { return overrideIndex() >= 0; }
- bool hasRevision() const { return revision() != 0; }
-
- bool isFullyResolved() const { return !_flags.notFullyResolved; }
-
- int propType() const { Q_ASSERT(isFullyResolved()); return _propType; }
- void setPropType(int pt)
- {
- Q_ASSERT(pt >= 0);
- Q_ASSERT(pt <= std::numeric_limits<qint16>::max());
- _propType = quint16(pt);
- }
-
- int notifyIndex() const { return _notifyIndex; }
- void setNotifyIndex(int idx)
- {
- Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
- Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
- _notifyIndex = qint16(idx);
- }
-
- bool overrideIndexIsProperty() const { return _flags.overrideIndexIsProperty; }
- void setOverrideIndexIsProperty(bool onoff) { _flags.overrideIndexIsProperty = onoff; }
-
- int overrideIndex() const { return _overrideIndex; }
- void setOverrideIndex(int idx)
- {
- Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
- Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
- _overrideIndex = qint16(idx);
- }
-
- int coreIndex() const { return _coreIndex; }
- void setCoreIndex(int idx)
- {
- Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
- Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
- _coreIndex = qint16(idx);
- }
-
- quint8 revision() const { return _revision; }
- void setRevision(quint8 rev)
- {
- Q_ASSERT(rev <= std::numeric_limits<quint8>::max());
- _revision = quint8(rev);
- }
-
- /* If a property is a C++ type, then we store the minor
- * version of this type.
- * This is required to resolve property or signal revisions
- * if this property is used as a grouped property.
- *
- * Test.qml
- * property TextEdit someTextEdit: TextEdit {}
- *
- * Test {
- * someTextEdit.preeditText: "test" //revision 7
- * someTextEdit.onEditingFinished: console.log("test") //revision 6
- * }
- *
- * To determine if these properties with revisions are available we need
- * the minor version of TextEdit as imported in Test.qml.
- *
- */
-
- quint8 typeMinorVersion() const { return _typeMinorVersion; }
- void setTypeMinorVersion(quint8 rev)
- {
- Q_ASSERT(rev <= std::numeric_limits<quint8>::max());
- _typeMinorVersion = quint8(rev);
- }
-
- QQmlPropertyCacheMethodArguments *arguments() const { return _arguments; }
- void setArguments(QQmlPropertyCacheMethodArguments *args) { _arguments = args; }
-
- int metaObjectOffset() const { return _metaObjectOffset; }
- void setMetaObjectOffset(int off)
- {
- Q_ASSERT(off >= std::numeric_limits<qint16>::min());
- Q_ASSERT(off <= std::numeric_limits<qint16>::max());
- _metaObjectOffset = qint16(off);
- }
-
- StaticMetaCallFunction staticMetaCallFunction() const { return _staticMetaCallFunction; }
- void trySetStaticMetaCallFunction(StaticMetaCallFunction f, unsigned relativePropertyIndex)
- {
- if (relativePropertyIndex < (1 << Flags::BitsLeftInFlags) - 1) {
- _flags._otherBits = relativePropertyIndex;
- _staticMetaCallFunction = f;
- }
- }
- quint16 relativePropertyIndex() const { Q_ASSERT(hasStaticMetaCallFunction()); return _flags._otherBits; }
-
-private:
- Flags _flags;
- qint16 _coreIndex = 0;
- quint16 _propType = 0;
-
- // The notify index is in the range returned by QObjectPrivate::signalIndex().
- // This is different from QMetaMethod::methodIndex().
- qint16 _notifyIndex = 0;
- qint16 _overrideIndex = 0;
-
- quint8 _revision = 0;
- quint8 _typeMinorVersion = 0;
- qint16 _metaObjectOffset = 0;
-
- QQmlPropertyCacheMethodArguments *_arguments = nullptr;
- StaticMetaCallFunction _staticMetaCallFunction = nullptr;
-
- friend class QQmlPropertyData;
- friend class QQmlPropertyCache;
-};
-
-#if QT_POINTER_SIZE == 4
-Q_STATIC_ASSERT(sizeof(QQmlPropertyRawData) == 24);
-#else // QT_POINTER_SIZE == 8
-Q_STATIC_ASSERT(sizeof(QQmlPropertyRawData) == 32);
-#endif
-
-class QQmlPropertyData : public QQmlPropertyRawData
-{
-public:
- enum WriteFlag {
- BypassInterceptor = 0x01,
- DontRemoveBinding = 0x02,
- RemoveBindingOnAliasWrite = 0x04
- };
- Q_DECLARE_FLAGS(WriteFlags, WriteFlag)
-
- inline QQmlPropertyData();
- inline QQmlPropertyData(const QQmlPropertyRawData &);
-
- inline bool operator==(const QQmlPropertyRawData &);
-
- static Flags flagsForProperty(const QMetaProperty &);
- void load(const QMetaProperty &);
- void load(const QMetaMethod &);
- QString name(QObject *) const;
- QString name(const QMetaObject *) const;
-
- void markAsOverrideOf(QQmlPropertyData *predecessor);
-
- inline void readProperty(QObject *target, void *property) const
- {
- void *args[] = { property, nullptr };
- readPropertyWithArgs(target, args);
- }
-
- inline void readPropertyWithArgs(QObject *target, void *args[]) const
- {
- if (hasStaticMetaCallFunction())
- staticMetaCallFunction()(target, QMetaObject::ReadProperty, relativePropertyIndex(), args);
- else if (isDirect())
- target->qt_metacall(QMetaObject::ReadProperty, coreIndex(), args);
- else
- QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex(), args);
- }
-
- bool writeProperty(QObject *target, void *value, WriteFlags flags) const
- {
- int status = -1;
- void *argv[] = { value, nullptr, &status, &flags };
- if (flags.testFlag(BypassInterceptor) && hasStaticMetaCallFunction())
- staticMetaCallFunction()(target, QMetaObject::WriteProperty, relativePropertyIndex(), argv);
- else if (flags.testFlag(BypassInterceptor) && isDirect())
- target->qt_metacall(QMetaObject::WriteProperty, coreIndex(), argv);
- else
- QMetaObject::metacall(target, QMetaObject::WriteProperty, coreIndex(), argv);
- return true;
- }
-
- static Flags defaultSignalFlags()
- {
- Flags f;
- f.isSignal = true;
- f.type = Flags::FunctionType;
- f.isVMESignal = true;
- return f;
- }
-
- static Flags defaultSlotFlags()
- {
- Flags f;
- f.type = Flags::FunctionType;
- f.isVMEFunction = true;
- return f;
- }
-
-private:
- friend class QQmlPropertyCache;
- void lazyLoad(const QMetaProperty &);
- void lazyLoad(const QMetaMethod &);
- bool notFullyResolved() const { return _flags.notFullyResolved; }
-};
-
-struct QQmlEnumValue
-{
- QQmlEnumValue() {}
- QQmlEnumValue(const QString &n, int v) : namedValue(n), value(v) {}
- QString namedValue;
- int value = -1;
-};
-
-struct QQmlEnumData
-{
- QString name;
- QVector<QQmlEnumValue> values;
-};
-
class QQmlPropertyCacheMethodArguments;
+
class Q_QML_PRIVATE_EXPORT QQmlPropertyCache : public QQmlRefCount
{
public:
@@ -399,28 +85,26 @@ public:
void update(const QMetaObject *);
void invalidate(const QMetaObject *);
- // Used by qmlpuppet. Remove as soon Creator requires Qt 5.5.
- void invalidate(void *, const QMetaObject *mo) { invalidate(mo); }
QQmlPropertyCache *copy();
QQmlPropertyCache *copyAndAppend(const QMetaObject *,
- QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyData::Flags(),
- QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(),
- QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags());
+ QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(),
+ QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(),
+ QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags());
QQmlPropertyCache *copyAndAppend(const QMetaObject *, int typeMinorVersion,
- QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyData::Flags(),
- QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(),
- QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags());
+ QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(),
+ QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(),
+ QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags());
QQmlPropertyCache *copyAndReserve(int propertyCount,
int methodCount, int signalCount, int enumCount);
- void appendProperty(const QString &, QQmlPropertyRawData::Flags flags, int coreIndex,
+ void appendProperty(const QString &, QQmlPropertyData::Flags flags, int coreIndex,
int propType, int revision, int notifyIndex);
- void appendSignal(const QString &, QQmlPropertyRawData::Flags, int coreIndex,
+ void appendSignal(const QString &, QQmlPropertyData::Flags, int coreIndex,
const int *types = nullptr, const QList<QByteArray> &names = QList<QByteArray>());
- void appendMethod(const QString &, QQmlPropertyData::Flags flags, int coreIndex,
- const QList<QByteArray> &names = QList<QByteArray>());
+ void appendMethod(const QString &, QQmlPropertyData::Flags flags, int coreIndex, int returnType,
+ const QList<QByteArray> &names, const QVector<int> &parameterTypes);
void appendEnum(const QString &, const QVector<QQmlEnumValue> &);
const QMetaObject *metaObject() const;
@@ -488,6 +172,10 @@ public:
static bool addToHash(QCryptographicHash &hash, const QMetaObject &mo);
QByteArray checksum(bool *ok);
+
+ int allowedRevision(int index) const { return allowedRevisionCache[index]; }
+ void setAllowedRevision(int index, int allowed) { allowedRevisionCache[index] = allowed; }
+
private:
friend class QQmlEnginePrivate;
friend class QQmlCompiler;
@@ -495,19 +183,18 @@ private:
template <typename T> friend class QQmlPropertyCacheAliasCreator;
friend class QQmlComponentAndAliasResolver;
friend class QQmlMetaObject;
- friend struct QQmlMetaTypeData;
inline QQmlPropertyCache *copy(int reserve);
void append(const QMetaObject *, int typeMinorVersion,
- QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyRawData::Flags(),
- QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(),
- QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags());
+ QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(),
+ QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(),
+ QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags());
QQmlPropertyCacheMethodArguments *createArgumentsObject(int count, const QList<QByteArray> &names);
typedef QVector<QQmlPropertyData> IndexCache;
- typedef QStringMultiHash<QPair<int, QQmlPropertyData *> > StringCache;
+ typedef QLinkedStringMultiHash<QPair<int, QQmlPropertyData *> > StringCache;
typedef QVector<int> AllowedRevisionCache;
QQmlPropertyData *findProperty(StringCache::ConstIterator it, QObject *, QQmlContextData *) const;
@@ -556,172 +243,6 @@ private:
QByteArray _checksum;
};
-typedef QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCachePtr;
-
-// QQmlMetaObject serves as a wrapper around either QMetaObject or QQmlPropertyCache.
-// This is necessary as we delay creation of QMetaObject for synthesized QObjects, but
-// we don't want to needlessly generate QQmlPropertyCaches every time we encounter a
-// QObject type used in assignment or when we don't have a QQmlEngine etc.
-//
-// This class does NOT reference the propertycache.
-class QQmlEnginePrivate;
-class Q_QML_EXPORT QQmlMetaObject
-{
-public:
- typedef QVarLengthArray<int, 9> ArgTypeStorage;
-
- inline QQmlMetaObject();
- inline QQmlMetaObject(QObject *);
- inline QQmlMetaObject(const QMetaObject *);
- inline QQmlMetaObject(QQmlPropertyCache *);
- inline QQmlMetaObject(const QQmlMetaObject &);
-
- inline QQmlMetaObject &operator=(const QQmlMetaObject &);
-
- inline bool isNull() const;
-
- inline const char *className() const;
- inline int propertyCount() const;
-
- inline bool hasMetaObject() const;
- inline const QMetaObject *metaObject() const;
-
- QQmlPropertyCache *propertyCache(QQmlEnginePrivate *) const;
-
- int methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const;
- int *methodParameterTypes(int index, ArgTypeStorage *argStorage,
- QByteArray *unknownTypeError) const;
-
- static bool canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to);
-
- // static_metacall (on Gadgets) doesn't call the base implementation and therefore
- // we need a helper to find the correct meta object and property/method index.
- static void resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index);
-
-protected:
- QBiPointer<QQmlPropertyCache, const QMetaObject> _m;
- int *methodParameterTypes(const QMetaMethod &method, ArgTypeStorage *argStorage,
- QByteArray *unknownTypeError) const;
-
-};
-
-class QQmlObjectOrGadget: public QQmlMetaObject
-{
-public:
- QQmlObjectOrGadget(QObject *obj)
- : QQmlMetaObject(obj),
- ptr(obj)
- {}
- QQmlObjectOrGadget(QQmlPropertyCache *propertyCache, void *gadget)
- : QQmlMetaObject(propertyCache)
- , ptr(gadget)
- {}
-
- void metacall(QMetaObject::Call type, int index, void **argv) const;
-
-private:
- QBiPointer<QObject, void> ptr;
-
-protected:
- QQmlObjectOrGadget(const QMetaObject* metaObject)
- : QQmlMetaObject(metaObject)
- {}
-
-};
-
-class QQmlStaticMetaObject : public QQmlObjectOrGadget {
-public:
- QQmlStaticMetaObject(const QMetaObject* metaObject)
- : QQmlObjectOrGadget(metaObject)
- {}
- int *constructorParameterTypes(int index, ArgTypeStorage *dummy, QByteArray *unknownTypeError) const;
-};
-
-QQmlPropertyRawData::Flags::Flags()
- : _otherBits(0)
- , isConstant(false)
- , isWritable(false)
- , isResettable(false)
- , isAlias(false)
- , isFinal(false)
- , isOverridden(false)
- , isDirect(false)
- , type(OtherType)
- , isVMEFunction(false)
- , hasArguments(false)
- , isSignal(false)
- , isVMESignal(false)
- , isV4Function(false)
- , isSignalHandler(false)
- , isOverload(false)
- , isCloned(false)
- , isConstructor(false)
- , notFullyResolved(false)
- , overrideIndexIsProperty(false)
-{}
-
-bool QQmlPropertyRawData::Flags::operator==(const QQmlPropertyRawData::Flags &other) const
-{
- return isConstant == other.isConstant &&
- isWritable == other.isWritable &&
- isResettable == other.isResettable &&
- isAlias == other.isAlias &&
- isFinal == other.isFinal &&
- isOverridden == other.isOverridden &&
- type == other.type &&
- isVMEFunction == other.isVMEFunction &&
- hasArguments == other.hasArguments &&
- isSignal == other.isSignal &&
- isVMESignal == other.isVMESignal &&
- isV4Function == other.isV4Function &&
- isSignalHandler == other.isSignalHandler &&
- isOverload == other.isOverload &&
- isCloned == other.isCloned &&
- isConstructor == other.isConstructor &&
- notFullyResolved == other.notFullyResolved &&
- overrideIndexIsProperty == other.overrideIndexIsProperty;
-}
-
-void QQmlPropertyRawData::Flags::copyPropertyTypeFlags(QQmlPropertyRawData::Flags from)
-{
- switch (from.type) {
- case QObjectDerivedType:
- case EnumType:
- case QListType:
- case QmlBindingType:
- case QJSValueType:
- case V4HandleType:
- case QVariantType:
- type = from.type;
- }
-}
-
-QQmlPropertyData::QQmlPropertyData()
-{
- setCoreIndex(-1);
- setPropType(0);
- setNotifyIndex(-1);
- setOverrideIndex(-1);
- setRevision(0);
- setMetaObjectOffset(-1);
- setArguments(nullptr);
- trySetStaticMetaCallFunction(nullptr, 0);
-}
-
-QQmlPropertyData::QQmlPropertyData(const QQmlPropertyRawData &d)
-{
- *(static_cast<QQmlPropertyRawData *>(this)) = d;
-}
-
-bool QQmlPropertyData::operator==(const QQmlPropertyRawData &other)
-{
- return flags() == other.flags() &&
- propType() == other.propType() &&
- coreIndex() == other.coreIndex() &&
- notifyIndex() == other.notifyIndex() &&
- revision() == other.revision();
-}
-
inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) const
{
if (p && Q_UNLIKELY(p->notFullyResolved()))
@@ -880,124 +401,6 @@ bool QQmlPropertyCache::callJSFactoryMethod(QObject *object, void **args) const
return false;
}
-QQmlMetaObject::QQmlMetaObject()
-{
-}
-
-QQmlMetaObject::QQmlMetaObject(QObject *o)
-{
- if (o) {
- QQmlData *ddata = QQmlData::get(o, false);
- if (ddata && ddata->propertyCache) _m = ddata->propertyCache;
- else _m = o->metaObject();
- }
-}
-
-QQmlMetaObject::QQmlMetaObject(const QMetaObject *m)
-: _m(m)
-{
-}
-
-QQmlMetaObject::QQmlMetaObject(QQmlPropertyCache *m)
-: _m(m)
-{
-}
-
-QQmlMetaObject::QQmlMetaObject(const QQmlMetaObject &o)
-: _m(o._m)
-{
-}
-
-QQmlMetaObject &QQmlMetaObject::operator=(const QQmlMetaObject &o)
-{
- _m = o._m;
- return *this;
-}
-
-bool QQmlMetaObject::isNull() const
-{
- return _m.isNull();
-}
-
-const char *QQmlMetaObject::className() const
-{
- if (_m.isNull()) {
- return nullptr;
- } else if (_m.isT1()) {
- return _m.asT1()->className();
- } else {
- return _m.asT2()->className();
- }
-}
-
-int QQmlMetaObject::propertyCount() const
-{
- if (_m.isNull()) {
- return 0;
- } else if (_m.isT1()) {
- return _m.asT1()->propertyCount();
- } else {
- return _m.asT2()->propertyCount();
- }
-}
-
-bool QQmlMetaObject::hasMetaObject() const
-{
- return _m.isT2() || (!_m.isNull() && _m.asT1()->metaObject());
-}
-
-const QMetaObject *QQmlMetaObject::metaObject() const
-{
- if (_m.isNull()) return nullptr;
- if (_m.isT1()) return _m.asT1()->createMetaObject();
- else return _m.asT2();
-}
-
-class QQmlPropertyCacheVector
-{
-public:
- QQmlPropertyCacheVector() {}
- QQmlPropertyCacheVector(QQmlPropertyCacheVector &&other)
- : data(std::move(other.data)) {}
- QQmlPropertyCacheVector &operator=(QQmlPropertyCacheVector &&other) {
- QVector<QFlagPointer<QQmlPropertyCache>> moved(std::move(other.data));
- data.swap(moved);
- return *this;
- }
-
- ~QQmlPropertyCacheVector() { clear(); }
- void resize(int size) { return data.resize(size); }
- int count() const { return data.count(); }
- void clear()
- {
- for (int i = 0; i < data.count(); ++i) {
- if (QQmlPropertyCache *cache = data.at(i).data())
- cache->release();
- }
- data.clear();
- }
-
- void append(QQmlPropertyCache *cache) { cache->addref(); data.append(cache); }
- QQmlPropertyCache *at(int index) const { return data.at(index).data(); }
- void set(int index, const QQmlRefPointer<QQmlPropertyCache> &replacement) {
- if (QQmlPropertyCache *oldCache = data.at(index).data()) {
- if (replacement.data() == oldCache)
- return;
- oldCache->release();
- }
- data[index] = replacement.data();
- replacement->addref();
- }
-
- void setNeedsVMEMetaObject(int index) { data[index].setFlag(); }
- bool needsVMEMetaObject(int index) const { return data.at(index).flag(); }
-private:
- Q_DISABLE_COPY(QQmlPropertyCacheVector)
- QVector<QFlagPointer<QQmlPropertyCache>> data;
-};
-
-Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyData::WriteFlags)
-
QT_END_NAMESPACE
#endif // QQMLPROPERTYCACHE_P_H
diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/qml/qqmlpropertycachecreator.cpp
index fb54da5b73..034ebfc743 100644
--- a/src/qml/compiler/qqmlpropertycachecreator.cpp
+++ b/src/qml/qml/qqmlpropertycachecreator.cpp
@@ -45,6 +45,35 @@ QT_BEGIN_NAMESPACE
QAtomicInt QQmlPropertyCacheCreatorBase::classIndexCounter(0);
+
+int QQmlPropertyCacheCreatorBase::metaTypeForPropertyType(QV4::CompiledData::BuiltinType type)
+{
+ switch (type) {
+ case QV4::CompiledData::BuiltinType::Var: return QMetaType::QVariant;
+ case QV4::CompiledData::BuiltinType::Variant: return QMetaType::QVariant;
+ case QV4::CompiledData::BuiltinType::Int: return QMetaType::Int;
+ case QV4::CompiledData::BuiltinType::Bool: return QMetaType::Bool;
+ case QV4::CompiledData::BuiltinType::Real: return QMetaType::Double;
+ case QV4::CompiledData::BuiltinType::String: return QMetaType::QString;
+ case QV4::CompiledData::BuiltinType::Url: return QMetaType::QUrl;
+ case QV4::CompiledData::BuiltinType::Color: return QMetaType::QColor;
+ case QV4::CompiledData::BuiltinType::Font: return QMetaType::QFont;
+ case QV4::CompiledData::BuiltinType::Time: return QMetaType::QTime;
+ case QV4::CompiledData::BuiltinType::Date: return QMetaType::QDate;
+ case QV4::CompiledData::BuiltinType::DateTime: return QMetaType::QDateTime;
+ case QV4::CompiledData::BuiltinType::Rect: return QMetaType::QRectF;
+ case QV4::CompiledData::BuiltinType::Point: return QMetaType::QPointF;
+ case QV4::CompiledData::BuiltinType::Size: return QMetaType::QSizeF;
+ case QV4::CompiledData::BuiltinType::Vector2D: return QMetaType::QVector2D;
+ case QV4::CompiledData::BuiltinType::Vector3D: return QMetaType::QVector3D;
+ case QV4::CompiledData::BuiltinType::Vector4D: return QMetaType::QVector4D;
+ case QV4::CompiledData::BuiltinType::Matrix4x4: return QMetaType::QMatrix4x4;
+ case QV4::CompiledData::BuiltinType::Quaternion: return QMetaType::QQuaternion;
+ case QV4::CompiledData::BuiltinType::InvalidBuiltin: break;
+ };
+ return QMetaType::UnknownType;
+}
+
QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding,
const QString &instantiatingPropertyName, QQmlPropertyCache *referencingObjectPropertyCache)
: referencingObjectIndex(referencingObjectIndex)
@@ -64,7 +93,9 @@ bool QQmlBindingInstantiationContext::resolveInstantiatingProperty()
Q_ASSERT(instantiatingBinding->propertyNameIndex != 0);
bool notInRevision = false;
- instantiatingProperty = QmlIR::PropertyResolver(referencingObjectPropertyCache).property(instantiatingPropertyName, &notInRevision, QmlIR::PropertyResolver::IgnoreRevision);
+ instantiatingProperty = QQmlPropertyResolver(referencingObjectPropertyCache)
+ .property(instantiatingPropertyName, &notInRevision,
+ QQmlPropertyResolver::IgnoreRevision);
return instantiatingProperty != nullptr;
}
diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h
index 901602d17b..39778aa328 100644
--- a/src/qml/compiler/qqmlpropertycachecreator_p.h
+++ b/src/qml/qml/qqmlpropertycachecreator_p.h
@@ -52,9 +52,22 @@
#include <private/qqmlvaluetype_p.h>
#include <private/qqmlengine_p.h>
+#include <private/qqmlmetaobject_p.h>
+#include <private/qqmlpropertyresolver_p.h>
+#include <private/qqmltypedata_p.h>
QT_BEGIN_NAMESPACE
+inline QQmlJS::DiagnosticMessage qQmlCompileError(const QV4::CompiledData::Location &location,
+ const QString &description)
+{
+ QQmlJS::DiagnosticMessage error;
+ error.line = location.line;
+ error.column = location.column;
+ error.message = description;
+ return error;
+}
+
struct QQmlBindingInstantiationContext {
QQmlBindingInstantiationContext() {}
QQmlBindingInstantiationContext(int referencingObjectIndex,
@@ -82,6 +95,8 @@ struct QQmlPropertyCacheCreatorBase
Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreatorBase)
public:
static QAtomicInt classIndexCounter;
+
+ static int metaTypeForPropertyType(QV4::CompiledData::BuiltinType type);
};
template <typename ObjectContainer>
@@ -95,12 +110,14 @@ public:
QQmlEnginePrivate *enginePrivate,
const ObjectContainer *objectContainer, const QQmlImports *imports);
- QQmlCompileError buildMetaObjects();
+ QQmlJS::DiagnosticMessage buildMetaObjects();
protected:
- QQmlCompileError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context);
- QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const;
- QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache);
+ QQmlJS::DiagnosticMessage buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context);
+ QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlJS::DiagnosticMessage *error) const;
+ QQmlJS::DiagnosticMessage createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache);
+
+ int metaTypeForParameter(const QV4::CompiledData::ParameterType &param, QString *customTypeName = nullptr);
QString stringAt(int index) const { return objectContainer->stringAt(index); }
@@ -126,18 +143,27 @@ inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlP
}
template <typename ObjectContainer>
-inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects()
+inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects()
{
QQmlBindingInstantiationContext context;
return buildMetaObjectRecursively(/*root object*/0, context);
}
template <typename ObjectContainer>
-inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context)
+inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context)
{
+ auto isAddressable = [](const QUrl &url) {
+ const QString fileName = url.fileName();
+ return !fileName.isEmpty() && fileName.front().isUpper();
+ };
+
const CompiledObject *obj = objectContainer->objectAt(objectIndex);
+ bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0
+ || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0
+ || (((obj->flags & QV4::CompiledData::Object::IsComponent)
+ || (objectIndex == 0 && isAddressable(objectContainer->url())))
+ && !objectContainer->resolvedType(obj->inheritedTypeNameIndex)->isFullyDynamicType);
- bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0;
if (!needVMEMetaObject) {
auto binding = obj->bindingsBegin();
auto end = obj->bindingsEnd();
@@ -153,8 +179,8 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje
auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex);
Q_ASSERT(typeRef);
QQmlRefPointer<QQmlPropertyCache> baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
- QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache);
- if (error.isSet())
+ QQmlJS::DiagnosticMessage error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache);
+ if (error.isValid())
return error;
}
} else {
@@ -168,16 +194,16 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje
QQmlRefPointer<QQmlPropertyCache> baseTypeCache;
{
- QQmlCompileError error;
+ QQmlJS::DiagnosticMessage error;
baseTypeCache = propertyCacheForObject(obj, context, &error);
- if (error.isSet())
+ if (error.isValid())
return error;
}
if (baseTypeCache) {
if (needVMEMetaObject) {
- QQmlCompileError error = createMetaObject(objectIndex, obj, baseTypeCache);
- if (error.isSet())
+ QQmlJS::DiagnosticMessage error = createMetaObject(objectIndex, obj, baseTypeCache);
+ if (error.isValid())
return error;
} else {
propertyCaches->set(objectIndex, baseTypeCache);
@@ -198,18 +224,18 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje
if (!context.resolveInstantiatingProperty())
pendingGroupPropertyBindings->append(context);
- QQmlCompileError error = buildMetaObjectRecursively(binding->value.objectIndex, context);
- if (error.isSet())
+ QQmlJS::DiagnosticMessage error = buildMetaObjectRecursively(binding->value.objectIndex, context);
+ if (error.isValid())
return error;
}
}
- QQmlCompileError noError;
+ QQmlJS::DiagnosticMessage noError;
return noError;
}
template <typename ObjectContainer>
-inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const
+inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlJS::DiagnosticMessage *error) const
{
if (context.instantiatingProperty) {
return context.instantiatingPropertyCache(enginePrivate);
@@ -219,15 +245,15 @@ inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContaine
if (typeRef->isFullyDynamicType) {
if (obj->propertyCount() > 0 || obj->aliasCount() > 0) {
- *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new properties."));
+ *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new properties."));
return nullptr;
}
if (obj->signalCount() > 0) {
- *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new signals."));
+ *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new signals."));
return nullptr;
}
if (obj->functionCount() > 0) {
- *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully Dynamic types cannot declare new functions."));
+ *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully Dynamic types cannot declare new functions."));
return nullptr;
}
}
@@ -239,22 +265,13 @@ inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContaine
Q_ASSERT(typeRef);
QQmlType qmltype = typeRef->type;
if (!qmltype.isValid()) {
- QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex);
- if (imports->resolveType(propertyName, &qmltype, nullptr, nullptr, nullptr)) {
- if (qmltype.isComposite()) {
- QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
- Q_ASSERT(tdata);
- Q_ASSERT(tdata->isComplete());
-
- auto compilationUnit = tdata->compilationUnit();
- qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId);
- }
- }
+ imports->resolveType(stringAt(context.instantiatingBinding->propertyNameIndex),
+ &qmltype, nullptr, nullptr, nullptr);
}
const QMetaObject *attachedMo = qmltype.attachedPropertiesType(enginePrivate);
if (!attachedMo) {
- *error = QQmlCompileError(context.instantiatingBinding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object"));
+ *error = qQmlCompileError(context.instantiatingBinding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object"));
return nullptr;
}
return enginePrivate->cache(attachedMo);
@@ -263,7 +280,7 @@ inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContaine
}
template <typename ObjectContainer>
-inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache)
+inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache)
{
QQmlRefPointer<QQmlPropertyCache> cache;
cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(),
@@ -273,33 +290,6 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
propertyCaches->set(objectIndex, cache);
propertyCaches->setNeedsVMEMetaObject(objectIndex);
- struct TypeData {
- QV4::CompiledData::Property::Type dtype;
- int metaType;
- } builtinTypes[] = {
- { QV4::CompiledData::Property::Var, QMetaType::QVariant },
- { QV4::CompiledData::Property::Variant, QMetaType::QVariant },
- { QV4::CompiledData::Property::Int, QMetaType::Int },
- { QV4::CompiledData::Property::Bool, QMetaType::Bool },
- { QV4::CompiledData::Property::Real, QMetaType::Double },
- { QV4::CompiledData::Property::String, QMetaType::QString },
- { QV4::CompiledData::Property::Url, QMetaType::QUrl },
- { QV4::CompiledData::Property::Color, QMetaType::QColor },
- { QV4::CompiledData::Property::Font, QMetaType::QFont },
- { QV4::CompiledData::Property::Time, QMetaType::QTime },
- { QV4::CompiledData::Property::Date, QMetaType::QDate },
- { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime },
- { QV4::CompiledData::Property::Rect, QMetaType::QRectF },
- { QV4::CompiledData::Property::Point, QMetaType::QPointF },
- { QV4::CompiledData::Property::Size, QMetaType::QSizeF },
- { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D },
- { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D },
- { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D },
- { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 },
- { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion }
-};
- static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData);
-
QByteArray newClassName;
if (objectIndex == /*root object*/0) {
@@ -322,18 +312,18 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
int varPropCount = 0;
- QmlIR::PropertyResolver resolver(baseTypeCache);
+ QQmlPropertyResolver resolver(baseTypeCache);
auto p = obj->propertiesBegin();
auto pend = obj->propertiesEnd();
for ( ; p != pend; ++p) {
- if (p->type == QV4::CompiledData::Property::Var)
+ if (p->builtinType() == QV4::CompiledData::BuiltinType::Var)
varPropCount++;
bool notInRevision = false;
QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), &notInRevision);
if (d && d->isFinal())
- return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property"));
+ return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property"));
}
auto a = obj->aliasesBegin();
@@ -342,7 +332,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
bool notInRevision = false;
QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), &notInRevision);
if (d && d->isFinal())
- return QQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property"));
+ return qQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property"));
}
int effectivePropertyIndex = cache->propertyIndexCacheStart;
@@ -427,29 +417,13 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
auto end = s->parametersEnd();
for ( ; param != end; ++param, ++i) {
names.append(stringAt(param->nameIndex).toUtf8());
- if (param->type < builtinTypeCount) {
- // built-in type
- paramTypes[i + 1] = builtinTypes[param->type].metaType;
- } else {
- // lazily resolved type
- Q_ASSERT(param->type == QV4::CompiledData::Property::Custom);
- const QString customTypeName = stringAt(param->customTypeNameIndex);
- QQmlType qmltype;
- if (!imports->resolveType(customTypeName, &qmltype, nullptr, nullptr, nullptr))
- return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName));
-
- if (qmltype.isComposite()) {
- QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
- Q_ASSERT(tdata);
- Q_ASSERT(tdata->isComplete());
-
- auto compilationUnit = tdata->compilationUnit();
-
- paramTypes[i + 1] = compilationUnit->metaTypeId;
- } else {
- paramTypes[i + 1] = qmltype.typeId();
- }
- }
+
+ QString customTypeName;
+ auto type = metaTypeForParameter(param->type, &customTypeName);
+ if (type == QMetaType::UnknownType)
+ return qQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName));
+
+ paramTypes[i + 1] = type;
}
}
@@ -459,7 +433,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
QString signalName = stringAt(s->nameIndex);
if (seenSignals.contains(signalName))
- return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Duplicate signal name: invalid override of property change signal or superclass signal"));
+ return qQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Duplicate signal name: invalid override of property change signal or superclass signal"));
seenSignals.insert(signalName);
cache->appendSignal(signalName, flags, effectiveMethodIndex++,
@@ -475,19 +449,28 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
const QString slotName = stringAt(function->nameIndex);
if (seenSignals.contains(slotName))
- return QQmlCompileError(function->location, QQmlPropertyCacheCreatorBase::tr("Duplicate method name: invalid override of property change signal or superclass signal"));
+ return qQmlCompileError(function->location, QQmlPropertyCacheCreatorBase::tr("Duplicate method name: invalid override of property change signal or superclass signal"));
// Note: we don't append slotName to the seenSignals list, since we don't
// protect against overriding change signals or methods with properties.
QList<QByteArray> parameterNames;
+ QVector<int> parameterTypes;
auto formal = function->formalsBegin();
auto end = function->formalsEnd();
for ( ; formal != end; ++formal) {
flags.hasArguments = true;
- parameterNames << stringAt(*formal).toUtf8();
+ parameterNames << stringAt(formal->nameIndex).toUtf8();
+ int type = metaTypeForParameter(formal->type);
+ if (type == QMetaType::UnknownType)
+ type = QMetaType::QVariant;
+ parameterTypes << type;
}
- cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames);
+ int returnType = metaTypeForParameter(function->returnType);
+ if (returnType == QMetaType::UnknownType)
+ returnType = QMetaType::QVariant;
+
+ cache->appendMethod(slotName, flags, effectiveMethodIndex++, returnType, parameterNames, parameterTypes);
}
@@ -501,21 +484,23 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
int propertTypeMinorVersion = 0;
QQmlPropertyData::Flags propertyFlags;
- if (p->type == QV4::CompiledData::Property::Var) {
- propertyType = QMetaType::QVariant;
+ const QV4::CompiledData::BuiltinType type = p->builtinType();
+
+ if (type == QV4::CompiledData::BuiltinType::Var)
propertyFlags.type = QQmlPropertyData::Flags::VarPropertyType;
- } else if (p->type < builtinTypeCount) {
- propertyType = builtinTypes[p->type].metaType;
- if (p->type == QV4::CompiledData::Property::Variant)
+
+ if (type != QV4::CompiledData::BuiltinType::InvalidBuiltin) {
+ propertyType = metaTypeForPropertyType(type);
+
+ if (type == QV4::CompiledData::BuiltinType::Variant)
propertyFlags.type = QQmlPropertyData::Flags::QVariantType;
} else {
- Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList ||
- p->type == QV4::CompiledData::Property::Custom);
+ Q_ASSERT(!p->isBuiltinType);
QQmlType qmltype;
- if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, nullptr, nullptr, nullptr)) {
- return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type"));
+ if (!imports->resolveType(stringAt(p->builtinTypeOrTypeNameIndex), &qmltype, nullptr, nullptr, nullptr)) {
+ return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type"));
}
Q_ASSERT(qmltype.isValid());
@@ -526,27 +511,27 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
auto compilationUnit = tdata->compilationUnit();
- if (p->type == QV4::CompiledData::Property::Custom) {
- propertyType = compilationUnit->metaTypeId;
- } else {
+ if (p->isList) {
propertyType = compilationUnit->listMetaTypeId;
+ } else {
+ propertyType = compilationUnit->metaTypeId;
}
} else {
- if (p->type == QV4::CompiledData::Property::Custom) {
+ if (p->isList) {
+ propertyType = qmltype.qListTypeId();
+ } else {
propertyType = qmltype.typeId();
propertTypeMinorVersion = qmltype.minorVersion();
- } else {
- propertyType = qmltype.qListTypeId();
}
}
- if (p->type == QV4::CompiledData::Property::Custom)
- propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType;
- else
+ if (p->isList)
propertyFlags.type = QQmlPropertyData::Flags::QListType;
+ else
+ propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType;
}
- if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList)
+ if (!p->isReadOnly && !p->isList)
propertyFlags.isWritable = true;
@@ -559,11 +544,40 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
effectiveSignalIndex++;
}
- QQmlCompileError noError;
+ QQmlJS::DiagnosticMessage noError;
return noError;
}
template <typename ObjectContainer>
+inline int QQmlPropertyCacheCreator<ObjectContainer>::metaTypeForParameter(const QV4::CompiledData::ParameterType &param,
+ QString *customTypeName)
+{
+ if (param.indexIsBuiltinType) {
+ // built-in type
+ return metaTypeForPropertyType(static_cast<QV4::CompiledData::BuiltinType>(int(param.typeNameIndexOrBuiltinType)));
+ }
+
+ // lazily resolved type
+ const QString typeName = stringAt(param.typeNameIndexOrBuiltinType);
+ if (customTypeName)
+ *customTypeName = typeName;
+ QQmlType qmltype;
+ if (!imports->resolveType(typeName, &qmltype, nullptr, nullptr, nullptr))
+ return QMetaType::UnknownType;
+
+ if (!qmltype.isComposite())
+ return qmltype.typeId();
+
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
+ Q_ASSERT(tdata);
+ Q_ASSERT(tdata->isComplete());
+
+ auto compilationUnit = tdata->compilationUnit();
+
+ return compilationUnit->metaTypeId;
+}
+
+template <typename ObjectContainer>
class QQmlPropertyCacheAliasCreator
{
public:
@@ -571,13 +585,13 @@ public:
QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer);
- void appendAliasPropertiesToMetaObjects();
+ void appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv);
- QQmlCompileError appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex);
+ QQmlJS::DiagnosticMessage appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv);
private:
- void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex);
- QQmlCompileError propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyRawData::Flags *propertyFlags);
+ void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv);
+ QQmlJS::DiagnosticMessage propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv);
void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const;
@@ -596,7 +610,7 @@ inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCre
}
template <typename ObjectContainer>
-inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects()
+inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv)
{
// skip the root object (index 0) as that one does not have a first object index originating
// from a binding.
@@ -606,15 +620,15 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertie
continue;
const auto rootBinding = component.bindingsBegin();
- appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex);
+ appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex, enginePriv);
}
const int rootObjectIndex = 0;
- appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex);
+ appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex, enginePriv);
}
template <typename ObjectContainer>
-inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex)
+inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv)
{
QVector<int> objectsWithAliases;
collectObjectsWithAliasesRecursively(firstObjectIndex, &objectsWithAliases);
@@ -654,7 +668,7 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertie
const CompiledObject &object = *objectContainer->objectAt(objectIndex);
if (allAliasTargetsExist(object)) {
- appendAliasesToPropertyCache(component, objectIndex);
+ appendAliasesToPropertyCache(component, objectIndex, enginePriv);
} else {
pendingObjects.append(objectIndex);
}
@@ -688,9 +702,8 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAl
}
template <typename ObjectContainer>
-inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(
- const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion,
- QQmlPropertyData::Flags *propertyFlags)
+inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion,
+ QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv)
{
*type = 0;
bool writable = false;
@@ -714,7 +727,7 @@ inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::property
const QV4::CompiledData::Alias *targetAlias = &(*nextAlias);
if (seenAliases.contains(targetAlias)) {
- return QQmlCompileError(targetAlias->location,
+ return qQmlCompileError(targetAlias->location,
QQmlPropertyCacheCreatorBase::tr("Cyclic alias"));
}
@@ -722,7 +735,7 @@ inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::property
lastAlias = targetAlias;
} while (lastAlias->aliasToLocalAlias);
- return propertyDataForAlias(component, *lastAlias, type, minorVersion, propertyFlags);
+ return propertyDataForAlias(component, *lastAlias, type, minorVersion, propertyFlags, enginePriv);
}
const int targetObjectIndex = objectForId(component, alias.targetObjectId);
@@ -736,7 +749,8 @@ inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::property
// Can be caused by the alias target not being a valid id or property. E.g.:
// property alias dataValue: dataVal
// invalidAliasComponent { id: dataVal }
- return QQmlCompileError(targetObject.location, QQmlPropertyCacheCreatorBase::tr("Invalid alias target"));
+ return qQmlCompileError(targetObject.location,
+ QQmlPropertyCacheCreatorBase::tr("Invalid alias target"));
}
if (typeRef->type.isValid())
@@ -753,45 +767,62 @@ inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::property
QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex);
Q_ASSERT(targetCache);
+
QQmlPropertyData *targetProperty = targetCache->property(coreIndex);
Q_ASSERT(targetProperty);
- *type = targetProperty->propType();
+ // for deep aliases, valueTypeIndex is always set
+ if (!QQmlValueTypeFactory::isValueType(targetProperty->propType()) && valueTypeIndex != -1) {
+ // deep alias property
+ *type = targetProperty->propType();
+ targetCache = enginePriv->propertyCacheForType(*type);
+ Q_ASSERT(targetCache);
+ targetProperty = targetCache->property(valueTypeIndex);
- writable = targetProperty->isWritable();
- resettable = targetProperty->isResettable();
- if (valueTypeIndex != -1) {
- const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type);
- if (valueTypeMetaObject->property(valueTypeIndex).isEnumType())
- *type = QVariant::Int;
- else
- *type = valueTypeMetaObject->property(valueTypeIndex).userType();
+ *type = targetProperty->propType();
+ writable = targetProperty->isWritable();
+ resettable = targetProperty->isResettable();
+
} else {
- if (targetProperty->isEnum()) {
- *type = QVariant::Int;
+ // value type or primitive type or enum
+ *type = targetProperty->propType();
+
+ writable = targetProperty->isWritable();
+ resettable = targetProperty->isResettable();
+
+ if (valueTypeIndex != -1) {
+ const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type);
+ if (valueTypeMetaObject->property(valueTypeIndex).isEnumType())
+ *type = QVariant::Int;
+ else
+ *type = valueTypeMetaObject->property(valueTypeIndex).userType();
} else {
- // Copy type flags
- propertyFlags->copyPropertyTypeFlags(targetProperty->flags());
+ if (targetProperty->isEnum()) {
+ *type = QVariant::Int;
+ } else {
+ // Copy type flags
+ propertyFlags->copyPropertyTypeFlags(targetProperty->flags());
- if (targetProperty->isVarProperty())
- propertyFlags->type = QQmlPropertyData::Flags::QVariantType;
+ if (targetProperty->isVarProperty())
+ propertyFlags->type = QQmlPropertyData::Flags::QVariantType;
+ }
}
}
}
- propertyFlags->isWritable = !(alias.flags & QV4::CompiledData::Property::IsReadOnly) && writable;
+ propertyFlags->isWritable = !(alias.flags & QV4::CompiledData::Alias::IsReadOnly) && writable;
propertyFlags->isResettable = resettable;
- return QQmlCompileError();
+ return QQmlJS::DiagnosticMessage();
}
template <typename ObjectContainer>
-inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache(
- const CompiledObject &component, int objectIndex)
+inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache(
+ const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv)
{
const CompiledObject &object = *objectContainer->objectAt(objectIndex);
if (!object.aliasCount())
- return QQmlCompileError();
+ return QQmlJS::DiagnosticMessage();
QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex);
Q_ASSERT(propertyCache);
@@ -808,8 +839,8 @@ inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAl
int type = 0;
int minorVersion = 0;
QQmlPropertyData::Flags propertyFlags;
- QQmlCompileError error = propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags);
- if (error.isSet())
+ QQmlJS::DiagnosticMessage error = propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags, enginePriv);
+ if (error.isValid())
return error;
const QString propertyName = objectContainer->stringAt(alias->nameIndex);
@@ -821,7 +852,7 @@ inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAl
type, minorVersion, effectiveSignalIndex++);
}
- return QQmlCompileError();
+ return QQmlJS::DiagnosticMessage();
}
template <typename ObjectContainer>
diff --git a/src/qml/qml/qqmlpropertycachemethodarguments_p.h b/src/qml/qml/qqmlpropertycachemethodarguments_p.h
new file mode 100644
index 0000000000..62f09bdfff
--- /dev/null
+++ b/src/qml/qml/qqmlpropertycachemethodarguments_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLPROPERTYCACHEMETODARGUMENTS_P_H
+#define QQMLPROPERTYCACHEMETODARGUMENTS_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/qlist.h>
+#include <QtCore/qbytearray.h>
+
+QT_BEGIN_NAMESPACE
+
+class QString;
+class QQmlPropertyCacheMethodArguments
+{
+public:
+ QQmlPropertyCacheMethodArguments *next;
+
+ //for signal handler rewrites
+ QString *signalParameterStringForJS;
+ int parameterError:1;
+ int argumentsValid:1;
+
+ QList<QByteArray> *names;
+
+ int arguments[1];
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPROPERTYCACHEMETODARGUMENTS_P_H
diff --git a/src/qml/debugger/qqmlmemoryprofiler_p.h b/src/qml/qml/qqmlpropertycachevector_p.h
index 12a31a851f..1dff7c61a6 100644
--- a/src/qml/debugger/qqmlmemoryprofiler_p.h
+++ b/src/qml/qml/qqmlpropertycachevector_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QQMLMEMORYPROFILER_H
-#define QQMLMEMORYPROFILER_H
+#ifndef QQMLPROPERTYCACHEVECTOR_P_H
+#define QQMLPROPERTYCACHEVECTOR_P_H
//
// W A R N I N G
@@ -51,84 +51,54 @@
// We mean it.
//
-#include <private/qtqmlglobal_p.h>
-#include <QUrl>
+#include <private/qflagpointer_p.h>
+#include <private/qqmlpropertycache_p.h>
QT_BEGIN_NAMESPACE
-#if !QT_CONFIG(qml_debug)
-
-#define QML_MEMORY_SCOPE_URL(url)
-#define QML_MEMORY_SCOPE_STRING(s)
-
-#else
-
-class Q_QML_PRIVATE_EXPORT QQmlMemoryScope
+class QQmlPropertyCacheVector
{
public:
- explicit QQmlMemoryScope(const QUrl &url)
- : pushed(false)
- {
- if (Q_UNLIKELY(openLibrary()))
- init(url.path().toUtf8().constData());
+ QQmlPropertyCacheVector() {}
+ QQmlPropertyCacheVector(QQmlPropertyCacheVector &&other)
+ : data(std::move(other.data)) {}
+ QQmlPropertyCacheVector &operator=(QQmlPropertyCacheVector &&other) {
+ QVector<QFlagPointer<QQmlPropertyCache>> moved(std::move(other.data));
+ data.swap(moved);
+ return *this;
}
- explicit QQmlMemoryScope(const char *string)
- : pushed(false)
+ ~QQmlPropertyCacheVector() { clear(); }
+ void resize(int size) { return data.resize(size); }
+ int count() const { return data.count(); }
+ void clear()
{
- if (Q_UNLIKELY(openLibrary()))
- init(string);
+ for (int i = 0; i < data.count(); ++i) {
+ if (QQmlPropertyCache *cache = data.at(i).data())
+ cache->release();
+ }
+ data.clear();
}
- ~QQmlMemoryScope()
- {
- if (Q_UNLIKELY(pushed))
- done();
- }
-
- enum LibraryState
- {
- Unloaded,
- Failed,
- Loaded
- };
-
- static bool openLibrary()
- {
- if (Q_LIKELY(state == Loaded))
- return true;
- if (state == Failed)
- return false;
-
- return doOpenLibrary();
+ void append(QQmlPropertyCache *cache) { cache->addref(); data.append(cache); }
+ QQmlPropertyCache *at(int index) const { return data.at(index).data(); }
+ void set(int index, const QQmlRefPointer<QQmlPropertyCache> &replacement) {
+ if (QQmlPropertyCache *oldCache = data.at(index).data()) {
+ if (replacement.data() == oldCache)
+ return;
+ oldCache->release();
+ }
+ data[index] = replacement.data();
+ replacement->addref();
}
+ void setNeedsVMEMetaObject(int index) { data[index].setFlag(); }
+ bool needsVMEMetaObject(int index) const { return data.at(index).flag(); }
private:
- Q_NEVER_INLINE void init(const char *string);
- Q_NEVER_INLINE void done();
- Q_NEVER_INLINE static bool doOpenLibrary();
-
- static LibraryState state;
-
- bool pushed;
-};
-
-class Q_QML_PRIVATE_EXPORT QQmlMemoryProfiler
-{
-public:
- static void enable();
- static void disable();
- static bool isEnabled();
-
- static void clear();
- static void stats(int *allocCount, int *bytesAllocated);
- static void save(const char *filename);
+ Q_DISABLE_COPY(QQmlPropertyCacheVector)
+ QVector<QFlagPointer<QQmlPropertyCache>> data;
};
-#define QML_MEMORY_SCOPE_URL(url) QQmlMemoryScope _qml_memory_scope(url)
-#define QML_MEMORY_SCOPE_STRING(s) QQmlMemoryScope _qml_memory_scope(s)
-
-#endif
-
QT_END_NAMESPACE
-#endif // QQMLMEMORYPROFILER_H
+
+#endif // QQMLPROPERTYCACHEVECTOR_P_H
diff --git a/src/qml/qml/qqmlpropertydata_p.h b/src/qml/qml/qqmlpropertydata_p.h
new file mode 100644
index 0000000000..dec696226e
--- /dev/null
+++ b/src/qml/qml/qqmlpropertydata_p.h
@@ -0,0 +1,411 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLPROPERTYDATA_P_H
+#define QQMLPROPERTYDATA_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/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlPropertyCacheMethodArguments;
+class QQmlPropertyData
+{
+public:
+ enum WriteFlag {
+ BypassInterceptor = 0x01,
+ DontRemoveBinding = 0x02,
+ RemoveBindingOnAliasWrite = 0x04
+ };
+ Q_DECLARE_FLAGS(WriteFlags, WriteFlag)
+
+ typedef QObjectPrivate::StaticMetaCallFunction StaticMetaCallFunction;
+
+ struct Flags {
+ enum Types {
+ OtherType = 0,
+ FunctionType = 1, // Is an invokable
+ QObjectDerivedType = 2, // Property type is a QObject* derived type
+ EnumType = 3, // Property type is an enum
+ QListType = 4, // Property type is a QML list
+ QmlBindingType = 5, // Property type is a QQmlBinding*
+ QJSValueType = 6, // Property type is a QScriptValue
+ // Gap, used to be V4HandleType
+ VarPropertyType = 8, // Property type is a "var" property of VMEMO
+ QVariantType = 9 // Property is a QVariant
+ };
+
+ // The _otherBits (which "pad" the Flags struct to align it nicely) are used
+ // to store the relative property index. It will only get used when said index fits. See
+ // trySetStaticMetaCallFunction for details.
+ // (Note: this padding is done here, because certain compilers have surprising behavior
+ // when an enum is declared in-between two bit fields.)
+ enum { BitsLeftInFlags = 10 };
+ unsigned otherBits : BitsLeftInFlags; // align to 32 bits
+
+ // Can apply to all properties, except IsFunction
+ unsigned isConstant : 1; // Has CONST flag
+ unsigned isWritable : 1; // Has WRITE function
+ unsigned isResettable : 1; // Has RESET function
+ unsigned isAlias : 1; // Is a QML alias to another property
+ unsigned isFinal : 1; // Has FINAL flag
+ unsigned isOverridden : 1; // Is overridden by a extension property
+ unsigned isDirect : 1; // Exists on a C++ QMetaObject
+
+ unsigned type : 4; // stores an entry of Types
+
+ // Apply only to IsFunctions
+ unsigned isVMEFunction : 1; // Function was added by QML
+ unsigned hasArguments : 1; // Function takes arguments
+ unsigned isSignal : 1; // Function is a signal
+ unsigned isVMESignal : 1; // Signal was added by QML
+ unsigned isV4Function : 1; // Function takes QQmlV4Function* args
+ unsigned isSignalHandler : 1; // Function is a signal handler
+ unsigned isOverload : 1; // Function is an overload of another function
+ unsigned isCloned : 1; // The function was marked as cloned
+ unsigned isConstructor : 1; // The function was marked is a constructor
+
+ // Internal QQmlPropertyCache flags
+ unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved
+ unsigned overrideIndexIsProperty: 1;
+
+ inline Flags();
+ inline bool operator==(const Flags &other) const;
+ inline void copyPropertyTypeFlags(Flags from);
+ };
+
+ inline bool operator==(const QQmlPropertyData &) const;
+
+ Flags flags() const { return m_flags; }
+ void setFlags(Flags f)
+ {
+ unsigned otherBits = m_flags.otherBits;
+ m_flags = f;
+ m_flags.otherBits = otherBits;
+ }
+
+ bool isValid() const { return coreIndex() != -1; }
+
+ bool isConstant() const { return m_flags.isConstant; }
+ bool isWritable() const { return m_flags.isWritable; }
+ void setWritable(bool onoff) { m_flags.isWritable = onoff; }
+ bool isResettable() const { return m_flags.isResettable; }
+ bool isAlias() const { return m_flags.isAlias; }
+ bool isFinal() const { return m_flags.isFinal; }
+ bool isOverridden() const { return m_flags.isOverridden; }
+ bool isDirect() const { return m_flags.isDirect; }
+ bool hasStaticMetaCallFunction() const { return staticMetaCallFunction() != nullptr; }
+ bool isFunction() const { return m_flags.type == Flags::FunctionType; }
+ bool isQObject() const { return m_flags.type == Flags::QObjectDerivedType; }
+ bool isEnum() const { return m_flags.type == Flags::EnumType; }
+ bool isQList() const { return m_flags.type == Flags::QListType; }
+ bool isQmlBinding() const { return m_flags.type == Flags::QmlBindingType; }
+ bool isQJSValue() const { return m_flags.type == Flags::QJSValueType; }
+ bool isVarProperty() const { return m_flags.type == Flags::VarPropertyType; }
+ bool isQVariant() const { return m_flags.type == Flags::QVariantType; }
+ bool isVMEFunction() const { return m_flags.isVMEFunction; }
+ bool hasArguments() const { return m_flags.hasArguments; }
+ bool isSignal() const { return m_flags.isSignal; }
+ bool isVMESignal() const { return m_flags.isVMESignal; }
+ bool isV4Function() const { return m_flags.isV4Function; }
+ bool isSignalHandler() const { return m_flags.isSignalHandler; }
+ bool isOverload() const { return m_flags.isOverload; }
+ void setOverload(bool onoff) { m_flags.isOverload = onoff; }
+ bool isCloned() const { return m_flags.isCloned; }
+ bool isConstructor() const { return m_flags.isConstructor; }
+
+ bool hasOverride() const { return overrideIndex() >= 0; }
+ bool hasRevision() const { return revision() != 0; }
+
+ bool isFullyResolved() const { return !m_flags.notFullyResolved; }
+
+ int propType() const { Q_ASSERT(isFullyResolved()); return m_propType; }
+ void setPropType(int pt)
+ {
+ Q_ASSERT(pt >= 0);
+ Q_ASSERT(pt <= std::numeric_limits<qint16>::max());
+ m_propType = quint16(pt);
+ }
+
+ int notifyIndex() const { return m_notifyIndex; }
+ void setNotifyIndex(int idx)
+ {
+ Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
+ Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
+ m_notifyIndex = qint16(idx);
+ }
+
+ bool overrideIndexIsProperty() const { return m_flags.overrideIndexIsProperty; }
+ void setOverrideIndexIsProperty(bool onoff) { m_flags.overrideIndexIsProperty = onoff; }
+
+ int overrideIndex() const { return m_overrideIndex; }
+ void setOverrideIndex(int idx)
+ {
+ Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
+ Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
+ m_overrideIndex = qint16(idx);
+ }
+
+ int coreIndex() const { return m_coreIndex; }
+ void setCoreIndex(int idx)
+ {
+ Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
+ Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
+ m_coreIndex = qint16(idx);
+ }
+
+ quint8 revision() const { return m_revision; }
+ void setRevision(quint8 rev)
+ {
+ Q_ASSERT(rev <= std::numeric_limits<quint8>::max());
+ m_revision = quint8(rev);
+ }
+
+ /* If a property is a C++ type, then we store the minor
+ * version of this type.
+ * This is required to resolve property or signal revisions
+ * if this property is used as a grouped property.
+ *
+ * Test.qml
+ * property TextEdit someTextEdit: TextEdit {}
+ *
+ * Test {
+ * someTextEdit.preeditText: "test" //revision 7
+ * someTextEdit.onEditingFinished: console.log("test") //revision 6
+ * }
+ *
+ * To determine if these properties with revisions are available we need
+ * the minor version of TextEdit as imported in Test.qml.
+ *
+ */
+
+ quint8 typeMinorVersion() const { return m_typeMinorVersion; }
+ void setTypeMinorVersion(quint8 rev)
+ {
+ Q_ASSERT(rev <= std::numeric_limits<quint8>::max());
+ m_typeMinorVersion = quint8(rev);
+ }
+
+ QQmlPropertyCacheMethodArguments *arguments() const { return m_arguments; }
+ void setArguments(QQmlPropertyCacheMethodArguments *args) { m_arguments = args; }
+
+ int metaObjectOffset() const { return m_metaObjectOffset; }
+ void setMetaObjectOffset(int off)
+ {
+ Q_ASSERT(off >= std::numeric_limits<qint16>::min());
+ Q_ASSERT(off <= std::numeric_limits<qint16>::max());
+ m_metaObjectOffset = qint16(off);
+ }
+
+ StaticMetaCallFunction staticMetaCallFunction() const { return m_staticMetaCallFunction; }
+ void trySetStaticMetaCallFunction(StaticMetaCallFunction f, unsigned relativePropertyIndex)
+ {
+ if (relativePropertyIndex < (1 << Flags::BitsLeftInFlags) - 1) {
+ m_flags.otherBits = relativePropertyIndex;
+ m_staticMetaCallFunction = f;
+ }
+ }
+ quint16 relativePropertyIndex() const { Q_ASSERT(hasStaticMetaCallFunction()); return m_flags.otherBits; }
+
+ static Flags flagsForProperty(const QMetaProperty &);
+ void load(const QMetaProperty &);
+ void load(const QMetaMethod &);
+ QString name(QObject *) const;
+ QString name(const QMetaObject *) const;
+
+ void markAsOverrideOf(QQmlPropertyData *predecessor);
+
+ inline void readProperty(QObject *target, void *property) const
+ {
+ void *args[] = { property, nullptr };
+ readPropertyWithArgs(target, args);
+ }
+
+ inline void readPropertyWithArgs(QObject *target, void *args[]) const
+ {
+ if (hasStaticMetaCallFunction())
+ staticMetaCallFunction()(target, QMetaObject::ReadProperty, relativePropertyIndex(), args);
+ else if (isDirect())
+ target->qt_metacall(QMetaObject::ReadProperty, coreIndex(), args);
+ else
+ QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex(), args);
+ }
+
+ bool writeProperty(QObject *target, void *value, WriteFlags flags) const
+ {
+ int status = -1;
+ void *argv[] = { value, nullptr, &status, &flags };
+ if (flags.testFlag(BypassInterceptor) && hasStaticMetaCallFunction())
+ staticMetaCallFunction()(target, QMetaObject::WriteProperty, relativePropertyIndex(), argv);
+ else if (flags.testFlag(BypassInterceptor) && isDirect())
+ target->qt_metacall(QMetaObject::WriteProperty, coreIndex(), argv);
+ else
+ QMetaObject::metacall(target, QMetaObject::WriteProperty, coreIndex(), argv);
+ return true;
+ }
+
+ static Flags defaultSignalFlags()
+ {
+ Flags f;
+ f.isSignal = true;
+ f.type = Flags::FunctionType;
+ f.isVMESignal = true;
+ return f;
+ }
+
+ static Flags defaultSlotFlags()
+ {
+ Flags f;
+ f.type = Flags::FunctionType;
+ f.isVMEFunction = true;
+ return f;
+ }
+
+private:
+ friend class QQmlPropertyCache;
+ void lazyLoad(const QMetaProperty &);
+ void lazyLoad(const QMetaMethod &);
+ bool notFullyResolved() const { return m_flags.notFullyResolved; }
+
+ Flags m_flags;
+ qint16 m_coreIndex = -1;
+ quint16 m_propType = 0;
+
+ // The notify index is in the range returned by QObjectPrivate::signalIndex().
+ // This is different from QMetaMethod::methodIndex().
+ qint16 m_notifyIndex = -1;
+ qint16 m_overrideIndex = -1;
+
+ quint8 m_revision = 0;
+ quint8 m_typeMinorVersion = 0;
+ qint16 m_metaObjectOffset = -1;
+
+ QQmlPropertyCacheMethodArguments *m_arguments = nullptr;
+ StaticMetaCallFunction m_staticMetaCallFunction = nullptr;
+};
+
+#if QT_POINTER_SIZE == 4
+ Q_STATIC_ASSERT(sizeof(QQmlPropertyData) == 24);
+#else // QT_POINTER_SIZE == 8
+ Q_STATIC_ASSERT(sizeof(QQmlPropertyData) == 32);
+#endif
+
+bool QQmlPropertyData::operator==(const QQmlPropertyData &other) const
+{
+ return flags() == other.flags() &&
+ propType() == other.propType() &&
+ coreIndex() == other.coreIndex() &&
+ notifyIndex() == other.notifyIndex() &&
+ revision() == other.revision();
+}
+
+QQmlPropertyData::Flags::Flags()
+ : otherBits(0)
+ , isConstant(false)
+ , isWritable(false)
+ , isResettable(false)
+ , isAlias(false)
+ , isFinal(false)
+ , isOverridden(false)
+ , isDirect(false)
+ , type(OtherType)
+ , isVMEFunction(false)
+ , hasArguments(false)
+ , isSignal(false)
+ , isVMESignal(false)
+ , isV4Function(false)
+ , isSignalHandler(false)
+ , isOverload(false)
+ , isCloned(false)
+ , isConstructor(false)
+ , notFullyResolved(false)
+ , overrideIndexIsProperty(false)
+{}
+
+bool QQmlPropertyData::Flags::operator==(const QQmlPropertyData::Flags &other) const
+{
+ return isConstant == other.isConstant &&
+ isWritable == other.isWritable &&
+ isResettable == other.isResettable &&
+ isAlias == other.isAlias &&
+ isFinal == other.isFinal &&
+ isOverridden == other.isOverridden &&
+ type == other.type &&
+ isVMEFunction == other.isVMEFunction &&
+ hasArguments == other.hasArguments &&
+ isSignal == other.isSignal &&
+ isVMESignal == other.isVMESignal &&
+ isV4Function == other.isV4Function &&
+ isSignalHandler == other.isSignalHandler &&
+ isOverload == other.isOverload &&
+ isCloned == other.isCloned &&
+ isConstructor == other.isConstructor &&
+ notFullyResolved == other.notFullyResolved &&
+ overrideIndexIsProperty == other.overrideIndexIsProperty;
+}
+
+void QQmlPropertyData::Flags::copyPropertyTypeFlags(QQmlPropertyData::Flags from)
+{
+ switch (from.type) {
+ case QObjectDerivedType:
+ case EnumType:
+ case QListType:
+ case QmlBindingType:
+ case QJSValueType:
+ case QVariantType:
+ type = from.type;
+ }
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyData::WriteFlags)
+
+QT_END_NAMESPACE
+
+#endif // QQMLPROPERTYDATA_P_H
diff --git a/src/qml/qml/qqmlpropertyresolver.cpp b/src/qml/qml/qqmlpropertyresolver.cpp
new file mode 100644
index 0000000000..90eaca0b90
--- /dev/null
+++ b/src/qml/qml/qqmlpropertyresolver.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlpropertyresolver_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QQmlPropertyData *QQmlPropertyResolver::property(const QString &name, bool *notInRevision,
+ RevisionCheck check) const
+{
+ if (notInRevision) *notInRevision = false;
+
+ QQmlPropertyData *d = cache->property(name, nullptr, nullptr);
+
+ // Find the first property
+ while (d && d->isFunction())
+ d = cache->overrideData(d);
+
+ if (check != IgnoreRevision && d && !cache->isAllowedInRevision(d)) {
+ if (notInRevision) *notInRevision = true;
+ return nullptr;
+ } else {
+ return d;
+ }
+}
+
+
+QQmlPropertyData *QQmlPropertyResolver::signal(const QString &name, bool *notInRevision) const
+{
+ if (notInRevision) *notInRevision = false;
+
+ QQmlPropertyData *d = cache->property(name, nullptr, nullptr);
+ if (notInRevision) *notInRevision = false;
+
+ while (d && !(d->isFunction()))
+ d = cache->overrideData(d);
+
+ if (d && !cache->isAllowedInRevision(d)) {
+ if (notInRevision) *notInRevision = true;
+ return nullptr;
+ } else if (d && d->isSignal()) {
+ return d;
+ }
+
+ if (name.endsWith(QLatin1String("Changed"))) {
+ QString propName = name.mid(0, name.length() - static_cast<int>(strlen("Changed")));
+
+ d = property(propName, notInRevision);
+ if (d)
+ return cache->signal(d->notifyIndex());
+ }
+
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlpropertyresolver_p.h b/src/qml/qml/qqmlpropertyresolver_p.h
new file mode 100644
index 0000000000..df857f242e
--- /dev/null
+++ b/src/qml/qml/qqmlpropertyresolver_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLPROPERTYRESOLVER_P_H
+#define QQMLPROPERTYRESOLVER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtqmlglobal_p.h>
+#include <private/qqmlpropertycache_p.h>
+#include <private/qqmlrefcount_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct Q_QML_EXPORT QQmlPropertyResolver
+{
+ QQmlPropertyResolver(const QQmlRefPointer<QQmlPropertyCache> &cache)
+ : cache(cache)
+ {}
+
+ QQmlPropertyData *property(int index) const
+ {
+ return cache->property(index);
+ }
+
+ enum RevisionCheck {
+ CheckRevision,
+ IgnoreRevision
+ };
+
+ QQmlPropertyData *property(const QString &name, bool *notInRevision = nullptr,
+ RevisionCheck check = CheckRevision) const;
+
+ // This code must match the semantics of QQmlPropertyPrivate::findSignalByName
+ QQmlPropertyData *signal(const QString &name, bool *notInRevision) const;
+
+ QQmlRefPointer<QQmlPropertyCache> cache;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPROPERTYRESOLVER_P_H
diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp
index d20efe616b..ef85d6e4d9 100644
--- a/src/qml/compiler/qqmlpropertyvalidator.cpp
+++ b/src/qml/qml/qqmlpropertyvalidator.cpp
@@ -40,12 +40,29 @@
#include "qqmlpropertyvalidator_p.h"
#include <private/qqmlcustomparser_p.h>
+#include <private/qqmlirbuilder_p.h>
#include <private/qqmlstringconverters_p.h>
+#include <private/qqmlpropertycachecreator_p.h>
+#include <private/qqmlpropertyresolver_p.h>
+
#include <QtCore/qdatetime.h>
QT_BEGIN_NAMESPACE
-QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
+static bool isPrimitiveType(int typeId)
+{
+ switch (typeId) {
+#define HANDLE_PRIMITIVE(Type, id, T) \
+ case QMetaType::Type:
+QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(HANDLE_PRIMITIVE);
+#undef HANDLE_PRIMITIVE
+ return true;
+ default:
+ return false;
+ }
+}
+
+QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit)
: enginePrivate(enginePrivate)
, compilationUnit(compilationUnit)
, imports(imports)
@@ -56,7 +73,7 @@ QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, c
bindingPropertyDataPerObject->resize(compilationUnit->objectCount());
}
-QVector<QQmlCompileError> QQmlPropertyValidator::validate()
+QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validate()
{
return validateObject(/*root object*/0, /*instantiatingBinding*/nullptr);
}
@@ -79,7 +96,8 @@ struct BindingFinder
}
};
-QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const
+QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject(
+ int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const
{
const QV4::CompiledData::Object *obj = compilationUnit->objectAt(objectIndex);
@@ -92,7 +110,7 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex,
QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex);
if (!propertyCache)
- return QVector<QQmlCompileError>();
+ return QVector<QQmlJS::DiagnosticMessage>();
QQmlCustomParser *customParser = nullptr;
if (auto typeRef = resolvedType(obj->inheritedTypeNameIndex)) {
@@ -121,7 +139,7 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex,
groupProperties.insert(pos, binding);
}
- QmlIR::PropertyResolver propertyResolver(propertyCache);
+ QQmlPropertyResolver propertyResolver(propertyCache);
QString defaultPropertyName;
QQmlPropertyData *defaultProperty = nullptr;
@@ -134,7 +152,7 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex,
defaultProperty = propertyCache->defaultProperty();
}
- QV4::CompiledData::BindingPropertyData collectedBindingPropertyData(obj->nBindings);
+ QV4::BindingPropertyData collectedBindingPropertyData(obj->nBindings);
binding = obj->bindingTable();
for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
@@ -164,7 +182,7 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex,
pd = propertyResolver.signal(name, &notInRevision);
} else {
pd = propertyResolver.property(name, &notInRevision,
- QmlIR::PropertyResolver::CheckRevision);
+ QQmlPropertyResolver::CheckRevision);
}
if (notInRevision) {
@@ -202,7 +220,7 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex,
= pd
&& QQmlValueTypeFactory::metaObjectForMetaType(pd->propType())
&& !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment);
- const QVector<QQmlCompileError> subObjectValidatorErrors
+ const QVector<QQmlJS::DiagnosticMessage> subObjectValidatorErrors
= validateObject(binding->value.objectIndex, binding,
populatingValueTypeGroupProperty);
if (!subObjectValidatorErrors.isEmpty())
@@ -259,12 +277,12 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex,
}
if (binding->type < QV4::CompiledData::Binding::Type_Script) {
- QQmlCompileError bindingError = validateLiteralBinding(propertyCache, pd, binding);
- if (bindingError.isSet())
+ QQmlJS::DiagnosticMessage bindingError = validateLiteralBinding(propertyCache, pd, binding);
+ if (bindingError.isValid())
return recordError(bindingError);
} else if (binding->type == QV4::CompiledData::Binding::Type_Object) {
- QQmlCompileError bindingError = validateObjectBinding(pd, name, binding);
- if (bindingError.isSet())
+ QQmlJS::DiagnosticMessage bindingError = validateObjectBinding(pd, name, binding);
+ if (bindingError.isValid())
return recordError(bindingError);
} else if (binding->isGroupProperty()) {
if (QQmlValueTypeFactory::isValueType(pd->propType())) {
@@ -276,11 +294,21 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex,
return recordError(binding->location, tr("Invalid grouped property access"));
}
} else {
- if (!enginePrivate->propertyCacheForType(pd->propType())) {
+ const int typeId = pd->propType();
+ if (isPrimitiveType(typeId)) {
+ return recordError(
+ binding->location,
+ tr("Invalid grouped property access: Property \"%1\" with primitive type \"%2\".")
+ .arg(name)
+ .arg(QString::fromLatin1(QMetaType::typeName(typeId)))
+ );
+ }
+
+ if (!enginePrivate->propertyCacheForType(typeId)) {
return recordError(binding->location,
tr("Invalid grouped property access: Property \"%1\" with type \"%2\", which is not a value type")
.arg(name)
- .arg(QString::fromLatin1(QMetaType::typeName(pd->propType())))
+ .arg(QString::fromLatin1(QMetaType::typeName(typeId)))
);
}
}
@@ -315,30 +343,30 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex,
customParser->validator = nullptr;
customParser->engine = nullptr;
customParser->imports = (QQmlImports*)nullptr;
- QVector<QQmlCompileError> parserErrors = customParser->errors();
+ QVector<QQmlJS::DiagnosticMessage> parserErrors = customParser->errors();
if (!parserErrors.isEmpty())
return parserErrors;
}
(*bindingPropertyDataPerObject)[objectIndex] = collectedBindingPropertyData;
- QVector<QQmlCompileError> noError;
+ QVector<QQmlJS::DiagnosticMessage> noError;
return noError;
}
-QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const
+QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const
{
if (property->isQList()) {
- return QQmlCompileError(binding->valueLocation, tr("Cannot assign primitives to lists"));
+ return qQmlCompileError(binding->valueLocation, tr("Cannot assign primitives to lists"));
}
- QQmlCompileError noError;
+ QQmlJS::DiagnosticMessage noError;
if (property->isEnum()) {
if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum)
return noError;
- QString value = binding->valueAsString(compilationUnit.data());
+ QString value = compilationUnit->bindingValueAsString(binding);
QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex());
bool ok;
if (p.isFlagType()) {
@@ -347,7 +375,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
p.enumerator().keyToValue(value.toUtf8().constData(), &ok);
if (!ok) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration"));
+ return qQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration"));
}
return noError;
}
@@ -364,7 +392,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
enginePrivate->warning(warning);
return noError;
}
- return QQmlCompileError(binding->valueLocation, error);
+ return qQmlCompileError(binding->valueLocation, error);
};
switch (property->propType()) {
@@ -396,7 +424,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::UInt: {
if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- double d = binding->valueAsNumber(compilationUnit->constants);
+ double d = compilationUnit->bindingValueAsNumber(binding);
if (double(uint(d)) == d)
return noError;
}
@@ -405,7 +433,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::Int: {
if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- double d = binding->valueAsNumber(compilationUnit->constants);
+ double d = compilationUnit->bindingValueAsNumber(binding);
if (double(int(d)) == d)
return noError;
}
@@ -426,7 +454,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::Color: {
bool ok = false;
- QQmlStringConverters::rgbaFromString(binding->valueAsString(compilationUnit.data()), &ok);
+ QQmlStringConverters::rgbaFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
return warnOrError(tr("Invalid property assignment: color expected"));
}
@@ -435,7 +463,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
#if QT_CONFIG(datestring)
case QVariant::Date: {
bool ok = false;
- QQmlStringConverters::dateFromString(binding->valueAsString(compilationUnit.data()), &ok);
+ QQmlStringConverters::dateFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
return warnOrError(tr("Invalid property assignment: date expected"));
}
@@ -443,7 +471,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::Time: {
bool ok = false;
- QQmlStringConverters::timeFromString(binding->valueAsString(compilationUnit.data()), &ok);
+ QQmlStringConverters::timeFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
return warnOrError(tr("Invalid property assignment: time expected"));
}
@@ -451,7 +479,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::DateTime: {
bool ok = false;
- QQmlStringConverters::dateTimeFromString(binding->valueAsString(compilationUnit.data()), &ok);
+ QQmlStringConverters::dateTimeFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
return warnOrError(tr("Invalid property assignment: datetime expected"));
}
@@ -460,7 +488,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
#endif // datestring
case QVariant::Point: {
bool ok = false;
- QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok);
+ QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
return warnOrError(tr("Invalid property assignment: point expected"));
}
@@ -468,7 +496,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::PointF: {
bool ok = false;
- QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok);
+ QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
return warnOrError(tr("Invalid property assignment: point expected"));
}
@@ -476,7 +504,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::Size: {
bool ok = false;
- QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok);
+ QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
return warnOrError(tr("Invalid property assignment: size expected"));
}
@@ -484,7 +512,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::SizeF: {
bool ok = false;
- QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok);
+ QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
return warnOrError(tr("Invalid property assignment: size expected"));
}
@@ -492,7 +520,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::Rect: {
bool ok = false;
- QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok);
+ QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
return warnOrError(tr("Invalid property assignment: rect expected"));
}
@@ -500,7 +528,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::RectF: {
bool ok = false;
- QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok);
+ QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok);
if (!ok) {
return warnOrError(tr("Invalid property assignment: point expected"));
}
@@ -517,7 +545,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
float xp;
float yp;
} vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) {
+ if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) {
return warnOrError(tr("Invalid property assignment: 2D vector expected"));
}
}
@@ -528,7 +556,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
float yp;
float zy;
} vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) {
+ if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) {
return warnOrError(tr("Invalid property assignment: 3D vector expected"));
}
}
@@ -540,7 +568,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
float zy;
float wp;
} vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) {
+ if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) {
return warnOrError(tr("Invalid property assignment: 4D vector expected"));
}
}
@@ -552,12 +580,13 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
float yp;
float zp;
} vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) {
+ if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) {
return warnOrError(tr("Invalid property assignment: quaternion expected"));
}
}
break;
case QVariant::RegExp:
+ case QVariant::RegularExpression:
return warnOrError(tr("Invalid property assignment: regular expression expected; use /pattern/ syntax"));
default: {
// generate single literal value assignment to a list property if required
@@ -569,7 +598,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
} else if (property->propType() == qMetaTypeId<QList<int> >()) {
bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number);
if (ok) {
- double n = binding->valueAsNumber(compilationUnit->constants);
+ double n = compilationUnit->bindingValueAsNumber(binding);
if (double(int(n)) != n)
ok = false;
}
@@ -627,23 +656,23 @@ bool QQmlPropertyValidator::canCoerce(int to, QQmlPropertyCache *fromMo) const
return false;
}
-QVector<QQmlCompileError> QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const
+QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const
{
- QVector<QQmlCompileError> errors;
- errors.append(QQmlCompileError(location, description));
+ QVector<QQmlJS::DiagnosticMessage> errors;
+ errors.append(qQmlCompileError(location, description));
return errors;
}
-QVector<QQmlCompileError> QQmlPropertyValidator::recordError(const QQmlCompileError &error) const
+QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::recordError(const QQmlJS::DiagnosticMessage &error) const
{
- QVector<QQmlCompileError> errors;
+ QVector<QQmlJS::DiagnosticMessage> errors;
errors.append(error);
return errors;
}
-QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const
+QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const
{
- QQmlCompileError noError;
+ QQmlJS::DiagnosticMessage noError;
if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object);
@@ -667,59 +696,73 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
}
if (!isValueSource && !isPropertyInterceptor) {
- return QQmlCompileError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName));
+ return qQmlCompileError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName));
}
return noError;
}
- if (QQmlMetaType::isInterface(property->propType())) {
+ const int propType = property->propType();
+ const auto rhsType = [&]() {
+ return stringAt(compilationUnit->objectAt(binding->value.objectIndex)
+ ->inheritedTypeNameIndex);
+ };
+
+ if (QQmlMetaType::isInterface(propType)) {
// Can only check at instantiation time if the created sub-object successfully casts to the
// target interface.
return noError;
- } else if (property->propType() == QMetaType::QVariant || property->propType() == qMetaTypeId<QJSValue>()) {
+ } else if (propType == QMetaType::QVariant || propType == qMetaTypeId<QJSValue>()) {
// We can convert everything to QVariant :)
return noError;
} else if (property->isQList()) {
- const int listType = enginePrivate->listType(property->propType());
+ const int listType = enginePrivate->listType(propType);
if (!QQmlMetaType::isInterface(listType)) {
QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex);
if (!canCoerce(listType, source)) {
- return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName));
+ return qQmlCompileError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName));
}
}
return noError;
} else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) {
return noError;
- } else if (QQmlValueTypeFactory::isValueType(property->propType())) {
- auto typeName = QMetaType::typeName(property->propType());
- return QQmlCompileError(binding->location, tr("Can not assign value of type \"%1\" to property \"%2\", expecting an object")
- .arg(typeName ? QString::fromLatin1(typeName) : QString::fromLatin1("<unknown type>"))
- .arg(propertyName));
- } else if (property->propType() == qMetaTypeId<QQmlScriptString>()) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected"));
+ } else if (isPrimitiveType(propType)) {
+ auto typeName = QMetaType::typeName(propType);
+ return qQmlCompileError(binding->location, tr("Cannot assign value of type \"%1\" to property \"%2\", expecting \"%3\"")
+ .arg(rhsType())
+ .arg(propertyName)
+ .arg(typeName));
+ } else if (QQmlValueTypeFactory::isValueType(propType)) {
+ return qQmlCompileError(binding->location, tr("Cannot assign value of type \"%1\" to property \"%2\", expecting an object")
+ .arg(rhsType()).arg(propertyName));
+ } else if (propType == qMetaTypeId<QQmlScriptString>()) {
+ return qQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected"));
} else {
// We want to use the raw metaObject here as the raw metaobject is the
// actual property type before we applied any extensions that might
// effect the properties on the type, but don't effect assignability
// Using -1 for the minor version ensures that we get the raw metaObject.
- QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType(), -1);
+ QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(propType, -1);
- // Will be true if the assigned type inherits propertyMetaObject
- bool isAssignable = false;
- // Determine isAssignable value
if (propertyMetaObject) {
+ // Will be true if the assigned type inherits propertyMetaObject
+ // Determine isAssignable value
+ bool isAssignable = false;
QQmlPropertyCache *c = propertyCaches.at(binding->value.objectIndex);
while (c && !isAssignable) {
isAssignable |= c == propertyMetaObject;
c = c->parent();
}
- }
- if (!isAssignable) {
- return QQmlCompileError(binding->valueLocation, tr("Cannot assign object of type \"%1\" to property of type \"%2\" as the former is neither the same as the latter nor a sub-class of it.")
- .arg(stringAt(compilationUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex)).arg(QLatin1String(QMetaType::typeName(property->propType()))));
+ if (!isAssignable) {
+ return qQmlCompileError(binding->valueLocation, tr("Cannot assign object of type \"%1\" to property of type \"%2\" as the former is neither the same as the latter nor a sub-class of it.")
+ .arg(rhsType()).arg(QLatin1String(QMetaType::typeName(propType))));
+ }
+ } else {
+ return qQmlCompileError(binding->valueLocation, tr("Cannot assign to property of unknown type \"%1\".")
+ .arg(QLatin1String(QMetaType::typeName(propType))));
}
+
}
return noError;
}
diff --git a/src/qml/compiler/qqmlpropertyvalidator_p.h b/src/qml/qml/qqmlpropertyvalidator_p.h
index e9ae844ccb..74a1281927 100644
--- a/src/qml/compiler/qqmlpropertyvalidator_p.h
+++ b/src/qml/qml/qqmlpropertyvalidator_p.h
@@ -50,7 +50,13 @@
// We mean it.
//
-#include <private/qqmltypecompiler_p.h>
+#include <private/qqmlengine_p.h>
+#include <private/qqmlimport_p.h>
+#include <private/qqmljsdiagnosticmessage_p.h>
+#include <private/qqmlpropertycache_p.h>
+#include <private/qv4compileddata_p.h>
+
+#include <QtCore/qcoreapplication.h>
QT_BEGIN_NAMESPACE
@@ -58,32 +64,40 @@ class QQmlPropertyValidator
{
Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator)
public:
- QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
+ QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit);
- QVector<QQmlCompileError> validate();
+ QVector<QQmlJS::DiagnosticMessage> validate();
private:
- QVector<QQmlCompileError> validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty = false) const;
- QQmlCompileError validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const;
- QQmlCompileError validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const;
+ QVector<QQmlJS::DiagnosticMessage> validateObject(
+ int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding,
+ bool populatingValueTypeGroupProperty = false) const;
+ QQmlJS::DiagnosticMessage validateLiteralBinding(
+ QQmlPropertyCache *propertyCache, QQmlPropertyData *property,
+ const QV4::CompiledData::Binding *binding) const;
+ QQmlJS::DiagnosticMessage validateObjectBinding(
+ QQmlPropertyData *property, const QString &propertyName,
+ const QV4::CompiledData::Binding *binding) const;
bool canCoerce(int to, QQmlPropertyCache *fromMo) const;
- Q_REQUIRED_RESULT QVector<QQmlCompileError> recordError(const QV4::CompiledData::Location &location, const QString &description) const;
- Q_REQUIRED_RESULT QVector<QQmlCompileError> recordError(const QQmlCompileError &error) const;
+ Q_REQUIRED_RESULT QVector<QQmlJS::DiagnosticMessage> recordError(
+ const QV4::CompiledData::Location &location, const QString &description) const;
+ Q_REQUIRED_RESULT QVector<QQmlJS::DiagnosticMessage> recordError(
+ const QQmlJS::DiagnosticMessage &error) const;
QString stringAt(int index) const { return compilationUnit->stringAt(index); }
- QV4::CompiledData::ResolvedTypeReference *resolvedType(int id) const
+ QV4::ResolvedTypeReference *resolvedType(int id) const
{
return compilationUnit->resolvedType(id);
}
QQmlEnginePrivate *enginePrivate;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
const QQmlImports &imports;
const QV4::CompiledData::Unit *qmlUnit;
const QQmlPropertyCacheVector &propertyCaches;
- QVector<QV4::CompiledData::BindingPropertyData> * const bindingPropertyDataPerObject;
+ QVector<QV4::BindingPropertyData> * const bindingPropertyDataPerObject;
};
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlscriptblob.cpp b/src/qml/qml/qqmlscriptblob.cpp
new file mode 100644
index 0000000000..eb103dc434
--- /dev/null
+++ b/src/qml/qml/qqmlscriptblob.cpp
@@ -0,0 +1,263 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qqmlengine_p.h>
+#include <private/qqmlirbuilder_p.h>
+#include <private/qqmlscriptblob_p.h>
+#include <private/qqmlscriptdata_p.h>
+#include <private/qv4runtimecodegen_p.h>
+#include <private/qv4script_p.h>
+
+#include <QtCore/qloggingcategory.h>
+
+Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE)
+Q_LOGGING_CATEGORY(DBG_DISK_CACHE, "qt.qml.diskcache")
+
+QT_BEGIN_NAMESPACE
+
+QQmlScriptBlob::QQmlScriptBlob(const QUrl &url, QQmlTypeLoader *loader)
+ : QQmlTypeLoader::Blob(url, JavaScriptFile, loader)
+ , m_isModule(url.path().endsWith(QLatin1String(".mjs")))
+{
+}
+
+QQmlScriptBlob::~QQmlScriptBlob()
+{
+}
+
+QQmlRefPointer<QQmlScriptData> QQmlScriptBlob::scriptData() const
+{
+ return m_scriptData;
+}
+
+void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
+{
+ if (diskCacheEnabled()) {
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> unit
+ = QV4::ExecutableCompilationUnit::create();
+ QString error;
+ if (unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) {
+ initializeFromCompilationUnit(unit);
+ return;
+ } else {
+ qCDebug(DBG_DISK_CACHE()) << "Error loading" << urlString() << "from disk cache:" << error;
+ }
+ }
+
+ if (!data.exists()) {
+ if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch)
+ setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile"));
+ else
+ setError(QQmlTypeLoader::tr("No such file or directory"));
+ return;
+ }
+
+ QString error;
+ QString source = data.readAll(&error);
+ if (!error.isEmpty()) {
+ setError(error);
+ return;
+ }
+
+ QV4::CompiledData::CompilationUnit unit;
+
+ if (m_isModule) {
+ QList<QQmlJS::DiagnosticMessage> diagnostics;
+ unit = QV4::Compiler::Codegen::compileModule(isDebugging(), urlString(), source,
+ data.sourceTimeStamp(), &diagnostics);
+ QList<QQmlError> errors = QQmlEnginePrivate::qmlErrorFromDiagnostics(urlString(), diagnostics);
+ if (!errors.isEmpty()) {
+ setError(errors);
+ return;
+ }
+ } else {
+ QmlIR::Document irUnit(isDebugging());
+
+ irUnit.jsModule.sourceTimeStamp = data.sourceTimeStamp();
+
+ QmlIR::ScriptDirectivesCollector collector(&irUnit);
+ irUnit.jsParserEngine.setDirectives(&collector);
+
+ QList<QQmlError> errors;
+ irUnit.javaScriptCompilationUnit = QV4::Script::precompile(
+ &irUnit.jsModule, &irUnit.jsParserEngine, &irUnit.jsGenerator, urlString(), finalUrlString(),
+ source, &errors, QV4::Compiler::ContextType::ScriptImportedByQML);
+
+ source.clear();
+ if (!errors.isEmpty()) {
+ setError(errors);
+ return;
+ }
+
+ QmlIR::QmlUnitGenerator qmlGenerator;
+ qmlGenerator.generate(irUnit);
+ unit = std::move(irUnit.javaScriptCompilationUnit);
+ }
+
+ auto executableUnit = QV4::ExecutableCompilationUnit::create(std::move(unit));
+
+ if (diskCacheEnabled()) {
+ QString errorString;
+ if (executableUnit->saveToDisk(url(), &errorString)) {
+ QString error;
+ if (!executableUnit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) {
+ // ignore error, keep using the in-memory compilation unit.
+ }
+ } else {
+ qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of"
+ << executableUnit->fileName() << "to disk:" << errorString;
+ }
+ }
+
+ initializeFromCompilationUnit(executableUnit);
+}
+
+void QQmlScriptBlob::initializeFromCachedUnit(const QV4::CompiledData::Unit *unit)
+{
+ initializeFromCompilationUnit(QV4::ExecutableCompilationUnit::create(
+ QV4::CompiledData::CompilationUnit(unit, urlString(), finalUrlString())));
+}
+
+void QQmlScriptBlob::done()
+{
+ if (isError())
+ return;
+
+ // Check all script dependencies for errors
+ for (int ii = 0; ii < m_scripts.count(); ++ii) {
+ const ScriptReference &script = m_scripts.at(ii);
+ Q_ASSERT(script.script->isCompleteOrError());
+ if (script.script->isError()) {
+ QList<QQmlError> errors = script.script->errors();
+ QQmlError error;
+ error.setUrl(url());
+ error.setLine(script.location.line);
+ error.setColumn(script.location.column);
+ error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString()));
+ errors.prepend(error);
+ setError(errors);
+ return;
+ }
+ }
+
+ if (!m_isModule) {
+ m_scriptData->typeNameCache.adopt(new QQmlTypeNameCache(m_importCache));
+
+ QSet<QString> ns;
+
+ for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) {
+ const ScriptReference &script = m_scripts.at(scriptIndex);
+
+ m_scriptData->scripts.append(script.script);
+
+ if (!script.nameSpace.isNull()) {
+ if (!ns.contains(script.nameSpace)) {
+ ns.insert(script.nameSpace);
+ m_scriptData->typeNameCache->add(script.nameSpace);
+ }
+ }
+ m_scriptData->typeNameCache->add(script.qualifier, scriptIndex, script.nameSpace);
+ }
+
+ m_importCache.populateCache(m_scriptData->typeNameCache.data());
+ }
+ m_scripts.clear();
+}
+
+QString QQmlScriptBlob::stringAt(int index) const
+{
+ return m_scriptData->m_precompiledScript->stringAt(index);
+}
+
+void QQmlScriptBlob::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace)
+{
+ ScriptReference ref;
+ ref.script = blob;
+ ref.location = location;
+ ref.qualifier = qualifier;
+ ref.nameSpace = nameSpace;
+
+ m_scripts << ref;
+}
+
+void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit)
+{
+ Q_ASSERT(!m_scriptData);
+ m_scriptData.adopt(new QQmlScriptData());
+ m_scriptData->url = finalUrl();
+ m_scriptData->urlString = finalUrlString();
+ m_scriptData->m_precompiledScript = unit;
+
+ m_importCache.setBaseUrl(finalUrl(), finalUrlString());
+
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> script = m_scriptData->m_precompiledScript;
+
+ if (!m_isModule) {
+ QList<QQmlError> errors;
+ for (quint32 i = 0, count = script->importCount(); i < count; ++i) {
+ const QV4::CompiledData::Import *import = script->importAt(i);
+ if (!addImport(import, &errors)) {
+ Q_ASSERT(errors.size());
+ QQmlError error(errors.takeFirst());
+ error.setUrl(m_importCache.baseUrl());
+ error.setLine(import->location.line);
+ error.setColumn(import->location.column);
+ errors.prepend(error); // put it back on the list after filling out information.
+ setError(errors);
+ return;
+ }
+ }
+ }
+
+ auto *v4 = QQmlEnginePrivate::getV4Engine(typeLoader()->engine());
+
+ v4->injectModule(unit);
+
+ for (const QString &request: unit->moduleRequests()) {
+ if (v4->moduleForUrl(QUrl(request), unit.data()))
+ continue;
+
+ const QUrl absoluteRequest = unit->finalUrl().resolved(QUrl(request));
+ QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(absoluteRequest);
+ addDependency(blob.data());
+ scriptImported(blob, /* ### */QV4::CompiledData::Location(), /*qualifier*/QString(), /*namespace*/QString());
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlscriptblob_p.h b/src/qml/qml/qqmlscriptblob_p.h
new file mode 100644
index 0000000000..10c0437e7b
--- /dev/null
+++ b/src/qml/qml/qqmlscriptblob_p.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLSCRIPTBLOB_P_H
+#define QQMLSCRIPTBLOB_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/qqmltypeloader_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlScriptData;
+class Q_AUTOTEST_EXPORT QQmlScriptBlob : public QQmlTypeLoader::Blob
+{
+private:
+ friend class QQmlTypeLoader;
+
+ QQmlScriptBlob(const QUrl &, QQmlTypeLoader *);
+
+public:
+ ~QQmlScriptBlob() override;
+
+ struct ScriptReference
+ {
+ QV4::CompiledData::Location location;
+ QString qualifier;
+ QString nameSpace;
+ QQmlRefPointer<QQmlScriptBlob> script;
+ };
+
+ QQmlRefPointer<QQmlScriptData> scriptData() const;
+
+protected:
+ void dataReceived(const SourceCodeData &) override;
+ void initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) override;
+ void done() override;
+
+ QString stringAt(int index) const override;
+
+private:
+ void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override;
+ void initializeFromCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit);
+
+ QList<ScriptReference> m_scripts;
+ QQmlRefPointer<QQmlScriptData> m_scriptData;
+ const bool m_isModule;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLSCRIPTBLOB_P_H
diff --git a/src/qml/qml/qqmlscriptdata.cpp b/src/qml/qml/qqmlscriptdata.cpp
new file mode 100644
index 0000000000..ae268ca904
--- /dev/null
+++ b/src/qml/qml/qqmlscriptdata.cpp
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qqmlscriptdata_p.h>
+#include <private/qqmlcontext_p.h>
+#include <private/qqmlengine_p.h>
+#include <private/qqmlscriptblob_p.h>
+#include <private/qv4engine_p.h>
+#include <private/qv4scopedvalue_p.h>
+#include <private/qv4object_p.h>
+#include <private/qv4qmlcontext_p.h>
+#include <private/qv4module_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlScriptData::QQmlScriptData()
+ : typeNameCache(nullptr)
+ , m_loaded(false)
+{
+}
+
+QQmlContextData *QQmlScriptData::qmlContextDataForContext(QQmlContextData *parentQmlContextData)
+{
+ Q_ASSERT(parentQmlContextData && parentQmlContextData->engine);
+
+ if (m_precompiledScript->isESModule())
+ return nullptr;
+
+ auto qmlContextData = new QQmlContextData();
+
+ qmlContextData->isInternal = true;
+ qmlContextData->isJSContext = true;
+ if (m_precompiledScript->isSharedLibrary())
+ qmlContextData->isPragmaLibraryContext = true;
+ else
+ qmlContextData->isPragmaLibraryContext = parentQmlContextData->isPragmaLibraryContext;
+ qmlContextData->baseUrl = url;
+ qmlContextData->baseUrlString = urlString;
+
+ // For backward compatibility, if there are no imports, we need to use the
+ // imports from the parent context. See QTBUG-17518.
+ if (!typeNameCache->isEmpty()) {
+ qmlContextData->imports = typeNameCache;
+ } else if (!m_precompiledScript->isSharedLibrary()) {
+ qmlContextData->imports = parentQmlContextData->imports;
+ qmlContextData->importedScripts = parentQmlContextData->importedScripts;
+ }
+
+ if (!m_precompiledScript->isSharedLibrary()) {
+ qmlContextData->setParent(parentQmlContextData);
+ } else {
+ qmlContextData->engine = parentQmlContextData->engine; // Fix for QTBUG-21620
+ }
+
+ QV4::ExecutionEngine *v4 = parentQmlContextData->engine->handle();
+ QV4::Scope scope(v4);
+ QV4::ScopedObject scriptsArray(scope);
+ if (qmlContextData->importedScripts.isNullOrUndefined()) {
+ scriptsArray = v4->newArrayObject(scripts.count());
+ qmlContextData->importedScripts.set(v4, scriptsArray);
+ } else {
+ scriptsArray = qmlContextData->importedScripts.valueRef();
+ }
+ QV4::ScopedValue v(scope);
+ for (int ii = 0; ii < scripts.count(); ++ii)
+ scriptsArray->put(ii, (v = scripts.at(ii)->scriptData()->scriptValueForContext(qmlContextData)));
+
+ return qmlContextData;
+}
+
+QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parentQmlContextData)
+{
+ if (m_loaded)
+ return m_value.value();
+
+ Q_ASSERT(parentQmlContextData && parentQmlContextData->engine);
+ QV4::ExecutionEngine *v4 = parentQmlContextData->engine->handle();
+ QV4::Scope scope(v4);
+
+ if (!hasEngine()) {
+ addToEngine(parentQmlContextData->engine);
+ addref();
+ }
+
+ QQmlContextDataRef qmlContextData = qmlContextDataForContext(parentQmlContextData);
+ QV4::Scoped<QV4::QmlContext> qmlExecutionContext(scope);
+ if (qmlContextData)
+ qmlExecutionContext =
+ QV4::QmlContext::create(v4->rootContext(), qmlContextData, /* scopeObject: */ nullptr);
+
+ QV4::Scoped<QV4::Module> module(scope, m_precompiledScript->instantiate(v4));
+ if (module) {
+ if (qmlContextData) {
+ module->d()->scope->outer.set(v4, qmlExecutionContext->d());
+ qmlExecutionContext->d()->qml()->module.set(v4, module->d());
+ }
+
+ module->evaluate();
+ }
+
+ if (v4->hasException) {
+ QQmlError error = v4->catchExceptionAsQmlError();
+ if (error.isValid())
+ QQmlEnginePrivate::get(v4)->warning(error);
+ }
+
+ QV4::ScopedValue value(scope);
+ if (qmlContextData)
+ value = qmlExecutionContext->d()->qml();
+ else if (module)
+ value = module->d();
+
+ if (m_precompiledScript->isSharedLibrary() || m_precompiledScript->isESModule()) {
+ m_loaded = true;
+ m_value.set(v4, value);
+ }
+
+ return value->asReturnedValue();
+}
+
+void QQmlScriptData::clear()
+{
+ typeNameCache = nullptr;
+ scripts.clear();
+
+ // An addref() was made when the QQmlCleanup was added to the engine.
+ release();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlscriptdata_p.h b/src/qml/qml/qqmlscriptdata_p.h
new file mode 100644
index 0000000000..80b65b699c
--- /dev/null
+++ b/src/qml/qml/qqmlscriptdata_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLSCRIPTDATA_P_H
+#define QQMLSCRIPTDATA_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/qqmlrefcount_p.h>
+#include <private/qqmlcleanup_p.h>
+#include <private/qqmlscriptblob_p.h>
+#include <private/qv4value_p.h>
+#include <private/qv4persistent_p.h>
+#include <private/qv4executablecompilationunit_p.h>
+
+#include <QtCore/qurl.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlTypeNameCache;
+class QQmlContextData;
+
+// QQmlScriptData instances are created, uninitialized, by the loader in the
+// load thread. The first time they are used by the VME, they are initialized which
+// creates their v8 objects and they are referenced and added to the engine's cleanup
+// list. During QQmlCleanup::clear() all v8 resources are destroyed, and the
+// reference that was created is released but final deletion only occurs once all the
+// references as released. This is all intended to ensure that the v8 resources are
+// only created and destroyed in the main thread :)
+class Q_AUTOTEST_EXPORT QQmlScriptData : public QQmlCleanup, public QQmlRefCount
+{
+private:
+ friend class QQmlTypeLoader;
+
+ QQmlScriptData();
+
+public:
+ QUrl url;
+ QString urlString;
+ QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
+ QVector<QQmlRefPointer<QQmlScriptBlob>> scripts;
+
+ QV4::ReturnedValue scriptValueForContext(QQmlContextData *parentCtxt);
+
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit() const { return m_precompiledScript; }
+
+protected:
+ void clear() override; // From QQmlCleanup
+
+private:
+ friend class QQmlScriptBlob;
+
+ void initialize(QQmlEngine *);
+ QQmlContextData *qmlContextDataForContext(QQmlContextData *parentQmlContextData);
+
+ bool m_loaded;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> m_precompiledScript;
+ QV4::PersistentValue m_value;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLSCRIPTDATA_P_H
diff --git a/src/qml/qml/qqmlsourcecoordinate_p.h b/src/qml/qml/qqmlsourcecoordinate_p.h
new file mode 100644
index 0000000000..55e11fd147
--- /dev/null
+++ b/src/qml/qml/qqmlsourcecoordinate_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLSOURCECOORDINATE_P_H
+#define QQMLSOURCECOORDINATE_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/qglobal.h>
+
+#include <limits>
+
+QT_BEGIN_NAMESPACE
+
+// These methods are needed because in some public methods we historically interpret -1 as the
+// invalid line or column, even though all the lines and columns are 1-based. Also, the different
+// integer ranges may turn certain large values into invalid ones on conversion.
+
+template<typename From, typename To>
+To qmlConvertSourceCoordinate(From n);
+
+template<>
+inline quint16 qmlConvertSourceCoordinate<int, quint16>(int n)
+{
+ return (n > 0 && n <= int(std::numeric_limits<quint16>::max())) ? quint16(n) : 0;
+}
+
+template<>
+inline quint32 qmlConvertSourceCoordinate<int, quint32>(int n)
+{
+ return n > 0 ? quint32(n) : 0u;
+}
+
+// TODO: In Qt6, change behavior and make the invalid coordinate 0 for the following two methods.
+
+template<>
+inline int qmlConvertSourceCoordinate<quint16, int>(quint16 n)
+{
+ return (n == 0u) ? -1 : int(n);
+}
+
+template<>
+inline int qmlConvertSourceCoordinate<quint32, int>(quint32 n)
+{
+ return (n == 0u || n > quint32(std::numeric_limits<int>::max())) ? -1 : int(n);
+}
+
+QT_END_NAMESPACE
+
+#endif // QQMLSOURCECOORDINATE_P_H
diff --git a/src/qml/qml/qqmlstaticmetaobject.cpp b/src/qml/qml/qqmlstaticmetaobject.cpp
new file mode 100644
index 0000000000..218d0134fd
--- /dev/null
+++ b/src/qml/qml/qqmlstaticmetaobject.cpp
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlstaticmetaobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+int *QQmlStaticMetaObject::constructorParameterTypes(int index, ArgTypeStorage *dummy,
+ QByteArray *unknownTypeError) const
+{
+ QMetaMethod m = _m.asT2()->constructor(index);
+ return methodParameterTypes(m, dummy, unknownTypeError);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlstaticmetaobject_p.h b/src/qml/qml/qqmlstaticmetaobject_p.h
new file mode 100644
index 0000000000..e1ca496080
--- /dev/null
+++ b/src/qml/qml/qqmlstaticmetaobject_p.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLSTATICMETAOBJECT_P_H
+#define QQMLSTATICMETAOBJECT_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/qqmlobjectorgadget_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlStaticMetaObject : public QQmlObjectOrGadget {
+public:
+ QQmlStaticMetaObject(const QMetaObject* metaObject)
+ : QQmlObjectOrGadget(metaObject)
+ {}
+ int *constructorParameterTypes(int index, ArgTypeStorage *dummy, QByteArray *unknownTypeError) const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLSTATICMETAOBJECT_P_H
diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp
new file mode 100644
index 0000000000..874bcd4bca
--- /dev/null
+++ b/src/qml/qml/qqmltype.cpp
@@ -0,0 +1,885 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmltype_p_p.h"
+
+#include <QtQml/qjsvalue.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlcomponent.h>
+
+#include <private/qqmlcustomparser_p.h>
+#include <private/qqmldata_p.h>
+#include <private/qqmlmetatypedata_p.h>
+#include <private/qqmlpropertycache_p.h>
+#include <private/qqmltypedata_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type)
+ : regType(type), iid(nullptr), typeId(0), listId(0), revision(0),
+ containsRevisionedAttributes(false), baseMetaObject(nullptr),
+ index(-1), isSetup(false), isEnumFromCacheSetup(false), isEnumFromBaseSetup(false),
+ haveSuperType(false)
+{
+ switch (type) {
+ case QQmlType::CppType:
+ extraData.cd = new QQmlCppTypeData;
+ extraData.cd->allocationSize = 0;
+ extraData.cd->newFunc = nullptr;
+ extraData.cd->parserStatusCast = -1;
+ extraData.cd->extFunc = nullptr;
+ extraData.cd->extMetaObject = nullptr;
+ extraData.cd->customParser = nullptr;
+ extraData.cd->attachedPropertiesFunc = nullptr;
+ extraData.cd->attachedPropertiesType = nullptr;
+ extraData.cd->propertyValueSourceCast = -1;
+ extraData.cd->propertyValueInterceptorCast = -1;
+ extraData.cd->registerEnumClassesUnscoped = true;
+ break;
+ case QQmlType::SingletonType:
+ case QQmlType::CompositeSingletonType:
+ extraData.sd = new QQmlSingletonTypeData;
+ extraData.sd->singletonInstanceInfo = nullptr;
+ break;
+ case QQmlType::InterfaceType:
+ extraData.cd = nullptr;
+ break;
+ case QQmlType::CompositeType:
+ extraData.fd = new QQmlCompositeTypeData;
+ break;
+ default: qFatal("QQmlTypePrivate Internal Error.");
+ }
+}
+
+QQmlTypePrivate::~QQmlTypePrivate()
+{
+ qDeleteAll(scopedEnums);
+ for (const auto &metaObject : metaObjects)
+ free(metaObject.metaObject);
+ switch (regType) {
+ case QQmlType::CppType:
+ delete extraData.cd->customParser;
+ delete extraData.cd;
+ break;
+ case QQmlType::SingletonType:
+ case QQmlType::CompositeSingletonType:
+ delete extraData.sd->singletonInstanceInfo;
+ delete extraData.sd;
+ break;
+ case QQmlType::CompositeType:
+ delete extraData.fd;
+ break;
+ default: //Also InterfaceType, because it has no extra data
+ break;
+ }
+}
+
+QQmlType::QQmlType() = default;
+QQmlType::QQmlType(const QQmlType &) = default;
+QQmlType::QQmlType(QQmlType &&) = default;
+QQmlType &QQmlType::operator =(const QQmlType &other) = default;
+QQmlType &QQmlType::operator =(QQmlType &&other) = default;
+QQmlType::QQmlType(const QQmlTypePrivate *priv) : d(priv) {}
+QQmlType::~QQmlType() = default;
+
+QHashedString QQmlType::module() const
+{
+ if (!d)
+ return QHashedString();
+ return d->module;
+}
+
+int QQmlType::majorVersion() const
+{
+ if (!d)
+ return -1;
+ return d->version_maj;
+}
+
+int QQmlType::minorVersion() const
+{
+ if (!d)
+ return -1;
+ return d->version_min;
+}
+
+bool QQmlType::availableInVersion(int vmajor, int vminor) const
+{
+ Q_ASSERT(vmajor >= 0 && vminor >= 0);
+ if (!d)
+ return false;
+ return vmajor == d->version_maj && vminor >= d->version_min;
+}
+
+bool QQmlType::availableInVersion(const QHashedStringRef &module, int vmajor, int vminor) const
+{
+ Q_ASSERT(vmajor >= 0 && vminor >= 0);
+ if (!d)
+ return false;
+ return module == d->module && vmajor == d->version_maj && vminor >= d->version_min;
+}
+
+QQmlType QQmlTypePrivate::resolveCompositeBaseType(QQmlEnginePrivate *engine) const
+{
+ Q_ASSERT(isComposite());
+ if (!engine)
+ return QQmlType();
+ QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()));
+ if (td.isNull() || !td->isComplete())
+ return QQmlType();
+ QV4::ExecutableCompilationUnit *compilationUnit = td->compilationUnit();
+ const QMetaObject *mo = compilationUnit->rootPropertyCache()->firstCppMetaObject();
+ return QQmlMetaType::qmlType(mo);
+}
+
+QQmlPropertyCache *QQmlTypePrivate::compositePropertyCache(QQmlEnginePrivate *engine) const
+{
+ // similar logic to resolveCompositeBaseType
+ Q_ASSERT(isComposite());
+ if (!engine)
+ return nullptr;
+ QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()));
+ if (td.isNull() || !td->isComplete())
+ return nullptr;
+ QV4::ExecutableCompilationUnit *compilationUnit = td->compilationUnit();
+ return compilationUnit->rootPropertyCache().data();
+}
+
+static bool isPropertyRevisioned(const QMetaObject *mo, int index)
+{
+ int i = index;
+ i -= mo->propertyOffset();
+ if (i < 0 && mo->d.superdata)
+ return isPropertyRevisioned(mo->d.superdata, index);
+
+ const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate*>(mo->d.data);
+ if (i >= 0 && i < mop->propertyCount) {
+ int handle = mop->propertyData + 3*i;
+ int flags = mo->d.data[handle + 2];
+
+ return (flags & Revisioned);
+ }
+
+ return false;
+}
+
+void QQmlTypePrivate::init() const
+{
+ if (isSetup)
+ return;
+
+ QMutexLocker lock(QQmlMetaType::typeRegistrationLock());
+ if (isSetup)
+ return;
+
+ const QMetaObject *mo = baseMetaObject;
+ if (!mo) {
+ // version 0 singleton type without metaobject information
+ return;
+ }
+
+ if (regType == QQmlType::CppType) {
+ // Setup extended meta object
+ // XXX - very inefficient
+ if (extraData.cd->extFunc) {
+ QMetaObjectBuilder builder;
+ QQmlMetaType::clone(builder, extraData.cd->extMetaObject, extraData.cd->extMetaObject,
+ extraData.cd->extMetaObject);
+ builder.setFlags(QMetaObjectBuilder::DynamicMetaObject);
+ QMetaObject *mmo = builder.toMetaObject();
+ mmo->d.superdata = mo;
+ QQmlProxyMetaObject::ProxyData data = { mmo, extraData.cd->extFunc, 0, 0 };
+ metaObjects << data;
+ }
+ }
+
+ metaObjects.append(QQmlMetaType::proxyData(
+ mo, baseMetaObject, metaObjects.isEmpty() ? nullptr
+ : metaObjects.constLast().metaObject));
+
+ for (int ii = 0; ii < metaObjects.count(); ++ii) {
+ metaObjects[ii].propertyOffset =
+ metaObjects.at(ii).metaObject->propertyOffset();
+ metaObjects[ii].methodOffset =
+ metaObjects.at(ii).metaObject->methodOffset();
+ }
+
+ // Check for revisioned details
+ {
+ const QMetaObject *mo = nullptr;
+ if (metaObjects.isEmpty())
+ mo = baseMetaObject;
+ else
+ mo = metaObjects.constFirst().metaObject;
+
+ for (int ii = 0; !containsRevisionedAttributes && ii < mo->propertyCount(); ++ii) {
+ if (isPropertyRevisioned(mo, ii))
+ containsRevisionedAttributes = true;
+ }
+
+ for (int ii = 0; !containsRevisionedAttributes && ii < mo->methodCount(); ++ii) {
+ if (mo->method(ii).revision() != 0)
+ containsRevisionedAttributes = true;
+ }
+ }
+
+ isSetup = true;
+ lock.unlock();
+}
+
+void QQmlTypePrivate::initEnums(QQmlEnginePrivate *engine) const
+{
+ const QQmlPropertyCache *cache = (!isEnumFromCacheSetup && isComposite())
+ ? compositePropertyCache(engine)
+ : nullptr;
+
+ const QMetaObject *metaObject = !isEnumFromCacheSetup
+ ? baseMetaObject // beware: It could be a singleton type without metaobject
+ : nullptr;
+
+ if (!cache && !metaObject)
+ return;
+
+ init();
+
+ QMutexLocker lock(QQmlMetaType::typeRegistrationLock());
+
+ if (cache) {
+ insertEnumsFromPropertyCache(cache);
+ isEnumFromCacheSetup = true;
+ }
+
+ if (metaObject) {
+ insertEnums(metaObject);
+ isEnumFromBaseSetup = true;
+ }
+}
+
+void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const
+{
+ // Add any enum values defined by 'related' classes
+ if (metaObject->d.relatedMetaObjects) {
+ const auto *related = metaObject->d.relatedMetaObjects;
+ if (related) {
+ while (*related)
+ insertEnums(*related++);
+ }
+ }
+
+ QSet<QString> localEnums;
+ const QMetaObject *localMetaObject = nullptr;
+
+ // Add any enum values defined by this class, overwriting any inherited values
+ for (int ii = 0; ii < metaObject->enumeratorCount(); ++ii) {
+ QMetaEnum e = metaObject->enumerator(ii);
+ const bool isScoped = e.isScoped();
+ QStringHash<int> *scoped = isScoped ? new QStringHash<int>() : nullptr;
+
+ // We allow enums in sub-classes to overwrite enums from base-classes, such as
+ // ListView.Center (from enum PositionMode) overwriting Item.Center (from enum TransformOrigin).
+ // This is acceptable because the _use_ of the enum from the QML side requires qualification
+ // anyway, i.e. ListView.Center vs. Item.Center.
+ // However if a class defines two enums with the same value, then that must produce a warning
+ // because it represents a valid conflict.
+ if (e.enclosingMetaObject() != localMetaObject) {
+ localEnums.clear();
+ localMetaObject = e.enclosingMetaObject();
+ }
+
+ for (int jj = 0; jj < e.keyCount(); ++jj) {
+ const QString key = QString::fromUtf8(e.key(jj));
+ const int value = e.value(jj);
+ if (!isScoped || (regType == QQmlType::CppType && extraData.cd->registerEnumClassesUnscoped)) {
+ if (localEnums.contains(key)) {
+ auto existingEntry = enums.find(key);
+ if (existingEntry != enums.end() && existingEntry.value() != value) {
+ qWarning("Previously registered enum will be overwritten due to name clash: %s.%s", metaObject->className(), key.toUtf8().constData());
+ createEnumConflictReport(metaObject, key);
+ }
+ } else {
+ localEnums.insert(key);
+ }
+ enums.insert(key, value);
+ }
+ if (isScoped)
+ scoped->insert(key, value);
+ }
+
+ if (isScoped) {
+ scopedEnums << scoped;
+ scopedEnumIndex.insert(QString::fromUtf8(e.name()), scopedEnums.count()-1);
+ }
+ }
+}
+
+void QQmlTypePrivate::createListOfPossibleConflictingItems(const QMetaObject *metaObject, QList<EnumInfo> &enumInfoList, QStringList path) const
+{
+ path.append(QString::fromUtf8(metaObject->className()));
+
+ if (metaObject->d.relatedMetaObjects) {
+ const auto *related = metaObject->d.relatedMetaObjects;
+ if (related) {
+ while (*related)
+ createListOfPossibleConflictingItems(*related++, enumInfoList, path);
+ }
+ }
+
+ for (int ii = 0; ii < metaObject->enumeratorCount(); ++ii) {
+ const auto e = metaObject->enumerator(ii);
+
+ for (int jj = 0; jj < e.keyCount(); ++jj) {
+ const QString key = QString::fromUtf8(e.key(jj));
+
+ EnumInfo enumInfo;
+ enumInfo.metaObjectName = QString::fromUtf8(metaObject->className());
+ enumInfo.enumName = QString::fromUtf8(e.name());
+ enumInfo.enumKey = key;
+ enumInfo.scoped = e.isScoped();
+ enumInfo.path = path;
+ enumInfo.metaEnumScope = QString::fromUtf8(e.scope());
+ enumInfoList.append(enumInfo);
+ }
+ }
+}
+
+void QQmlTypePrivate::createEnumConflictReport(const QMetaObject *metaObject, const QString &conflictingKey) const
+{
+ QList<EnumInfo> enumInfoList;
+
+ if (baseMetaObject) // prefer baseMetaObject if available
+ metaObject = baseMetaObject;
+
+ if (!metaObject) { // If there is no metaObject at all return early
+ qWarning() << "No meta object information available. Skipping conflict analysis.";
+ return;
+ }
+
+ createListOfPossibleConflictingItems(metaObject, enumInfoList, QStringList());
+
+ qWarning().noquote() << QLatin1String("Possible conflicting items:");
+ // find items with conflicting key
+ for (const auto &i : qAsConst(enumInfoList)) {
+ if (i.enumKey == conflictingKey)
+ qWarning().noquote().nospace() << " " << i.metaObjectName << "." << i.enumName << "." << i.enumKey << " from scope "
+ << i.metaEnumScope << " injected by " << i.path.join(QLatin1String("->"));
+ }
+}
+
+void QQmlTypePrivate::insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const
+{
+ const QMetaObject *cppMetaObject = cache->firstCppMetaObject();
+
+ while (cache && cache->metaObject() != cppMetaObject) {
+
+ int count = cache->qmlEnumCount();
+ for (int ii = 0; ii < count; ++ii) {
+ QStringHash<int> *scoped = new QStringHash<int>();
+ QQmlEnumData *enumData = cache->qmlEnum(ii);
+
+ for (int jj = 0; jj < enumData->values.count(); ++jj) {
+ const QQmlEnumValue &value = enumData->values.at(jj);
+ enums.insert(value.namedValue, value.value);
+ scoped->insert(value.namedValue, value.value);
+ }
+ scopedEnums << scoped;
+ scopedEnumIndex.insert(enumData->name, scopedEnums.count()-1);
+ }
+ cache = cache->parent();
+ }
+ insertEnums(cppMetaObject);
+}
+
+void QQmlTypePrivate::setName(const QString &uri, const QString &element)
+{
+ module = uri;
+ elementName = element;
+ name = uri.isEmpty() ? element : (uri + QLatin1Char('/') + element);
+}
+
+QByteArray QQmlType::typeName() const
+{
+ if (d) {
+ if (d->regType == SingletonType || d->regType == CompositeSingletonType)
+ return d->extraData.sd->singletonInstanceInfo->typeName.toUtf8();
+ else if (d->baseMetaObject)
+ return d->baseMetaObject->className();
+ }
+ return QByteArray();
+}
+
+QString QQmlType::elementName() const
+{
+ if (!d)
+ return QString();
+ return d->elementName;
+}
+
+QString QQmlType::qmlTypeName() const
+{
+ if (!d)
+ return QString();
+ return d->name;
+}
+
+QObject *QQmlType::create() const
+{
+ if (!d || !isCreatable())
+ return nullptr;
+
+ d->init();
+
+ QObject *rv = (QObject *)operator new(d->extraData.cd->allocationSize);
+ d->extraData.cd->newFunc(rv);
+
+ if (rv && !d->metaObjects.isEmpty())
+ (void)new QQmlProxyMetaObject(rv, &d->metaObjects);
+
+ return rv;
+}
+
+void QQmlType::create(QObject **out, void **memory, size_t additionalMemory) const
+{
+ if (!d || !isCreatable())
+ return;
+
+ d->init();
+
+ QObject *rv = (QObject *)operator new(d->extraData.cd->allocationSize + additionalMemory);
+ d->extraData.cd->newFunc(rv);
+
+ if (rv && !d->metaObjects.isEmpty())
+ (void)new QQmlProxyMetaObject(rv, &d->metaObjects);
+
+ *out = rv;
+ *memory = ((char *)rv) + d->extraData.cd->allocationSize;
+}
+
+QQmlType::SingletonInstanceInfo *QQmlType::singletonInstanceInfo() const
+{
+ if (!d)
+ return nullptr;
+ if (d->regType != SingletonType && d->regType != CompositeSingletonType)
+ return nullptr;
+ return d->extraData.sd->singletonInstanceInfo;
+}
+
+QQmlCustomParser *QQmlType::customParser() const
+{
+ if (!d)
+ return nullptr;
+ if (d->regType != CppType)
+ return nullptr;
+ return d->extraData.cd->customParser;
+}
+
+QQmlType::CreateFunc QQmlType::createFunction() const
+{
+ if (!d || d->regType != CppType)
+ return nullptr;
+ return d->extraData.cd->newFunc;
+}
+
+QString QQmlType::noCreationReason() const
+{
+ if (!d || d->regType != CppType)
+ return QString();
+ return d->extraData.cd->noCreationReason;
+}
+
+bool QQmlType::isCreatable() const
+{
+ return d && d->regType == CppType && d->extraData.cd->newFunc;
+}
+
+QQmlType::ExtensionFunc QQmlType::extensionFunction() const
+{
+ if (!d || d->regType != CppType)
+ return nullptr;
+ return d->extraData.cd->extFunc;
+}
+
+bool QQmlType::isExtendedType() const
+{
+ if (!d)
+ return false;
+ d->init();
+
+ return !d->metaObjects.isEmpty();
+}
+
+bool QQmlType::isSingleton() const
+{
+ return d && (d->regType == SingletonType || d->regType == CompositeSingletonType);
+}
+
+bool QQmlType::isInterface() const
+{
+ return d && d->regType == InterfaceType;
+}
+
+bool QQmlType::isComposite() const
+{
+ return d && d->isComposite();
+}
+
+bool QQmlType::isCompositeSingleton() const
+{
+ return d && d->regType == CompositeSingletonType;
+}
+
+bool QQmlType::isQObjectSingleton() const
+{
+ return d && d->regType == SingletonType && d->extraData.sd->singletonInstanceInfo->qobjectCallback;
+}
+
+bool QQmlType::isQJSValueSingleton() const
+{
+ return d && d->regType == SingletonType && d->extraData.sd->singletonInstanceInfo->scriptCallback;
+}
+
+int QQmlType::typeId() const
+{
+ return d ? d->typeId : -1;
+}
+
+int QQmlType::qListTypeId() const
+{
+ return d ? d->listId : -1;
+}
+
+const QMetaObject *QQmlType::metaObject() const
+{
+ if (!d)
+ return nullptr;
+ d->init();
+
+ if (d->metaObjects.isEmpty())
+ return d->baseMetaObject;
+ else
+ return d->metaObjects.constFirst().metaObject;
+
+}
+
+const QMetaObject *QQmlType::baseMetaObject() const
+{
+ return d ? d->baseMetaObject : nullptr;
+}
+
+bool QQmlType::containsRevisionedAttributes() const
+{
+ if (!d)
+ return false;
+ d->init();
+
+ return d->containsRevisionedAttributes;
+}
+
+int QQmlType::metaObjectRevision() const
+{
+ return d ? d->revision : -1;
+}
+
+QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction(QQmlEnginePrivate *engine) const
+{
+ if (!d)
+ return nullptr;
+ if (d->regType == CppType)
+ return d->extraData.cd->attachedPropertiesFunc;
+
+ QQmlType base;
+ if (d->regType == CompositeType)
+ base = d->resolveCompositeBaseType(engine);
+ return base.attachedPropertiesFunction(engine);
+}
+
+const QMetaObject *QQmlType::attachedPropertiesType(QQmlEnginePrivate *engine) const
+{
+ if (!d)
+ return nullptr;
+ if (d->regType == CppType)
+ return d->extraData.cd->attachedPropertiesType;
+
+ QQmlType base;
+ if (d->regType == CompositeType)
+ base = d->resolveCompositeBaseType(engine);
+ return base.attachedPropertiesType(engine);
+}
+
+#if QT_DEPRECATED_SINCE(5, 14)
+/*
+This is the id passed to qmlAttachedPropertiesById(). This is different from the index
+for the case that a single class is registered under two or more names (eg. Item in
+Qt 4.7 and QtQuick 1.0).
+*/
+int QQmlType::attachedPropertiesId(QQmlEnginePrivate *engine) const
+{
+ if (!d)
+ return -1;
+ if (d->regType == CppType)
+ return d->extraData.cd->attachedPropertiesType ? d->index : -1;
+
+ QQmlType base;
+ if (d->regType == CompositeType)
+ base = d->resolveCompositeBaseType(engine);
+ return base.attachedPropertiesId(engine);
+}
+#endif
+
+int QQmlType::parserStatusCast() const
+{
+ if (!d || d->regType != CppType)
+ return -1;
+ return d->extraData.cd->parserStatusCast;
+}
+
+int QQmlType::propertyValueSourceCast() const
+{
+ if (!d || d->regType != CppType)
+ return -1;
+ return d->extraData.cd->propertyValueSourceCast;
+}
+
+int QQmlType::propertyValueInterceptorCast() const
+{
+ if (!d || d->regType != CppType)
+ return -1;
+ return d->extraData.cd->propertyValueInterceptorCast;
+}
+
+const char *QQmlType::interfaceIId() const
+{
+ if (!d || d->regType != InterfaceType)
+ return nullptr;
+ return d->iid;
+}
+
+int QQmlType::index() const
+{
+ return d ? d->index : -1;
+}
+
+QUrl QQmlType::sourceUrl() const
+{
+ return d ? d->sourceUrl() : QUrl();
+}
+
+int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, bool *ok) const
+{
+ Q_ASSERT(ok);
+ if (d) {
+ *ok = true;
+
+ d->initEnums(engine);
+
+ int *rv = d->enums.value(name);
+ if (rv)
+ return *rv;
+ }
+
+ *ok = false;
+ return -1;
+}
+
+int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &name, bool *ok) const
+{
+ Q_ASSERT(ok);
+ if (d) {
+ *ok = true;
+
+ d->initEnums(engine);
+
+ int *rv = d->enums.value(name);
+ if (rv)
+ return *rv;
+ }
+
+ *ok = false;
+ return -1;
+}
+
+int QQmlType::enumValue(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const
+{
+ Q_ASSERT(ok);
+ if (d) {
+ *ok = true;
+
+ d->initEnums(engine);
+
+ int *rv = d->enums.value(name);
+ if (rv)
+ return *rv;
+ }
+
+ *ok = false;
+ return -1;
+}
+
+int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const
+{
+ Q_ASSERT(ok);
+ if (d) {
+ *ok = true;
+
+ d->initEnums(engine);
+
+ int *rv = d->scopedEnumIndex.value(name);
+ if (rv)
+ return *rv;
+ }
+
+ *ok = false;
+ return -1;
+}
+
+int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QString &name, bool *ok) const
+{
+ Q_ASSERT(ok);
+ if (d) {
+ *ok = true;
+
+ d->initEnums(engine);
+
+ int *rv = d->scopedEnumIndex.value(name);
+ if (rv)
+ return *rv;
+ }
+
+ *ok = false;
+ return -1;
+}
+
+int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *name, bool *ok) const
+{
+ Q_UNUSED(engine)
+ Q_ASSERT(ok);
+ *ok = true;
+
+ if (d) {
+ Q_ASSERT(index > -1 && index < d->scopedEnums.count());
+ int *rv = d->scopedEnums.at(index)->value(name);
+ if (rv)
+ return *rv;
+ }
+
+ *ok = false;
+ return -1;
+}
+
+int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &name, bool *ok) const
+{
+ Q_UNUSED(engine)
+ Q_ASSERT(ok);
+ *ok = true;
+
+ if (d) {
+ Q_ASSERT(index > -1 && index < d->scopedEnums.count());
+ int *rv = d->scopedEnums.at(index)->value(name);
+ if (rv)
+ return *rv;
+ }
+
+ *ok = false;
+ return -1;
+}
+
+int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &scopedEnumName, const QByteArray &name, bool *ok) const
+{
+ Q_ASSERT(ok);
+ if (d) {
+ *ok = true;
+
+ d->initEnums(engine);
+
+ int *rv = d->scopedEnumIndex.value(QHashedCStringRef(scopedEnumName.constData(), scopedEnumName.length()));
+ if (rv) {
+ int index = *rv;
+ Q_ASSERT(index > -1 && index < d->scopedEnums.count());
+ rv = d->scopedEnums.at(index)->value(QHashedCStringRef(name.constData(), name.length()));
+ if (rv)
+ return *rv;
+ }
+ }
+
+ *ok = false;
+ return -1;
+}
+
+int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &scopedEnumName, const QStringRef &name, bool *ok) const
+{
+ Q_ASSERT(ok);
+ if (d) {
+ *ok = true;
+
+ d->initEnums(engine);
+
+ int *rv = d->scopedEnumIndex.value(QHashedStringRef(scopedEnumName));
+ if (rv) {
+ int index = *rv;
+ Q_ASSERT(index > -1 && index < d->scopedEnums.count());
+ rv = d->scopedEnums.at(index)->value(QHashedStringRef(name));
+ if (rv)
+ return *rv;
+ }
+ }
+
+ *ok = false;
+ return -1;
+}
+
+void QQmlType::refHandle(const QQmlTypePrivate *priv)
+{
+ if (priv)
+ priv->addref();
+}
+
+void QQmlType::derefHandle(const QQmlTypePrivate *priv)
+{
+ if (priv)
+ priv->release();
+}
+
+int QQmlType::refCount(const QQmlTypePrivate *priv)
+{
+ if (priv)
+ return priv->count();
+ return -1;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltype_p.h b/src/qml/qml/qqmltype_p.h
new file mode 100644
index 0000000000..ec27b38a73
--- /dev/null
+++ b/src/qml/qml/qqmltype_p.h
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLTYPE_P_H
+#define QQMLTYPE_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 <functional>
+
+#include <private/qtqmlglobal_p.h>
+#include <private/qqmlrefcount_p.h>
+
+#include <QtQml/qqmlprivate.h>
+#include <QtQml/qjsvalue.h>
+
+#include <QtCore/qobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class QHashedCStringRef;
+class QQmlTypePrivate;
+class QHashedString;
+class QHashedStringRef;
+class QQmlCustomParser;
+class QQmlEnginePrivate;
+class QQmlPropertyCache;
+
+namespace QV4 {
+struct String;
+}
+
+class Q_QML_PRIVATE_EXPORT QQmlType
+{
+public:
+ QQmlType();
+ QQmlType(const QQmlType &other);
+ QQmlType(QQmlType &&other);
+ QQmlType &operator =(const QQmlType &other);
+ QQmlType &operator =(QQmlType &&other);
+ explicit QQmlType(const QQmlTypePrivate *priv);
+ ~QQmlType();
+
+ bool operator ==(const QQmlType &other) const {
+ return d.data() == other.d.data();
+ }
+
+ bool isValid() const { return !d.isNull(); }
+
+ QByteArray typeName() const;
+ QString qmlTypeName() const;
+ QString elementName() const;
+
+ QHashedString module() const;
+ int majorVersion() const;
+ int minorVersion() const;
+
+ bool availableInVersion(int vmajor, int vminor) const;
+ bool availableInVersion(const QHashedStringRef &module, int vmajor, int vminor) const;
+
+ QObject *create() const;
+ void create(QObject **, void **, size_t) const;
+
+ typedef void (*CreateFunc)(void *);
+ CreateFunc createFunction() const;
+ QQmlCustomParser *customParser() const;
+
+ bool isCreatable() const;
+ typedef QObject *(*ExtensionFunc)(QObject *);
+ ExtensionFunc extensionFunction() const;
+ bool isExtendedType() const;
+ QString noCreationReason() const;
+
+ bool isSingleton() const;
+ bool isInterface() const;
+ bool isComposite() const;
+ bool isCompositeSingleton() const;
+ bool isQObjectSingleton() const;
+ bool isQJSValueSingleton() const;
+
+ int typeId() const;
+ int qListTypeId() const;
+
+ const QMetaObject *metaObject() const;
+ const QMetaObject *baseMetaObject() const;
+ int metaObjectRevision() const;
+ bool containsRevisionedAttributes() const;
+
+ QQmlAttachedPropertiesFunc attachedPropertiesFunction(QQmlEnginePrivate *engine) const;
+ const QMetaObject *attachedPropertiesType(QQmlEnginePrivate *engine) const;
+#if QT_DEPRECATED_SINCE(5, 14)
+ QT_DEPRECATED int attachedPropertiesId(QQmlEnginePrivate *engine) const;
+#endif
+
+ int parserStatusCast() const;
+ const char *interfaceIId() const;
+ int propertyValueSourceCast() const;
+ int propertyValueInterceptorCast() const;
+
+ int index() const;
+
+ struct Q_QML_PRIVATE_EXPORT SingletonInstanceInfo
+ {
+ QJSValue (*scriptCallback)(QQmlEngine *, QJSEngine *) = nullptr;
+ std::function<QObject *(QQmlEngine *, QJSEngine *)> qobjectCallback = {};
+ const QMetaObject *instanceMetaObject = nullptr;
+ QString typeName;
+ QUrl url; // used by composite singletons
+ };
+ SingletonInstanceInfo *singletonInstanceInfo() const;
+
+ QUrl sourceUrl() const;
+
+ int enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &, bool *ok) const;
+ int enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &, bool *ok) const;
+ int enumValue(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const;
+
+ int scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const;
+ int scopedEnumIndex(QQmlEnginePrivate *engine, const QString &, bool *ok) const;
+ int scopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *, bool *ok) const;
+ int scopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &, bool *ok) const;
+ int scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &, const QByteArray &, bool *ok) const;
+ int scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &, const QStringRef &, bool *ok) const;
+
+ const QQmlTypePrivate *priv() const { return d.data(); }
+ static void refHandle(const QQmlTypePrivate *priv);
+ static void derefHandle(const QQmlTypePrivate *priv);
+ static int refCount(const QQmlTypePrivate *priv);
+
+ enum RegistrationType {
+ CppType = 0,
+ SingletonType = 1,
+ InterfaceType = 2,
+ CompositeType = 3,
+ CompositeSingletonType = 4,
+ AnyRegistrationType = 255
+ };
+
+private:
+ friend uint qHash(const QQmlType &t, uint seed);
+ QQmlRefPointer<const QQmlTypePrivate> d;
+};
+
+inline uint qHash(const QQmlType &t, uint seed = 0)
+{
+ return qHash(reinterpret_cast<quintptr>(t.d.data()), seed);
+}
+
+QT_END_NAMESPACE
+
+#endif // QQMLTYPE_P_H
diff --git a/src/qml/qml/qqmltype_p_p.h b/src/qml/qml/qqmltype_p_p.h
new file mode 100644
index 0000000000..6a2d961de8
--- /dev/null
+++ b/src/qml/qml/qqmltype_p_p.h
@@ -0,0 +1,170 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLTYPE_P_P_H
+#define QQMLTYPE_P_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/qqmltype_p.h>
+#include <private/qstringhash_p.h>
+#include <private/qqmlproxymetaobject_p.h>
+#include <private/qqmlrefcount_p.h>
+#include <private/qqmlpropertycache_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlTypePrivate : public QQmlRefCount
+{
+ Q_DISABLE_COPY_MOVE(QQmlTypePrivate)
+public:
+ QQmlTypePrivate(QQmlType::RegistrationType type);
+
+ void init() const;
+ void initEnums(QQmlEnginePrivate *engine) const;
+ void insertEnums(const QMetaObject *metaObject) const;
+ void insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const;
+
+ QUrl sourceUrl() const
+ {
+ switch (regType) {
+ case QQmlType::CompositeType:
+ return extraData.fd->url;
+ case QQmlType::CompositeSingletonType:
+ return extraData.sd->singletonInstanceInfo->url;
+ default:
+ return QUrl();
+ }
+ }
+
+ bool isComposite() const
+ {
+ return regType == QQmlType::CompositeType || regType == QQmlType::CompositeSingletonType;
+ }
+
+ QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const;
+ QQmlPropertyCache *compositePropertyCache(QQmlEnginePrivate *engine) const;
+
+ QQmlType::RegistrationType regType;
+
+ struct QQmlCppTypeData
+ {
+ int allocationSize;
+ void (*newFunc)(void *);
+ QString noCreationReason;
+ int parserStatusCast;
+ QObject *(*extFunc)(QObject *);
+ const QMetaObject *extMetaObject;
+ QQmlCustomParser *customParser;
+ QQmlAttachedPropertiesFunc attachedPropertiesFunc;
+ const QMetaObject *attachedPropertiesType;
+ int propertyValueSourceCast;
+ int propertyValueInterceptorCast;
+ bool registerEnumClassesUnscoped;
+ };
+
+ struct QQmlSingletonTypeData
+ {
+ QQmlType::SingletonInstanceInfo *singletonInstanceInfo;
+ };
+
+ struct QQmlCompositeTypeData
+ {
+ QUrl url;
+ };
+
+ union extraData {
+ QQmlCppTypeData* cd;
+ QQmlSingletonTypeData* sd;
+ QQmlCompositeTypeData* fd;
+ } extraData;
+
+ const char *iid;
+ QHashedString module;
+ QString name;
+ QString elementName;
+ int version_maj;
+ int version_min;
+ int typeId;
+ int listId;
+ int revision;
+ mutable bool containsRevisionedAttributes;
+ mutable QQmlType superType;
+ const QMetaObject *baseMetaObject;
+
+ int index;
+ mutable volatile bool isSetup:1;
+ mutable volatile bool isEnumFromCacheSetup:1;
+ mutable volatile bool isEnumFromBaseSetup:1;
+ mutable bool haveSuperType:1;
+ mutable QList<QQmlProxyMetaObject::ProxyData> metaObjects;
+ mutable QStringHash<int> enums;
+ mutable QStringHash<int> scopedEnumIndex; // maps from enum name to index in scopedEnums
+ mutable QList<QStringHash<int>*> scopedEnums;
+
+ void setName(const QString &uri, const QString &element);
+
+private:
+ ~QQmlTypePrivate() override;
+
+ struct EnumInfo {
+ QStringList path;
+ QString metaObjectName;
+ QString enumName;
+ QString enumKey;
+ QString metaEnumScope;
+ bool scoped;
+ };
+
+ void createListOfPossibleConflictingItems(const QMetaObject *metaObject, QList<EnumInfo> &enumInfoList, QStringList path) const;
+ void createEnumConflictReport(const QMetaObject *metaObject, const QString &conflictingKey) const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLTYPE_P_P_H
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp
index 66d3afc7a0..9ff0e3fb9e 100644
--- a/src/qml/compiler/qqmltypecompiler.cpp
+++ b/src/qml/qml/qqmltypecompiler.cpp
@@ -39,12 +39,11 @@
#include "qqmltypecompiler_p.h"
-#include <private/qqmlirbuilder_p.h>
#include <private/qqmlobjectcreator_p.h>
#include <private/qqmlcustomparser_p.h>
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlcomponent_p.h>
-#include <private/qqmldelegatecomponent_p.h>
+#include <private/qqmlpropertyresolver_p.h>
#define COMPILE_EXCEPTION(token, desc) \
{ \
@@ -56,7 +55,7 @@ QT_BEGIN_NAMESPACE
QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData,
QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
+ QV4::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
: resolvedTypes(resolvedTypeCache)
, engine(engine)
, typeData(typeData)
@@ -66,7 +65,7 @@ QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *type
{
}
-QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile()
+QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile()
{
// Build property caches and VME meta object data
@@ -83,8 +82,8 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile()
{
QQmlPropertyCacheCreator<QQmlTypeCompiler> propertyCacheBuilder(&m_propertyCaches, &pendingGroupPropertyBindings,
engine, this, imports());
- QQmlCompileError error = propertyCacheBuilder.buildMetaObjects();
- if (error.isSet()) {
+ QQmlJS::DiagnosticMessage error = propertyCacheBuilder.buildMetaObjects();
+ if (error.isValid()) {
recordError(error);
return nullptr;
}
@@ -134,7 +133,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile()
return nullptr;
}
- if (!document->javaScriptCompilationUnit) {
+ if (!document->javaScriptCompilationUnit.unitData()) {
// Compile JS binding expressions and signal handlers if necessary
{
// We can compile script strings ahead of time, but they must be compiled
@@ -145,11 +144,11 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile()
document->jsModule.fileName = typeData->urlString();
document->jsModule.finalUrl = typeData->finalUrlString();
- QmlIR::JSCodeGen v4CodeGenerator(document->code, &document->jsGenerator, &document->jsModule, &document->jsParserEngine,
- document->program, &document->jsGenerator.stringTable, engine->v8engine()->illegalNames());
- QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator);
- if (!jsCodeGen.generateCodeForComponents())
+ QmlIR::JSCodeGen v4CodeGenerator(document, engine->v4engine()->illegalNames());
+ if (!v4CodeGenerator.generateCodeForComponents(componentRoots())) {
+ recordError(v4CodeGenerator.error());
return nullptr;
+ }
document->javaScriptCompilationUnit = v4CodeGenerator.generateCompilationUnit(/*generated unit data*/false);
}
@@ -159,22 +158,17 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile()
QmlIR::QmlUnitGenerator qmlGenerator;
qmlGenerator.generate(*document, dependencyHasher);
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = document->javaScriptCompilationUnit;
+ if (!errors.isEmpty())
+ return nullptr;
+
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit
+ = QV4::ExecutableCompilationUnit::create(std::move(
+ document->javaScriptCompilationUnit));
compilationUnit->typeNameCache = typeNameCache;
compilationUnit->resolvedTypes = *resolvedTypes;
compilationUnit->propertyCaches = std::move(m_propertyCaches);
Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast<int>(compilationUnit->objectCount()));
-
- if (errors.isEmpty())
- return compilationUnit;
- else
- return nullptr;
-}
-
-void QQmlTypeCompiler::recordError(QQmlError error)
-{
- error.setUrl(url());
- errors << error;
+ return compilationUnit;
}
void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description)
@@ -187,9 +181,14 @@ void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location,
errors << error;
}
-void QQmlTypeCompiler::recordError(const QQmlCompileError &error)
+void QQmlTypeCompiler::recordError(const QQmlJS::DiagnosticMessage &message)
{
- recordError(error.location, error.description);
+ QQmlError error;
+ error.setDescription(message.message);
+ error.setLine(message.line);
+ error.setColumn(message.column);
+ error.setUrl(url());
+ errors << error;
}
QString QQmlTypeCompiler::stringAt(int idx) const
@@ -209,7 +208,7 @@ int QQmlTypeCompiler::registerConstant(QV4::ReturnedValue v)
const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const
{
- return document->javaScriptCompilationUnit->unitData();
+ return document->javaScriptCompilationUnit.unitData();
}
const QQmlImports *QQmlTypeCompiler::imports() const
@@ -253,11 +252,6 @@ const QV4::Compiler::StringTableGenerator *QQmlTypeCompiler::stringPool() const
return &document->jsGenerator.stringTable;
}
-void QQmlTypeCompiler::setBindingPropertyDataPerObject(const QVector<QV4::CompiledData::BindingPropertyData> &propertyData)
-{
- document->javaScriptCompilationUnit->bindingPropertyDataPerObject = propertyData;
-}
-
QString QQmlTypeCompiler::bindingAsString(const QmlIR::Object *object, int scriptIndex) const
{
return object->bindingAsString(document, scriptIndex);
@@ -298,7 +292,7 @@ SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler)
, qmlObjects(*typeCompiler->qmlObjects())
, imports(typeCompiler->imports())
, customParsers(typeCompiler->customParserCache())
- , illegalNames(typeCompiler->enginePrivate()->v8engine()->illegalNames())
+ , illegalNames(typeCompiler->enginePrivate()->v4engine()->illegalNames())
, propertyCaches(typeCompiler->propertyCaches())
{
}
@@ -333,18 +327,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex);
auto *typeRef = resolvedType(binding->propertyNameIndex);
QQmlType type = typeRef ? typeRef->type : QQmlType();
- if (!type.isValid()) {
- if (imports->resolveType(propertyName, &type, nullptr, nullptr, nullptr)) {
- if (type.isComposite()) {
- QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(type.sourceUrl());
- Q_ASSERT(tdata);
- Q_ASSERT(tdata->isComplete());
-
- auto compilationUnit = tdata->compilationUnit();
- type = QQmlMetaType::qmlType(compilationUnit->metaTypeId);
- }
- }
- }
+ if (!type.isValid())
+ imports->resolveType(propertyName, &type, nullptr, nullptr, nullptr);
const QMetaObject *attachedType = type.attachedPropertiesType(enginePrivate);
if (!attachedType)
@@ -358,7 +342,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
if (!QmlIR::IRBuilder::isSignalPropertyName(propertyName))
continue;
- QmlIR::PropertyResolver resolver(propertyCache);
+ QQmlPropertyResolver resolver(propertyCache);
Q_ASSERT(propertyName.startsWith(QLatin1String("on")));
propertyName.remove(0, 2);
@@ -514,7 +498,7 @@ bool QQmlEnumTypeResolver::resolveEnumBindings()
continue;
const QmlIR::Object *obj = qmlObjects.at(i);
- QmlIR::PropertyResolver resolver(propertyCache);
+ QQmlPropertyResolver resolver(propertyCache);
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
@@ -723,7 +707,7 @@ void QQmlAliasAnnotator::annotateBindingsToAliases()
const QmlIR::Object *obj = qmlObjects.at(i);
- QmlIR::PropertyResolver resolver(propertyCache);
+ QQmlPropertyResolver resolver(propertyCache);
QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
@@ -755,7 +739,7 @@ void QQmlScriptStringScanner::scan()
const QmlIR::Object *obj = qmlObjects.at(i);
- QmlIR::PropertyResolver resolver(propertyCache);
+ QQmlPropertyResolver resolver(propertyCache);
QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
@@ -781,9 +765,26 @@ QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *t
{
}
+static bool isUsableComponent(const QMetaObject *metaObject)
+{
+ // The metaObject is a component we're interested in if it either is a QQmlComponent itself
+ // or if any of its parents is a QQmlAbstractDelegateComponent. We don't want to include
+ // qqmldelegatecomponent_p.h because it belongs to QtQmlModels.
+
+ if (metaObject == &QQmlComponent::staticMetaObject)
+ return true;
+
+ for (; metaObject; metaObject = metaObject->superClass()) {
+ if (qstrcmp(metaObject->className(), "QQmlAbstractDelegateComponent") == 0)
+ return true;
+ }
+
+ return false;
+}
+
void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache)
{
- QmlIR::PropertyResolver propertyResolver(propertyCache);
+ QQmlPropertyResolver propertyResolver(propertyCache);
QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
@@ -802,15 +803,9 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
firstMetaObject = tr->type.metaObject();
else if (tr->compilationUnit)
firstMetaObject = tr->compilationUnit->rootPropertyCache()->firstCppMetaObject();
- // 1: test for QQmlComponent
- if (firstMetaObject && firstMetaObject == &QQmlComponent::staticMetaObject)
+ if (isUsableComponent(firstMetaObject))
continue;
- // 2: test for QQmlAbstractDelegateComponent
- while (firstMetaObject && firstMetaObject != &QQmlAbstractDelegateComponent::staticMetaObject)
- firstMetaObject = firstMetaObject->superClass();
- if (firstMetaObject)
- continue;
- // if here, not a QQmlComponent or a QQmlAbstractDelegateComponent, so needs wrapping
+ // if here, not a QQmlComponent, so needs wrapping
QQmlPropertyData *pd = nullptr;
if (binding->propertyNameIndex != quint32(0)) {
@@ -846,7 +841,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent;
if (!containsResolvedType(syntheticComponent->inheritedTypeNameIndex)) {
- auto typeRef = new QV4::CompiledData::ResolvedTypeReference;
+ auto typeRef = new QV4::ResolvedTypeReference;
typeRef->type = componentType;
typeRef->majorVersion = componentType.majorVersion();
typeRef->minorVersion = componentType.minorVersion();
@@ -1012,17 +1007,17 @@ bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex)
for (int objectIndex: qAsConst(_objectsWithAliases)) {
- QQmlCompileError error;
+ QQmlJS::DiagnosticMessage error;
const auto result = resolveAliasesInObject(objectIndex, &error);
- if (error.isSet()) {
+ if (error.isValid()) {
recordError(error);
return false;
}
if (result == AllAliasesResolved) {
- QQmlCompileError error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex);
- if (error.isSet()) {
+ QQmlJS::DiagnosticMessage error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex, enginePrivate);
+ if (error.isValid()) {
recordError(error);
return false;
}
@@ -1050,7 +1045,9 @@ bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex)
return true;
}
-QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, QQmlCompileError *error)
+QQmlComponentAndAliasResolver::AliasResolutionResult
+QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
+ QQmlJS::DiagnosticMessage *error)
{
const QmlIR::Object * const obj = qmlObjects->at(objectIndex);
if (!obj->aliasCount())
@@ -1068,7 +1065,9 @@ QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolv
const int idIndex = alias->idIndex;
const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1);
if (targetObjectIndex == -1) {
- *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex)));
+ *error = qQmlCompileError(
+ alias->referenceLocation,
+ tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex)));
break;
}
@@ -1096,11 +1095,13 @@ QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolv
} else {
QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex);
if (!targetCache) {
- *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString()));
+ *error = qQmlCompileError(
+ alias->referenceLocation,
+ tr("Invalid alias target location: %1").arg(property.toString()));
break;
}
- QmlIR::PropertyResolver resolver(targetCache);
+ QQmlPropertyResolver resolver(targetCache);
QQmlPropertyData *targetProperty = resolver.property(property.toString());
@@ -1131,7 +1132,9 @@ QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolv
}
if (!targetProperty || targetProperty->coreIndex() > 0x0000FFFF) {
- *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString()));
+ *error = qQmlCompileError(
+ alias->referenceLocation,
+ tr("Invalid alias target location: %1").arg(property.toString()));
break;
}
@@ -1140,19 +1143,42 @@ QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolv
if (!subProperty.isEmpty()) {
const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(targetProperty->propType());
if (!valueTypeMetaObject) {
- *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString()));
- break;
- }
+ // could be a deep alias
+ bool isDeepAlias = subProperty.at(0).isLower();
+ if (isDeepAlias) {
+ isDeepAlias = false;
+ for (auto it = targetObject->bindingsBegin(); it != targetObject->bindingsEnd(); ++it) {
+ auto binding = *it;
+ if (compiler->stringAt(binding.propertyNameIndex) == property) {
+ resolver = QQmlPropertyResolver(propertyCaches.at(binding.value.objectIndex));
+ QQmlPropertyData *actualProperty = resolver.property(subProperty.toString());
+ if (actualProperty) {
+ propIdx = QQmlPropertyIndex(propIdx.coreIndex(), actualProperty->coreIndex());
+ isDeepAlias = true;
+ }
+ }
+ }
+ }
+ if (!isDeepAlias) {
+ *error = qQmlCompileError(
+ alias->referenceLocation,
+ tr("Invalid alias target location: %1").arg(subProperty.toString()));
+ break;
+ }
+ } else {
- int valueTypeIndex =
- valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData());
- if (valueTypeIndex == -1) {
- *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString()));
- break;
- }
- Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
+ int valueTypeIndex =
+ valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData());
+ if (valueTypeIndex == -1) {
+ *error = qQmlCompileError(
+ alias->referenceLocation,
+ tr("Invalid alias target location: %1").arg(subProperty.toString()));
+ break;
+ }
+ Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
- propIdx = QQmlPropertyIndex(propIdx.coreIndex(), valueTypeIndex);
+ propIdx = QQmlPropertyIndex(propIdx.coreIndex(), valueTypeIndex);
+ }
} else {
if (targetProperty->isQObject())
alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
@@ -1214,7 +1240,7 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex);
- QmlIR::PropertyResolver propertyResolver(propertyCache);
+ QQmlPropertyResolver propertyResolver(propertyCache);
QStringList deferredPropertyNames;
{
@@ -1253,7 +1279,8 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
continue;
bool notInRevision = false;
- pd = propertyResolver.property(name, &notInRevision, QmlIR::PropertyResolver::CheckRevision);
+ pd = propertyResolver.property(name, &notInRevision,
+ QQmlPropertyResolver::CheckRevision);
}
bool seenSubObjectWithId = false;
@@ -1289,78 +1316,6 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
return true;
}
-QQmlJSCodeGenerator::QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::JSCodeGen *v4CodeGen)
- : QQmlCompilePass(typeCompiler)
- , customParsers(typeCompiler->customParserCache())
- , qmlObjects(*typeCompiler->qmlObjects())
- , propertyCaches(typeCompiler->propertyCaches())
- , v4CodeGen(v4CodeGen)
-{
-}
-
-bool QQmlJSCodeGenerator::generateCodeForComponents()
-{
- const QVector<quint32> &componentRoots = compiler->componentRoots();
- for (int i = 0; i < componentRoots.count(); ++i) {
- if (!compileComponent(componentRoots.at(i)))
- return false;
- }
-
- return compileComponent(/*root object*/0);
-}
-
-bool QQmlJSCodeGenerator::compileComponent(int contextObject)
-{
- const QmlIR::Object *obj = qmlObjects.at(contextObject);
- if (obj->flags & QV4::CompiledData::Object::IsComponent) {
- Q_ASSERT(obj->bindingCount() == 1);
- const QV4::CompiledData::Binding *componentBinding = obj->firstBinding();
- Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
- contextObject = componentBinding->value.objectIndex;
- }
-
- if (!compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject))
- return false;
-
- return true;
-}
-
-bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex)
-{
- QmlIR::Object *object = qmlObjects.at(objectIndex);
- if (object->flags & QV4::CompiledData::Object::IsComponent)
- return true;
-
- if (object->functionsAndExpressions->count > 0) {
- QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
- for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next)
- functionsToCompile << *foe;
- const QVector<int> runtimeFunctionIndices = v4CodeGen->generateJSCodeForFunctionsAndBindings(functionsToCompile);
- const QList<QQmlError> jsErrors = v4CodeGen->qmlErrors();
- if (!jsErrors.isEmpty()) {
- for (const QQmlError &e : jsErrors)
- compiler->recordError(e);
- return false;
- }
-
- QQmlJS::MemoryPool *pool = compiler->memoryPool();
- object->runtimeFunctionIndices.allocate(pool, runtimeFunctionIndices);
- }
-
- for (const QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) {
- if (binding->type < QV4::CompiledData::Binding::Type_Object)
- continue;
-
- int target = binding->value.objectIndex;
- int scope = binding->type == QV4::CompiledData::Binding::Type_Object ? target : scopeObjectIndex;
-
- if (!compileJavaScriptCodeInObjectsRecursively(binding->value.objectIndex, scope))
- return false;
- }
-
- return true;
-}
-
QQmlDefaultPropertyMerger::QQmlDefaultPropertyMerger(QQmlTypeCompiler *typeCompiler)
: QQmlCompilePass(typeCompiler)
, qmlObjects(*typeCompiler->qmlObjects())
diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/qml/qqmltypecompiler_p.h
index a49b97453f..40b0337848 100644
--- a/src/qml/compiler/qqmltypecompiler_p.h
+++ b/src/qml/qml/qqmltypecompiler_p.h
@@ -81,7 +81,7 @@ struct QQmlTypeCompiler
public:
QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document,
const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
+ QV4::ResolvedTypeReferenceMap *resolvedTypeCache,
const QV4::CompiledData::DependentTypesHasher &dependencyHasher);
// --- interface used by QQmlPropertyCacheCreator
@@ -91,15 +91,14 @@ public:
QString stringAt(int idx) const;
QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsBegin(const QmlIR::Object *object) const { return object->functionsBegin(); }
QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsEnd(const QmlIR::Object *object) const { return object->functionsEnd(); }
- QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypes = nullptr;
+ QV4::ResolvedTypeReferenceMap *resolvedTypes = nullptr;
// ---
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile();
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> compile();
QList<QQmlError> compilationErrors() const { return errors; }
- void recordError(QQmlError error);
void recordError(const QV4::CompiledData::Location &location, const QString &description);
- void recordError(const QQmlCompileError &error);
+ void recordError(const QQmlJS::DiagnosticMessage &error);
int registerString(const QString &str);
int registerConstant(QV4::ReturnedValue v);
@@ -118,7 +117,6 @@ public:
QQmlJS::MemoryPool *memoryPool();
QStringRef newStringRef(const QString &string);
const QV4::Compiler::StringTableGenerator *stringPool() const;
- void setBindingPropertyDataPerObject(const QVector<QV4::CompiledData::BindingPropertyData> &propertyData);
const QHash<int, QQmlCustomParser*> &customParserCache() const { return customParsers; }
@@ -126,7 +124,7 @@ public:
void addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion);
- QV4::CompiledData::ResolvedTypeReference *resolvedType(int id) const
+ QV4::ResolvedTypeReference *resolvedType(int id) const
{
return resolvedTypes->value(id);
}
@@ -154,15 +152,15 @@ struct QQmlCompilePass
protected:
void recordError(const QV4::CompiledData::Location &location, const QString &description) const
{ compiler->recordError(location, description); }
- void recordError(const QQmlCompileError &error)
+ void recordError(const QQmlJS::DiagnosticMessage &error)
{ compiler->recordError(error); }
- QV4::CompiledData::ResolvedTypeReference *resolvedType(int id) const
+ QV4::ResolvedTypeReference *resolvedType(int id) const
{ return compiler->resolvedType(id); }
bool containsResolvedType(int id) const
{ return compiler->resolvedTypes->contains(id); }
- QV4::CompiledData::ResolvedTypeReferenceMap::iterator insertResolvedType(
- int id, QV4::CompiledData::ResolvedTypeReference *value)
+ QV4::ResolvedTypeReferenceMap::iterator insertResolvedType(
+ int id, QV4::ResolvedTypeReference *value)
{ return compiler->resolvedTypes->insert(id, value); }
QQmlTypeCompiler *compiler;
@@ -277,7 +275,7 @@ protected:
AllAliasesResolved
};
- AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlCompileError *error);
+ AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlJS::DiagnosticMessage *error);
QQmlEnginePrivate *enginePrivate;
QQmlJS::MemoryPool *pool;
@@ -311,24 +309,6 @@ private:
bool _seenObjectWithId;
};
-// ### merge with QtQml::JSCodeGen and operate directly on object->functionsAndExpressions once old compiler is gone.
-class QQmlJSCodeGenerator : public QQmlCompilePass
-{
-public:
- QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::JSCodeGen *v4CodeGen);
-
- bool generateCodeForComponents();
-
-private:
- bool compileComponent(int componentRoot);
- bool compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex);
-
- const QHash<int, QQmlCustomParser*> &customParsers;
- const QVector<QmlIR::Object*> &qmlObjects;
- const QQmlPropertyCacheVector * const propertyCaches;
- QmlIR::JSCodeGen * const v4CodeGen;
-};
-
class QQmlDefaultPropertyMerger : public QQmlCompilePass
{
public:
diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp
new file mode 100644
index 0000000000..0fd5bc83e6
--- /dev/null
+++ b/src/qml/qml/qqmltypedata.cpp
@@ -0,0 +1,842 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qqmltypedata_p.h>
+#include <private/qqmlengine_p.h>
+#include <private/qqmlpropertycachecreator_p.h>
+#include <private/qqmlpropertyvalidator_p.h>
+#include <private/qqmlirbuilder_p.h>
+#include <private/qqmlirloader_p.h>
+#include <private/qqmlscriptblob_p.h>
+#include <private/qqmlscriptdata_p.h>
+#include <private/qqmltypecompiler_p.h>
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qcryptographichash.h>
+
+Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE)
+
+QT_BEGIN_NAMESPACE
+
+QQmlTypeData::TypeDataCallback::~TypeDataCallback()
+{
+}
+
+QString QQmlTypeData::TypeReference::qualifiedName() const
+{
+ QString result;
+ if (!prefix.isEmpty()) {
+ result = prefix + QLatin1Char('.');
+ }
+ result.append(type.qmlTypeName());
+ return result;
+}
+
+QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager)
+ : QQmlTypeLoader::Blob(url, QmlFile, manager),
+ m_typesResolved(false), m_implicitImportLoaded(false)
+{
+
+}
+
+QQmlTypeData::~QQmlTypeData()
+{
+ m_scripts.clear();
+ m_compositeSingletons.clear();
+ m_resolvedTypes.clear();
+}
+
+const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const
+{
+ return m_scripts;
+}
+
+QV4::ExecutableCompilationUnit *QQmlTypeData::compilationUnit() const
+{
+ return m_compiledData.data();
+}
+
+void QQmlTypeData::registerCallback(TypeDataCallback *callback)
+{
+ Q_ASSERT(!m_callbacks.contains(callback));
+ m_callbacks.append(callback);
+}
+
+void QQmlTypeData::unregisterCallback(TypeDataCallback *callback)
+{
+ Q_ASSERT(m_callbacks.contains(callback));
+ m_callbacks.removeOne(callback);
+ Q_ASSERT(!m_callbacks.contains(callback));
+}
+
+bool QQmlTypeData::tryLoadFromDiskCache()
+{
+ if (!diskCacheEnabled())
+ return false;
+
+ QV4::ExecutionEngine *v4 = typeLoader()->engine()->handle();
+ if (!v4)
+ return false;
+
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> unit = QV4::ExecutableCompilationUnit::create();
+ {
+ QString error;
+ if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) {
+ qCDebug(DBG_DISK_CACHE) << "Error loading" << urlString() << "from disk cache:" << error;
+ return false;
+ }
+ }
+
+ if (unit->unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation) {
+ restoreIR(std::move(*unit));
+ return true;
+ }
+
+ m_compiledData = unit;
+
+ for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i)
+ m_typeReferences.collectFromObject(m_compiledData->objectAt(i));
+
+ m_importCache.setBaseUrl(finalUrl(), finalUrlString());
+
+ // For remote URLs, we don't delay the loading of the implicit import
+ // because the loading probably requires an asynchronous fetch of the
+ // qmldir (so we can't load it just in time).
+ if (!finalUrl().scheme().isEmpty()) {
+ QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
+ if (!QQmlImports::isLocal(qmldirUrl)) {
+ if (!loadImplicitImport())
+ return false;
+
+ // find the implicit import
+ for (quint32 i = 0, count = m_compiledData->importCount(); i < count; ++i) {
+ const QV4::CompiledData::Import *import = m_compiledData->importAt(i);
+ if (m_compiledData->stringAt(import->uriIndex) == QLatin1String(".")
+ && import->qualifierIndex == 0
+ && import->majorVersion == -1
+ && import->minorVersion == -1) {
+ QList<QQmlError> errors;
+ auto pendingImport = std::make_shared<PendingImport>(this, import);
+ if (!fetchQmldir(qmldirUrl, pendingImport, 1, &errors)) {
+ setError(errors);
+ return false;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ for (int i = 0, count = m_compiledData->importCount(); i < count; ++i) {
+ const QV4::CompiledData::Import *import = m_compiledData->importAt(i);
+ QList<QQmlError> errors;
+ if (!addImport(import, &errors)) {
+ Q_ASSERT(errors.size());
+ QQmlError error(errors.takeFirst());
+ error.setUrl(m_importCache.baseUrl());
+ error.setLine(import->location.line);
+ error.setColumn(import->location.column);
+ errors.prepend(error); // put it back on the list after filling out information.
+ setError(errors);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void QQmlTypeData::createTypeAndPropertyCaches(
+ const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
+ const QV4::ResolvedTypeReferenceMap &resolvedTypeCache)
+{
+ Q_ASSERT(m_compiledData);
+ m_compiledData->typeNameCache = typeNameCache;
+ m_compiledData->resolvedTypes = resolvedTypeCache;
+
+ QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
+
+ QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings;
+
+ {
+ QQmlPropertyCacheCreator<QV4::ExecutableCompilationUnit> propertyCacheCreator(
+ &m_compiledData->propertyCaches, &pendingGroupPropertyBindings, engine,
+ m_compiledData.data(), &m_importCache);
+ QQmlJS::DiagnosticMessage error = propertyCacheCreator.buildMetaObjects();
+ if (error.isValid()) {
+ setError(error);
+ return;
+ }
+ }
+
+ QQmlPropertyCacheAliasCreator<QV4::ExecutableCompilationUnit> aliasCreator(
+ &m_compiledData->propertyCaches, m_compiledData.data());
+ aliasCreator.appendAliasPropertiesToMetaObjects(engine);
+
+ pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_compiledData->propertyCaches);
+}
+
+static bool addTypeReferenceChecksumsToHash(const QList<QQmlTypeData::TypeReference> &typeRefs, QCryptographicHash *hash, QQmlEngine *engine)
+{
+ for (const auto &typeRef: typeRefs) {
+ if (typeRef.typeData) {
+ const auto unit = typeRef.typeData->compilationUnit()->unitData();
+ hash->addData(unit->md5Checksum, sizeof(unit->md5Checksum));
+ } else if (typeRef.type.isValid()) {
+ const auto propertyCache = QQmlEnginePrivate::get(engine)->cache(typeRef.type.metaObject());
+ bool ok = false;
+ hash->addData(propertyCache->checksum(&ok));
+ if (!ok)
+ return false;
+ }
+ }
+ return true;
+}
+
+void QQmlTypeData::done()
+{
+ auto cleanup = qScopeGuard([this]{
+ m_document.reset();
+ m_typeReferences.clear();
+ if (isError())
+ m_compiledData = nullptr;
+ });
+
+ if (isError())
+ return;
+
+ // Check all script dependencies for errors
+ for (int ii = 0; ii < m_scripts.count(); ++ii) {
+ const ScriptReference &script = m_scripts.at(ii);
+ Q_ASSERT(script.script->isCompleteOrError());
+ if (script.script->isError()) {
+ QList<QQmlError> errors = script.script->errors();
+ QQmlError error;
+ error.setUrl(url());
+ error.setLine(script.location.line);
+ error.setColumn(script.location.column);
+ error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString()));
+ errors.prepend(error);
+ setError(errors);
+ return;
+ }
+ }
+
+ // Check all type dependencies for errors
+ for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); it != end;
+ ++it) {
+ const TypeReference &type = *it;
+ Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
+ if (type.typeData && type.typeData->isError()) {
+ const QString typeName = stringAt(it.key());
+
+ QList<QQmlError> errors = type.typeData->errors();
+ QQmlError error;
+ error.setUrl(url());
+ error.setLine(type.location.line);
+ error.setColumn(type.location.column);
+ error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
+ errors.prepend(error);
+ setError(errors);
+ return;
+ }
+ }
+
+ // Check all composite singleton type dependencies for errors
+ for (int ii = 0; ii < m_compositeSingletons.count(); ++ii) {
+ const TypeReference &type = m_compositeSingletons.at(ii);
+ Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
+ if (type.typeData && type.typeData->isError()) {
+ QString typeName = type.type.qmlTypeName();
+
+ QList<QQmlError> errors = type.typeData->errors();
+ QQmlError error;
+ error.setUrl(url());
+ error.setLine(type.location.line);
+ error.setColumn(type.location.column);
+ error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
+ errors.prepend(error);
+ setError(errors);
+ return;
+ }
+ }
+
+ QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
+ QV4::ResolvedTypeReferenceMap resolvedTypeCache;
+ {
+ QQmlJS::DiagnosticMessage error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache);
+ if (error.isValid()) {
+ setError(error);
+ qDeleteAll(resolvedTypeCache);
+ return;
+ }
+ }
+
+ QQmlEngine *const engine = typeLoader()->engine();
+
+ const auto dependencyHasher = [engine, &resolvedTypeCache, this]() {
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ return (resolvedTypeCache.addToHash(&hash, engine)
+ && ::addTypeReferenceChecksumsToHash(m_compositeSingletons, &hash, engine))
+ ? hash.result()
+ : QByteArray();
+ };
+
+ // verify if any dependencies changed if we're using a cache
+ if (m_document.isNull() && !m_compiledData->verifyChecksum(dependencyHasher)) {
+ qCDebug(DBG_DISK_CACHE) << "Checksum mismatch for cached version of" << m_compiledData->fileName();
+ if (!loadFromSource())
+ return;
+ m_backupSourceCode = SourceCodeData();
+ m_compiledData = nullptr;
+ }
+
+ if (!m_document.isNull()) {
+ // Compile component
+ compile(typeNameCache, &resolvedTypeCache, dependencyHasher);
+ } else {
+ createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache);
+ }
+
+ if (isError())
+ return;
+
+ {
+ QQmlEnginePrivate *const enginePrivate = QQmlEnginePrivate::get(engine);
+ {
+ // Sanity check property bindings
+ QQmlPropertyValidator validator(enginePrivate, m_importCache, m_compiledData);
+ QVector<QQmlJS::DiagnosticMessage> errors = validator.validate();
+ if (!errors.isEmpty()) {
+ setError(errors);
+ return;
+ }
+ }
+
+ m_compiledData->finalizeCompositeType(enginePrivate);
+ }
+
+ {
+ QQmlType type = QQmlMetaType::qmlType(finalUrl(), true);
+ if (m_compiledData && m_compiledData->unitData()->flags & QV4::CompiledData::Unit::IsSingleton) {
+ if (!type.isValid()) {
+ QQmlError error;
+ error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent."));
+ setError(error);
+ return;
+ } else if (!type.isCompositeSingleton()) {
+ QQmlError error;
+ error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(type.qmlTypeName()));
+ setError(error);
+ return;
+ }
+ } else {
+ // If the type is CompositeSingleton but there was no pragma Singleton in the
+ // QML file, lets report an error.
+ if (type.isValid() && type.isCompositeSingleton()) {
+ QString typeName = type.qmlTypeName();
+ setError(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName));
+ return;
+ }
+ }
+ }
+
+ {
+ // Collect imported scripts
+ m_compiledData->dependentScripts.reserve(m_scripts.count());
+ for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) {
+ const QQmlTypeData::ScriptReference &script = m_scripts.at(scriptIndex);
+
+ QStringRef qualifier(&script.qualifier);
+ QString enclosingNamespace;
+
+ const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.'));
+ if (lastDotIndex != -1) {
+ enclosingNamespace = qualifier.left(lastDotIndex).toString();
+ qualifier = qualifier.mid(lastDotIndex+1);
+ }
+
+ m_compiledData->typeNameCache->add(qualifier.toString(), scriptIndex, enclosingNamespace);
+ QQmlRefPointer<QQmlScriptData> scriptData = script.script->scriptData();
+ m_compiledData->dependentScripts << scriptData;
+ }
+ }
+}
+
+void QQmlTypeData::completed()
+{
+ // Notify callbacks
+ while (!m_callbacks.isEmpty()) {
+ TypeDataCallback *callback = m_callbacks.takeFirst();
+ callback->typeDataReady(this);
+ }
+}
+
+bool QQmlTypeData::loadImplicitImport()
+{
+ m_implicitImportLoaded = true; // Even if we hit an error, count as loaded (we'd just keep hitting the error)
+
+ m_importCache.setBaseUrl(finalUrl(), finalUrlString());
+
+ QQmlImportDatabase *importDatabase = typeLoader()->importDatabase();
+ // For local urls, add an implicit import "." as most overridden lookup.
+ // This will also trigger the loading of the qmldir and the import of any native
+ // types from available plugins.
+ QList<QQmlError> implicitImportErrors;
+ m_importCache.addImplicitImport(importDatabase, &implicitImportErrors);
+
+ if (!implicitImportErrors.isEmpty()) {
+ setError(implicitImportErrors);
+ return false;
+ }
+
+ return true;
+}
+
+void QQmlTypeData::dataReceived(const SourceCodeData &data)
+{
+ m_backupSourceCode = data;
+
+ if (tryLoadFromDiskCache())
+ return;
+
+ if (isError())
+ return;
+
+ if (!m_backupSourceCode.exists() || m_backupSourceCode.isEmpty()) {
+ if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch)
+ setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile"));
+ else if (!m_backupSourceCode.exists())
+ setError(QQmlTypeLoader::tr("No such file or directory"));
+ else
+ setError(QQmlTypeLoader::tr("File is empty"));
+ return;
+ }
+
+ if (!loadFromSource())
+ return;
+
+ continueLoadFromIR();
+}
+
+void QQmlTypeData::initializeFromCachedUnit(const QV4::CompiledData::Unit *unit)
+{
+ m_document.reset(new QmlIR::Document(isDebugging()));
+ QQmlIRLoader loader(unit, m_document.data());
+ loader.load();
+ m_document->jsModule.fileName = urlString();
+ m_document->jsModule.finalUrl = finalUrlString();
+ m_document->javaScriptCompilationUnit = QV4::CompiledData::CompilationUnit(unit);
+ continueLoadFromIR();
+}
+
+bool QQmlTypeData::loadFromSource()
+{
+ m_document.reset(new QmlIR::Document(isDebugging()));
+ m_document->jsModule.sourceTimeStamp = m_backupSourceCode.sourceTimeStamp();
+ QQmlEngine *qmlEngine = typeLoader()->engine();
+ QmlIR::IRBuilder compiler(qmlEngine->handle()->illegalNames());
+
+ QString sourceError;
+ const QString source = m_backupSourceCode.readAll(&sourceError);
+ if (!sourceError.isEmpty()) {
+ setError(sourceError);
+ return false;
+ }
+
+ if (!compiler.generateFromQml(source, finalUrlString(), m_document.data())) {
+ QList<QQmlError> errors;
+ errors.reserve(compiler.errors.count());
+ for (const QQmlJS::DiagnosticMessage &msg : qAsConst(compiler.errors)) {
+ QQmlError e;
+ e.setUrl(url());
+ e.setLine(msg.line);
+ e.setColumn(msg.column);
+ e.setDescription(msg.message);
+ errors << e;
+ }
+ setError(errors);
+ return false;
+ }
+ return true;
+}
+
+void QQmlTypeData::restoreIR(QV4::CompiledData::CompilationUnit &&unit)
+{
+ m_document.reset(new QmlIR::Document(isDebugging()));
+ QQmlIRLoader loader(unit.unitData(), m_document.data());
+ loader.load();
+ m_document->jsModule.fileName = urlString();
+ m_document->jsModule.finalUrl = finalUrlString();
+ m_document->javaScriptCompilationUnit = std::move(unit);
+ continueLoadFromIR();
+}
+
+void QQmlTypeData::continueLoadFromIR()
+{
+ m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd());
+ m_importCache.setBaseUrl(finalUrl(), finalUrlString());
+
+ // For remote URLs, we don't delay the loading of the implicit import
+ // because the loading probably requires an asynchronous fetch of the
+ // qmldir (so we can't load it just in time).
+ if (!finalUrl().scheme().isEmpty()) {
+ QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
+ if (!QQmlImports::isLocal(qmldirUrl)) {
+ if (!loadImplicitImport())
+ return;
+ // This qmldir is for the implicit import
+ auto implicitImport = std::make_shared<PendingImport>();
+ implicitImport->uri = QLatin1String(".");
+ implicitImport->majorVersion = -1;
+ implicitImport->minorVersion = -1;
+ QList<QQmlError> errors;
+
+ if (!fetchQmldir(qmldirUrl, implicitImport, 1, &errors)) {
+ setError(errors);
+ return;
+ }
+ }
+ }
+
+ QList<QQmlError> errors;
+
+ for (const QV4::CompiledData::Import *import : qAsConst(m_document->imports)) {
+ if (!addImport(import, &errors)) {
+ Q_ASSERT(errors.size());
+ QQmlError error(errors.takeFirst());
+ error.setUrl(m_importCache.baseUrl());
+ error.setLine(import->location.line);
+ error.setColumn(import->location.column);
+ errors.prepend(error); // put it back on the list after filling out information.
+ setError(errors);
+ return;
+ }
+ }
+}
+
+void QQmlTypeData::allDependenciesDone()
+{
+ QQmlTypeLoader::Blob::allDependenciesDone();
+
+ if (!m_typesResolved) {
+ // Check that all imports were resolved
+ QList<QQmlError> errors;
+ auto it = m_unresolvedImports.constBegin(), end = m_unresolvedImports.constEnd();
+ for ( ; it != end; ++it) {
+ if ((*it)->priority == 0) {
+ // This import was not resolved
+ for (auto keyIt = m_unresolvedImports.constBegin(),
+ keyEnd = m_unresolvedImports.constEnd();
+ keyIt != keyEnd; ++keyIt) {
+ PendingImportPtr import = *keyIt;
+ QQmlError error;
+ error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(import->uri));
+ error.setUrl(m_importCache.baseUrl());
+ error.setLine(import->location.line);
+ error.setColumn(import->location.column);
+ errors.prepend(error);
+ }
+ }
+ }
+ if (errors.size()) {
+ setError(errors);
+ return;
+ }
+
+ resolveTypes();
+ m_typesResolved = true;
+ }
+}
+
+void QQmlTypeData::downloadProgressChanged(qreal p)
+{
+ for (int ii = 0; ii < m_callbacks.count(); ++ii) {
+ TypeDataCallback *callback = m_callbacks.at(ii);
+ callback->typeDataProgress(this, p);
+ }
+}
+
+QString QQmlTypeData::stringAt(int index) const
+{
+ if (m_compiledData)
+ return m_compiledData->stringAt(index);
+ return m_document->jsGenerator.stringTable.stringForIndex(index);
+}
+
+void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
+ QV4::ResolvedTypeReferenceMap *resolvedTypeCache,
+ const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
+{
+ Q_ASSERT(m_compiledData.isNull());
+
+ const bool typeRecompilation = m_document && m_document->javaScriptCompilationUnit.unitData()
+ && (m_document->javaScriptCompilationUnit.unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation);
+
+ QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine());
+ QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), typeNameCache, resolvedTypeCache, dependencyHasher);
+ m_compiledData = compiler.compile();
+ if (!m_compiledData) {
+ qDeleteAll(*resolvedTypeCache);
+ resolvedTypeCache->clear();
+ setError(compiler.compilationErrors());
+ return;
+ }
+
+ const bool trySaveToDisk = diskCacheEnabled() && !typeRecompilation;
+ if (trySaveToDisk) {
+ QString errorString;
+ if (m_compiledData->saveToDisk(url(), &errorString)) {
+ QString error;
+ if (!m_compiledData->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) {
+ // ignore error, keep using the in-memory compilation unit.
+ }
+ } else {
+ qCDebug(DBG_DISK_CACHE) << "Error saving cached version of" << m_compiledData->fileName() << "to disk:" << errorString;
+ }
+ }
+}
+
+void QQmlTypeData::resolveTypes()
+{
+ // Add any imported scripts to our resolved set
+ const auto resolvedScripts = m_importCache.resolvedScripts();
+ for (const QQmlImports::ScriptReference &script : resolvedScripts) {
+ QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(script.location);
+ addDependency(blob.data());
+
+ ScriptReference ref;
+ //ref.location = ...
+ if (!script.qualifier.isEmpty())
+ {
+ ref.qualifier = script.qualifier + QLatin1Char('.') + script.nameSpace;
+ // Add a reference to the enclosing namespace
+ m_namespaces.insert(script.qualifier);
+ } else {
+ ref.qualifier = script.nameSpace;
+ }
+
+ ref.script = blob;
+ m_scripts << ref;
+ }
+
+ // Lets handle resolved composite singleton types
+ const auto resolvedCompositeSingletons = m_importCache.resolvedCompositeSingletons();
+ for (const QQmlImports::CompositeSingletonReference &csRef : resolvedCompositeSingletons) {
+ TypeReference ref;
+ QString typeName;
+ if (!csRef.prefix.isEmpty()) {
+ typeName = csRef.prefix + QLatin1Char('.') + csRef.typeName;
+ // Add a reference to the enclosing namespace
+ m_namespaces.insert(csRef.prefix);
+ } else {
+ typeName = csRef.typeName;
+ }
+
+ int majorVersion = csRef.majorVersion > -1 ? csRef.majorVersion : -1;
+ int minorVersion = csRef.minorVersion > -1 ? csRef.minorVersion : -1;
+
+ if (!resolveType(typeName, majorVersion, minorVersion, ref, -1, -1, true,
+ QQmlType::CompositeSingletonType))
+ return;
+
+ if (ref.type.isCompositeSingleton()) {
+ ref.typeData = typeLoader()->getType(ref.type.sourceUrl());
+ if (ref.typeData->status() == QQmlDataBlob::ResolvingDependencies || m_waitingOnMe.contains(ref.typeData.data())) {
+ // TODO: give an error message? If so, we should record and show the path of the cycle.
+ continue;
+ }
+ addDependency(ref.typeData.data());
+ ref.prefix = csRef.prefix;
+
+ m_compositeSingletons << ref;
+ }
+ }
+
+ for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_typeReferences.constBegin(), end = m_typeReferences.constEnd();
+ unresolvedRef != end; ++unresolvedRef) {
+
+ TypeReference ref; // resolved reference
+
+ const bool reportErrors = unresolvedRef->errorWhenNotFound;
+
+ int majorVersion = -1;
+ int minorVersion = -1;
+
+ const QString name = stringAt(unresolvedRef.key());
+
+ if (!resolveType(name, majorVersion, minorVersion, ref, unresolvedRef->location.line,
+ unresolvedRef->location.column, reportErrors,
+ QQmlType::AnyRegistrationType) && reportErrors)
+ return;
+
+ if (ref.type.isComposite()) {
+ ref.typeData = typeLoader()->getType(ref.type.sourceUrl());
+ addDependency(ref.typeData.data());
+ }
+ ref.majorVersion = majorVersion;
+ ref.minorVersion = minorVersion;
+
+ ref.location.line = unresolvedRef->location.line;
+ ref.location.column = unresolvedRef->location.column;
+
+ ref.needsCreation = unresolvedRef->needsCreation;
+
+ m_resolvedTypes.insert(unresolvedRef.key(), ref);
+ }
+
+ // ### this allows enums to work without explicit import or instantiation of the type
+ if (!m_implicitImportLoaded)
+ loadImplicitImport();
+}
+
+QQmlJS::DiagnosticMessage QQmlTypeData::buildTypeResolutionCaches(
+ QQmlRefPointer<QQmlTypeNameCache> *typeNameCache,
+ QV4::ResolvedTypeReferenceMap *resolvedTypeCache
+ ) const
+{
+ typeNameCache->adopt(new QQmlTypeNameCache(m_importCache));
+
+ for (const QString &ns: m_namespaces)
+ (*typeNameCache)->add(ns);
+
+ // Add any Composite Singletons that were used to the import cache
+ for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons)
+ (*typeNameCache)->add(singleton.type.qmlTypeName(), singleton.type.sourceUrl(), singleton.prefix);
+
+ m_importCache.populateCache(typeNameCache->data());
+
+ QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
+
+ for (auto resolvedType = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); resolvedType != end; ++resolvedType) {
+ QScopedPointer<QV4::ResolvedTypeReference> ref(new QV4::ResolvedTypeReference);
+ QQmlType qmlType = resolvedType->type;
+ if (resolvedType->typeData) {
+ if (resolvedType->needsCreation && qmlType.isCompositeSingleton()) {
+ return qQmlCompileError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType.qmlTypeName()));
+ }
+ ref->compilationUnit = resolvedType->typeData->compilationUnit();
+ } else if (qmlType.isValid()) {
+ ref->type = qmlType;
+ Q_ASSERT(ref->type.isValid());
+
+ if (resolvedType->needsCreation && !ref->type.isCreatable()) {
+ QString reason = ref->type.noCreationReason();
+ if (reason.isEmpty())
+ reason = tr("Element is not creatable.");
+ return qQmlCompileError(resolvedType->location, reason);
+ }
+
+ if (ref->type.containsRevisionedAttributes()) {
+ ref->typePropertyCache = engine->cache(ref->type,
+ resolvedType->minorVersion);
+ }
+ }
+ ref->majorVersion = resolvedType->majorVersion;
+ ref->minorVersion = resolvedType->minorVersion;
+ ref->doDynamicTypeCheck();
+ resolvedTypeCache->insert(resolvedType.key(), ref.take());
+ }
+ QQmlJS::DiagnosticMessage noError;
+ return noError;
+}
+
+bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int &minorVersion,
+ TypeReference &ref, int lineNumber, int columnNumber,
+ bool reportErrors, QQmlType::RegistrationType registrationType)
+{
+ QQmlImportNamespace *typeNamespace = nullptr;
+ QList<QQmlError> errors;
+
+ bool typeFound = m_importCache.resolveType(typeName, &ref.type, &majorVersion, &minorVersion,
+ &typeNamespace, &errors, registrationType);
+ if (!typeNamespace && !typeFound && !m_implicitImportLoaded) {
+ // Lazy loading of implicit import
+ if (loadImplicitImport()) {
+ // Try again to find the type
+ errors.clear();
+ typeFound = m_importCache.resolveType(typeName, &ref.type, &majorVersion, &minorVersion,
+ &typeNamespace, &errors, registrationType);
+ } else {
+ return false; //loadImplicitImport() hit an error, and called setError already
+ }
+ }
+
+ if ((!typeFound || typeNamespace) && reportErrors) {
+ // Known to not be a type:
+ // - known to be a namespace (Namespace {})
+ // - type with unknown namespace (UnknownNamespace.SomeType {})
+ QQmlError error;
+ if (typeNamespace) {
+ error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(typeName));
+ } else {
+ if (errors.size()) {
+ error = errors.takeFirst();
+ } else {
+ // this should not be possible!
+ // Description should come from error provided by addImport() function.
+ error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database"));
+ }
+ error.setUrl(m_importCache.baseUrl());
+ error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(typeName).arg(error.description()));
+ }
+
+ if (lineNumber != -1)
+ error.setLine(lineNumber);
+ if (columnNumber != -1)
+ error.setColumn(columnNumber);
+
+ errors.prepend(error);
+ setError(errors);
+ return false;
+ }
+
+ return true;
+}
+
+void QQmlTypeData::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &/*nameSpace*/)
+{
+ ScriptReference ref;
+ ref.script = blob;
+ ref.location = location;
+ ref.qualifier = qualifier;
+
+ m_scripts << ref;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltypedata_p.h b/src/qml/qml/qqmltypedata_p.h
new file mode 100644
index 0000000000..e1d0c900ea
--- /dev/null
+++ b/src/qml/qml/qqmltypedata_p.h
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLTYPEDATA_P_H
+#define QQMLTYPEDATA_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/qqmltypeloader_p.h>
+#include <private/qv4executablecompilationunit_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlTypeData)
+public:
+ struct TypeReference
+ {
+ TypeReference() : majorVersion(0), minorVersion(0), needsCreation(true) {}
+
+ QV4::CompiledData::Location location;
+ QQmlType type;
+ int majorVersion;
+ int minorVersion;
+ QQmlRefPointer<QQmlTypeData> typeData;
+ QString prefix; // used by CompositeSingleton types
+ QString qualifiedName() const;
+ bool needsCreation;
+ };
+
+ struct ScriptReference
+ {
+ QV4::CompiledData::Location location;
+ QString qualifier;
+ QQmlRefPointer<QQmlScriptBlob> script;
+ };
+
+private:
+ friend class QQmlTypeLoader;
+
+ QQmlTypeData(const QUrl &, QQmlTypeLoader *);
+
+public:
+ ~QQmlTypeData() override;
+
+ const QList<ScriptReference> &resolvedScripts() const;
+
+ QV4::ExecutableCompilationUnit *compilationUnit() const;
+
+ // Used by QQmlComponent to get notifications
+ struct TypeDataCallback {
+ virtual ~TypeDataCallback();
+ virtual void typeDataProgress(QQmlTypeData *, qreal) {}
+ virtual void typeDataReady(QQmlTypeData *) {}
+ };
+ void registerCallback(TypeDataCallback *);
+ void unregisterCallback(TypeDataCallback *);
+
+protected:
+ void done() override;
+ void completed() override;
+ void dataReceived(const SourceCodeData &) override;
+ void initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) override;
+ void allDependenciesDone() override;
+ void downloadProgressChanged(qreal) override;
+
+ QString stringAt(int index) const override;
+
+private:
+ bool tryLoadFromDiskCache();
+ bool loadFromSource();
+ void restoreIR(QV4::CompiledData::CompilationUnit &&unit);
+ void continueLoadFromIR();
+ void resolveTypes();
+ QQmlJS::DiagnosticMessage buildTypeResolutionCaches(
+ QQmlRefPointer<QQmlTypeNameCache> *typeNameCache,
+ QV4::ResolvedTypeReferenceMap *resolvedTypeCache
+ ) const;
+ void compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
+ QV4::ResolvedTypeReferenceMap *resolvedTypeCache,
+ const QV4::CompiledData::DependentTypesHasher &dependencyHasher);
+ void createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
+ const QV4::ResolvedTypeReferenceMap &resolvedTypeCache);
+ bool resolveType(const QString &typeName, int &majorVersion, int &minorVersion,
+ TypeReference &ref, int lineNumber = -1, int columnNumber = -1,
+ bool reportErrors = true,
+ QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType);
+
+ void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override;
+
+
+ SourceCodeData m_backupSourceCode; // used when cache verification fails.
+ QScopedPointer<QmlIR::Document> m_document;
+ QV4::CompiledData::TypeReferenceMap m_typeReferences;
+
+ QList<ScriptReference> m_scripts;
+
+ QSet<QString> m_namespaces;
+ QList<TypeReference> m_compositeSingletons;
+
+ // map from name index to resolved type
+ // While this could be a hash, a map is chosen here to provide a stable
+ // order, which is used to calculating a check-sum on dependent meta-objects.
+ QMap<int, TypeReference> m_resolvedTypes;
+ bool m_typesResolved:1;
+
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> m_compiledData;
+
+ QList<TypeDataCallback *> m_callbacks;
+
+ bool m_implicitImportLoaded;
+ bool loadImplicitImport();
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLTYPEDATA_P_H
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 5d3b1cedc5..c6c3ee5523 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -37,77 +37,37 @@
**
****************************************************************************/
-#include "qqmltypeloader_p.h"
-#include "qqmlabstracturlinterceptor.h"
-#include "qqmlexpression_p.h"
-
-#include <private/qqmlengine_p.h>
-#include <private/qqmlglobal_p.h>
-#include <private/qqmlthread_p.h>
-#include <private/qv4codegen_p.h>
-#include <private/qqmlcomponent_p.h>
+#include <private/qqmltypeloader_p.h>
+
+#include <private/qqmldirdata_p.h>
#include <private/qqmlprofiler_p.h>
-#include <private/qqmlmemoryprofiler_p.h>
-#include <private/qqmltypecompiler_p.h>
-#include <private/qqmlpropertyvalidator_p.h>
-#include <private/qqmlpropertycachecreator_p.h>
-#include <private/qv4module_p.h>
+#include <private/qqmlscriptblob_p.h>
+#include <private/qqmltypedata_p.h>
+#include <private/qqmltypeloaderqmldircontent_p.h>
+#include <private/qqmltypeloaderthread_p.h>
+
+#include <QtQml/qqmlabstracturlinterceptor.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlextensioninterface.h>
+#include <QtQml/qqmlfile.h>
#include <QtCore/qdir.h>
+#include <QtCore/qdiriterator.h>
#include <QtCore/qfile.h>
-#include <QtCore/qdatetime.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qmutex.h>
#include <QtCore/qthread.h>
-#include <QtQml/qqmlfile.h>
-#include <QtCore/qdiriterator.h>
-#include <QtQml/qqmlcomponent.h>
-#include <QtCore/qwaitcondition.h>
-#include <QtCore/qloggingcategory.h>
-#include <QtQml/qqmlextensioninterface.h>
-#include <QtCore/qcryptographichash.h>
-#include <QtCore/qscopeguard.h>
#include <functional>
-#if defined (Q_OS_UNIX)
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#endif
-
-#if defined (QT_LINUXBASE)
-// LSB doesn't declare NAME_MAX. Use SYMLINK_MAX instead, which seems to
-// always be identical to NAME_MAX
-#ifndef NAME_MAX
-# define NAME_MAX _POSIX_SYMLINK_MAX
-#endif
-
-#endif
-
// #define DATABLOB_DEBUG
-
#ifdef DATABLOB_DEBUG
-
-#define ASSERT_MAINTHREAD() do { if (m_thread->isThisThread()) qFatal("QQmlTypeLoader: Caller not in main thread"); } while (false)
#define ASSERT_LOADTHREAD() do { if (!m_thread->isThisThread()) qFatal("QQmlTypeLoader: Caller not in load thread"); } while (false)
-#define ASSERT_CALLBACK() do { if (!m_typeLoader || !m_typeLoader->m_thread->isThisThread()) qFatal("QQmlDataBlob: An API call was made outside a callback"); } while (false)
-
#else
-
-#define ASSERT_MAINTHREAD()
#define ASSERT_LOADTHREAD()
-#define ASSERT_CALLBACK()
-
#endif
-DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS);
DEFINE_BOOL_CONFIG_OPTION(disableDiskCache, QML_DISABLE_DISK_CACHE);
DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE);
-Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE)
-Q_LOGGING_CATEGORY(DBG_DISK_CACHE, "qt.qml.diskcache")
-
QT_BEGIN_NAMESPACE
namespace {
@@ -121,830 +81,6 @@ namespace {
};
}
-#if QT_CONFIG(qml_network)
-// This is a lame object that we need to ensure that slots connected to
-// QNetworkReply get called in the correct thread (the loader thread).
-// As QQmlTypeLoader lives in the main thread, and we can't use
-// Qt::DirectConnection connections from a QNetworkReply (because then
-// sender() wont work), we need to insert this object in the middle.
-class QQmlTypeLoaderNetworkReplyProxy : public QObject
-{
- Q_OBJECT
-public:
- QQmlTypeLoaderNetworkReplyProxy(QQmlTypeLoader *l);
-
-public slots:
- void finished();
- void downloadProgress(qint64, qint64);
- void manualFinished(QNetworkReply*);
-
-private:
- QQmlTypeLoader *l;
-};
-#endif // qml_network
-
-class QQmlTypeLoaderThread : public QQmlThread
-{
- typedef QQmlTypeLoaderThread This;
-
-public:
- QQmlTypeLoaderThread(QQmlTypeLoader *loader);
-#if QT_CONFIG(qml_network)
- QNetworkAccessManager *networkAccessManager() const;
- QQmlTypeLoaderNetworkReplyProxy *networkReplyProxy() const;
-#endif // qml_network
- void load(QQmlDataBlob *b);
- void loadAsync(QQmlDataBlob *b);
- void loadWithStaticData(QQmlDataBlob *b, const QByteArray &);
- void loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &);
- void loadWithCachedUnit(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit);
- void loadWithCachedUnitAsync(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit);
- void callCompleted(QQmlDataBlob *b);
- void callDownloadProgressChanged(QQmlDataBlob *b, qreal p);
- void initializeEngine(QQmlExtensionInterface *, const char *);
-
-protected:
- void shutdownThread() override;
-
-private:
- void loadThread(QQmlDataBlob *b);
- void loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &);
- void loadWithCachedUnitThread(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit);
- void callCompletedMain(QQmlDataBlob *b);
- void callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p);
- void initializeEngineMain(QQmlExtensionInterface *iface, const char *uri);
-
- QQmlTypeLoader *m_loader;
-#if QT_CONFIG(qml_network)
- mutable QNetworkAccessManager *m_networkAccessManager;
- mutable QQmlTypeLoaderNetworkReplyProxy *m_networkReplyProxy;
-#endif // qml_network
-};
-
-#if QT_CONFIG(qml_network)
-QQmlTypeLoaderNetworkReplyProxy::QQmlTypeLoaderNetworkReplyProxy(QQmlTypeLoader *l)
-: l(l)
-{
-}
-
-void QQmlTypeLoaderNetworkReplyProxy::finished()
-{
- Q_ASSERT(sender());
- Q_ASSERT(qobject_cast<QNetworkReply *>(sender()));
- QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
- l->networkReplyFinished(reply);
-}
-
-void QQmlTypeLoaderNetworkReplyProxy::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
-{
- Q_ASSERT(sender());
- Q_ASSERT(qobject_cast<QNetworkReply *>(sender()));
- QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
- l->networkReplyProgress(reply, bytesReceived, bytesTotal);
-}
-
-// This function is for when you want to shortcut the signals and call directly
-void QQmlTypeLoaderNetworkReplyProxy::manualFinished(QNetworkReply *reply)
-{
- qint64 replySize = reply->size();
- l->networkReplyProgress(reply, replySize, replySize);
- l->networkReplyFinished(reply);
-}
-#endif // qml_network
-
-/*!
-\class QQmlDataBlob
-\brief The QQmlDataBlob encapsulates a data request that can be issued to a QQmlTypeLoader.
-\internal
-
-QQmlDataBlob's are loaded by a QQmlTypeLoader. The user creates the QQmlDataBlob
-and then calls QQmlTypeLoader::load() or QQmlTypeLoader::loadWithStaticData() to load it.
-The QQmlTypeLoader invokes callbacks on the QQmlDataBlob as data becomes available.
-*/
-
-/*!
-\enum QQmlDataBlob::Status
-
-This enum describes the status of the data blob.
-
-\list
-\li Null The blob has not yet been loaded by a QQmlTypeLoader
-\li Loading The blob is loading network data. The QQmlDataBlob::setData() callback has not yet been
-invoked or has not yet returned.
-\li WaitingForDependencies The blob is waiting for dependencies to be done before continueing. This status
-only occurs after the QQmlDataBlob::setData() callback has been made, and when the blob has outstanding
-dependencies.
-\li Complete The blob's data has been loaded and all dependencies are done.
-\li Error An error has been set on this blob.
-\endlist
-*/
-
-/*!
-\enum QQmlDataBlob::Type
-
-This enum describes the type of the data blob.
-
-\list
-\li QmlFile This is a QQmlTypeData
-\li JavaScriptFile This is a QQmlScriptData
-\li QmldirFile This is a QQmlQmldirData
-\endlist
-*/
-
-/*!
-Create a new QQmlDataBlob for \a url and of the provided \a type.
-*/
-QQmlDataBlob::QQmlDataBlob(const QUrl &url, Type type, QQmlTypeLoader *manager)
-: m_typeLoader(manager), m_type(type), m_url(url), m_finalUrl(url), m_redirectCount(0),
- m_inCallback(false), m_isDone(false)
-{
- //Set here because we need to get the engine from the manager
- if (m_typeLoader->engine() && m_typeLoader->engine()->urlInterceptor())
- m_url = m_typeLoader->engine()->urlInterceptor()->intercept(m_url,
- (QQmlAbstractUrlInterceptor::DataType)m_type);
-}
-
-/*! \internal */
-QQmlDataBlob::~QQmlDataBlob()
-{
- Q_ASSERT(m_waitingOnMe.isEmpty());
-
- cancelAllWaitingFor();
-}
-
-/*!
- Must be called before loading can occur.
-*/
-void QQmlDataBlob::startLoading()
-{
- Q_ASSERT(status() == QQmlDataBlob::Null);
- m_data.setStatus(QQmlDataBlob::Loading);
-}
-
-/*!
-Returns the type provided to the constructor.
-*/
-QQmlDataBlob::Type QQmlDataBlob::type() const
-{
- return m_type;
-}
-
-/*!
-Returns the blob's status.
-*/
-QQmlDataBlob::Status QQmlDataBlob::status() const
-{
- return m_data.status();
-}
-
-/*!
-Returns true if the status is Null.
-*/
-bool QQmlDataBlob::isNull() const
-{
- return status() == Null;
-}
-
-/*!
-Returns true if the status is Loading.
-*/
-bool QQmlDataBlob::isLoading() const
-{
- return status() == Loading;
-}
-
-/*!
-Returns true if the status is WaitingForDependencies.
-*/
-bool QQmlDataBlob::isWaiting() const
-{
- return status() == WaitingForDependencies ||
- status() == ResolvingDependencies;
-}
-
-/*!
-Returns true if the status is Complete.
-*/
-bool QQmlDataBlob::isComplete() const
-{
- return status() == Complete;
-}
-
-/*!
-Returns true if the status is Error.
-*/
-bool QQmlDataBlob::isError() const
-{
- return status() == Error;
-}
-
-/*!
-Returns true if the status is Complete or Error.
-*/
-bool QQmlDataBlob::isCompleteOrError() const
-{
- Status s = status();
- return s == Error || s == Complete;
-}
-
-/*!
-Returns the data download progress from 0 to 1.
-*/
-qreal QQmlDataBlob::progress() const
-{
- quint8 p = m_data.progress();
- if (p == 0xFF) return 1.;
- else return qreal(p) / qreal(0xFF);
-}
-
-/*!
-Returns the physical url of the data. Initially this is the same as
-finalUrl(), but if a URL interceptor is set, it will work on this URL
-and leave finalUrl() alone.
-
-\sa finalUrl()
-*/
-QUrl QQmlDataBlob::url() const
-{
- return m_url;
-}
-
-QString QQmlDataBlob::urlString() const
-{
- if (m_urlString.isEmpty())
- m_urlString = m_url.toString();
-
- return m_urlString;
-}
-
-/*!
-Returns the logical URL to be used for resolving further URLs referred to in
-the code.
-
-This is the blob url passed to the constructor. If a URL interceptor rewrites
-the URL, this one stays the same. If a network redirect happens while fetching
-the data, this url is updated to reflect the new location. Therefore, if both
-an interception and a redirection happen, the final url will indirectly
-incorporate the result of the interception, potentially breaking further
-lookups.
-
-\sa url()
-*/
-QUrl QQmlDataBlob::finalUrl() const
-{
- return m_finalUrl;
-}
-
-/*!
-Returns the finalUrl() as a string.
-*/
-QString QQmlDataBlob::finalUrlString() const
-{
- if (m_finalUrlString.isEmpty())
- m_finalUrlString = m_finalUrl.toString();
-
- return m_finalUrlString;
-}
-
-/*!
-Return the errors on this blob.
-
-May only be called from the load thread, or after the blob isCompleteOrError().
-*/
-QList<QQmlError> QQmlDataBlob::errors() const
-{
- Q_ASSERT(isCompleteOrError() || (m_typeLoader && m_typeLoader->m_thread->isThisThread()));
- return m_errors;
-}
-
-/*!
-Mark this blob as having \a errors.
-
-All outstanding dependencies will be cancelled. Requests to add new dependencies
-will be ignored. Entry into the Error state is irreversable.
-
-The setError() method may only be called from within a QQmlDataBlob callback.
-*/
-void QQmlDataBlob::setError(const QQmlError &errors)
-{
- ASSERT_CALLBACK();
-
- QList<QQmlError> l;
- l << errors;
- setError(l);
-}
-
-/*!
-\overload
-*/
-void QQmlDataBlob::setError(const QList<QQmlError> &errors)
-{
- ASSERT_CALLBACK();
-
- Q_ASSERT(status() != Error);
- Q_ASSERT(m_errors.isEmpty());
-
- m_errors = errors; // Must be set before the m_data fence
- m_data.setStatus(Error);
-
- if (dumpErrors()) {
- qWarning().nospace() << "Errors for " << urlString();
- for (int ii = 0; ii < errors.count(); ++ii)
- qWarning().nospace() << " " << qPrintable(errors.at(ii).toString());
- }
- cancelAllWaitingFor();
-
- if (!m_inCallback)
- tryDone();
-}
-
-void QQmlDataBlob::setError(const QQmlCompileError &error)
-{
- QQmlError e;
- e.setColumn(error.location.column);
- e.setLine(error.location.line);
- e.setDescription(error.description);
- e.setUrl(url());
- setError(e);
-}
-
-void QQmlDataBlob::setError(const QVector<QQmlCompileError> &errors)
-{
- QList<QQmlError> finalErrors;
- finalErrors.reserve(errors.count());
- for (const QQmlCompileError &error: errors) {
- QQmlError e;
- e.setColumn(error.location.column);
- e.setLine(error.location.line);
- e.setDescription(error.description);
- e.setUrl(url());
- finalErrors << e;
- }
- setError(finalErrors);
-}
-
-void QQmlDataBlob::setError(const QString &description)
-{
- QQmlError e;
- e.setDescription(description);
- e.setUrl(url());
- setError(e);
-}
-
-/*!
-Wait for \a blob to become complete or to error. If \a blob is already
-complete or in error, or this blob is already complete, this has no effect.
-
-The setError() method may only be called from within a QQmlDataBlob callback.
-*/
-void QQmlDataBlob::addDependency(QQmlDataBlob *blob)
-{
- ASSERT_CALLBACK();
-
- Q_ASSERT(status() != Null);
-
- if (!blob ||
- blob->status() == Error || blob->status() == Complete ||
- status() == Error || status() == Complete || m_isDone)
- return;
-
- for (auto existingDep: qAsConst(m_waitingFor))
- if (existingDep.data() == blob)
- return;
-
- m_data.setStatus(WaitingForDependencies);
-
- m_waitingFor.append(blob);
- blob->m_waitingOnMe.append(this);
-}
-
-/*!
-\fn void QQmlDataBlob::dataReceived(const Data &data)
-
-Invoked when data for the blob is received. Implementors should use this callback
-to determine a blob's dependencies. Within this callback you may call setError()
-or addDependency().
-*/
-
-/*!
-Invoked once data has either been received or a network error occurred, and all
-dependencies are complete.
-
-You can set an error in this method, but you cannot add new dependencies. Implementors
-should use this callback to finalize processing of data.
-
-The default implementation does nothing.
-
-XXX Rename processData() or some such to avoid confusion between done() (processing thread)
-and completed() (main thread)
-*/
-void QQmlDataBlob::done()
-{
-}
-
-#if QT_CONFIG(qml_network)
-/*!
-Invoked if there is a network error while fetching this blob.
-
-The default implementation sets an appropriate QQmlError.
-*/
-void QQmlDataBlob::networkError(QNetworkReply::NetworkError networkError)
-{
- Q_UNUSED(networkError);
-
- QQmlError error;
- error.setUrl(m_url);
-
- const char *errorString = nullptr;
- switch (networkError) {
- default:
- errorString = "Network error";
- break;
- case QNetworkReply::ConnectionRefusedError:
- errorString = "Connection refused";
- break;
- case QNetworkReply::RemoteHostClosedError:
- errorString = "Remote host closed the connection";
- break;
- case QNetworkReply::HostNotFoundError:
- errorString = "Host not found";
- break;
- case QNetworkReply::TimeoutError:
- errorString = "Timeout";
- break;
- case QNetworkReply::ProxyConnectionRefusedError:
- case QNetworkReply::ProxyConnectionClosedError:
- case QNetworkReply::ProxyNotFoundError:
- case QNetworkReply::ProxyTimeoutError:
- case QNetworkReply::ProxyAuthenticationRequiredError:
- case QNetworkReply::UnknownProxyError:
- errorString = "Proxy error";
- break;
- case QNetworkReply::ContentAccessDenied:
- errorString = "Access denied";
- break;
- case QNetworkReply::ContentNotFoundError:
- errorString = "File not found";
- break;
- case QNetworkReply::AuthenticationRequiredError:
- errorString = "Authentication required";
- break;
- };
-
- error.setDescription(QLatin1String(errorString));
-
- setError(error);
-}
-#endif // qml_network
-
-/*!
-Called if \a blob, which was previously waited for, has an error.
-
-The default implementation does nothing.
-*/
-void QQmlDataBlob::dependencyError(QQmlDataBlob *blob)
-{
- Q_UNUSED(blob);
-}
-
-/*!
-Called if \a blob, which was previously waited for, has completed.
-
-The default implementation does nothing.
-*/
-void QQmlDataBlob::dependencyComplete(QQmlDataBlob *blob)
-{
- Q_UNUSED(blob);
-}
-
-/*!
-Called when all blobs waited for have completed. This occurs regardless of
-whether they are in error, or complete state.
-
-The default implementation does nothing.
-*/
-void QQmlDataBlob::allDependenciesDone()
-{
- m_data.setStatus(QQmlDataBlob::ResolvingDependencies);
-}
-
-/*!
-Called when the download progress of this blob changes. \a progress goes
-from 0 to 1.
-
-This callback is only invoked if an asynchronous load for this blob is
-made. An asynchronous load is one in which the Asynchronous mode is
-specified explicitly, or one that is implicitly delayed due to a network
-operation.
-
-The default implementation does nothing.
-*/
-void QQmlDataBlob::downloadProgressChanged(qreal progress)
-{
- Q_UNUSED(progress);
-}
-
-/*!
-Invoked on the main thread sometime after done() was called on the load thread.
-
-You cannot modify the blobs state at all in this callback and cannot depend on the
-order or timeliness of these callbacks. Implementors should use this callback to notify
-dependencies on the main thread that the blob is done and not a lot else.
-
-This callback is only invoked if an asynchronous load for this blob is
-made. An asynchronous load is one in which the Asynchronous mode is
-specified explicitly, or one that is implicitly delayed due to a network
-operation.
-
-The default implementation does nothing.
-*/
-void QQmlDataBlob::completed()
-{
-}
-
-
-void QQmlDataBlob::tryDone()
-{
- if (status() != Loading && m_waitingFor.isEmpty() && !m_isDone) {
- m_isDone = true;
- addref();
-
-#ifdef DATABLOB_DEBUG
- qWarning("QQmlDataBlob::done() %s", qPrintable(urlString()));
-#endif
- done();
-
- if (status() != Error)
- m_data.setStatus(Complete);
-
- notifyAllWaitingOnMe();
-
- // Locking is not required here, as anyone expecting callbacks must
- // already be protected against the blob being completed (as set above);
-#ifdef DATABLOB_DEBUG
- qWarning("QQmlDataBlob: Dispatching completed");
-#endif
- m_typeLoader->m_thread->callCompleted(this);
-
- release();
- }
-}
-
-void QQmlDataBlob::cancelAllWaitingFor()
-{
- while (m_waitingFor.count()) {
- QQmlRefPointer<QQmlDataBlob> blob = m_waitingFor.takeLast();
-
- Q_ASSERT(blob->m_waitingOnMe.contains(this));
-
- blob->m_waitingOnMe.removeOne(this);
- }
-}
-
-void QQmlDataBlob::notifyAllWaitingOnMe()
-{
- while (m_waitingOnMe.count()) {
- QQmlDataBlob *blob = m_waitingOnMe.takeLast();
-
- Q_ASSERT(std::any_of(blob->m_waitingFor.constBegin(), blob->m_waitingFor.constEnd(),
- [this](const QQmlRefPointer<QQmlDataBlob> &waiting) { return waiting.data() == this; }));
-
- blob->notifyComplete(this);
- }
-}
-
-void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob)
-{
- Q_ASSERT(blob->status() == Error || blob->status() == Complete);
- QQmlCompilingProfiler prof(typeLoader()->profiler(), blob);
-
- m_inCallback = true;
-
- QQmlRefPointer<QQmlDataBlob> blobRef;
- for (int i = 0; i < m_waitingFor.count(); ++i) {
- if (m_waitingFor.at(i).data() == blob) {
- blobRef = m_waitingFor.takeAt(i);
- break;
- }
- }
- Q_ASSERT(blobRef);
-
- if (blob->status() == Error) {
- dependencyError(blob);
- } else if (blob->status() == Complete) {
- dependencyComplete(blob);
- }
-
- if (!isError() && m_waitingFor.isEmpty())
- allDependenciesDone();
-
- m_inCallback = false;
-
- tryDone();
-}
-
-#define TD_STATUS_MASK 0x0000FFFF
-#define TD_STATUS_SHIFT 0
-#define TD_PROGRESS_MASK 0x00FF0000
-#define TD_PROGRESS_SHIFT 16
-#define TD_ASYNC_MASK 0x80000000
-
-QQmlDataBlob::ThreadData::ThreadData()
-: _p(0)
-{
-}
-
-QQmlDataBlob::Status QQmlDataBlob::ThreadData::status() const
-{
- return QQmlDataBlob::Status((_p.load() & TD_STATUS_MASK) >> TD_STATUS_SHIFT);
-}
-
-void QQmlDataBlob::ThreadData::setStatus(QQmlDataBlob::Status status)
-{
- while (true) {
- int d = _p.load();
- int nd = (d & ~TD_STATUS_MASK) | ((status << TD_STATUS_SHIFT) & TD_STATUS_MASK);
- if (d == nd || _p.testAndSetOrdered(d, nd)) return;
- }
-}
-
-bool QQmlDataBlob::ThreadData::isAsync() const
-{
- return _p.load() & TD_ASYNC_MASK;
-}
-
-void QQmlDataBlob::ThreadData::setIsAsync(bool v)
-{
- while (true) {
- int d = _p.load();
- int nd = (d & ~TD_ASYNC_MASK) | (v?TD_ASYNC_MASK:0);
- if (d == nd || _p.testAndSetOrdered(d, nd)) return;
- }
-}
-
-quint8 QQmlDataBlob::ThreadData::progress() const
-{
- return quint8((_p.load() & TD_PROGRESS_MASK) >> TD_PROGRESS_SHIFT);
-}
-
-void QQmlDataBlob::ThreadData::setProgress(quint8 v)
-{
- while (true) {
- int d = _p.load();
- int nd = (d & ~TD_PROGRESS_MASK) | ((v << TD_PROGRESS_SHIFT) & TD_PROGRESS_MASK);
- if (d == nd || _p.testAndSetOrdered(d, nd)) return;
- }
-}
-
-QQmlTypeLoaderThread::QQmlTypeLoaderThread(QQmlTypeLoader *loader)
-: m_loader(loader)
-#if QT_CONFIG(qml_network)
-, m_networkAccessManager(nullptr), m_networkReplyProxy(nullptr)
-#endif // qml_network
-{
- // Do that after initializing all the members.
- startup();
-}
-
-#if QT_CONFIG(qml_network)
-QNetworkAccessManager *QQmlTypeLoaderThread::networkAccessManager() const
-{
- Q_ASSERT(isThisThread());
- if (!m_networkAccessManager) {
- m_networkAccessManager = QQmlEnginePrivate::get(m_loader->engine())->createNetworkAccessManager(nullptr);
- m_networkReplyProxy = new QQmlTypeLoaderNetworkReplyProxy(m_loader);
- }
-
- return m_networkAccessManager;
-}
-
-QQmlTypeLoaderNetworkReplyProxy *QQmlTypeLoaderThread::networkReplyProxy() const
-{
- Q_ASSERT(isThisThread());
- Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first
- return m_networkReplyProxy;
-}
-#endif // qml_network
-
-void QQmlTypeLoaderThread::load(QQmlDataBlob *b)
-{
- b->addref();
- callMethodInThread(&This::loadThread, b);
-}
-
-void QQmlTypeLoaderThread::loadAsync(QQmlDataBlob *b)
-{
- b->addref();
- postMethodToThread(&This::loadThread, b);
-}
-
-void QQmlTypeLoaderThread::loadWithStaticData(QQmlDataBlob *b, const QByteArray &d)
-{
- b->addref();
- callMethodInThread(&This::loadWithStaticDataThread, b, d);
-}
-
-void QQmlTypeLoaderThread::loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &d)
-{
- b->addref();
- postMethodToThread(&This::loadWithStaticDataThread, b, d);
-}
-
-void QQmlTypeLoaderThread::loadWithCachedUnit(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit)
-{
- b->addref();
- callMethodInThread(&This::loadWithCachedUnitThread, b, unit);
-}
-
-void QQmlTypeLoaderThread::loadWithCachedUnitAsync(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit)
-{
- b->addref();
- postMethodToThread(&This::loadWithCachedUnitThread, b, unit);
-}
-
-void QQmlTypeLoaderThread::callCompleted(QQmlDataBlob *b)
-{
- b->addref();
-#if !QT_CONFIG(thread)
- if (!isThisThread())
- postMethodToThread(&This::callCompletedMain, b);
-#else
- postMethodToMain(&This::callCompletedMain, b);
-#endif
-}
-
-void QQmlTypeLoaderThread::callDownloadProgressChanged(QQmlDataBlob *b, qreal p)
-{
- b->addref();
-#if !QT_CONFIG(thread)
- if (!isThisThread())
- postMethodToThread(&This::callDownloadProgressChangedMain, b, p);
-#else
- postMethodToMain(&This::callDownloadProgressChangedMain, b, p);
-#endif
-}
-
-void QQmlTypeLoaderThread::initializeEngine(QQmlExtensionInterface *iface,
- const char *uri)
-{
- callMethodInMain(&This::initializeEngineMain, iface, uri);
-}
-
-void QQmlTypeLoaderThread::shutdownThread()
-{
-#if QT_CONFIG(qml_network)
- delete m_networkAccessManager;
- m_networkAccessManager = nullptr;
- delete m_networkReplyProxy;
- m_networkReplyProxy = nullptr;
-#endif // qml_network
-}
-
-void QQmlTypeLoaderThread::loadThread(QQmlDataBlob *b)
-{
- m_loader->loadThread(b);
- b->release();
-}
-
-void QQmlTypeLoaderThread::loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &d)
-{
- m_loader->loadWithStaticDataThread(b, d);
- b->release();
-}
-
-void QQmlTypeLoaderThread::loadWithCachedUnitThread(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit)
-{
- m_loader->loadWithCachedUnitThread(b, unit);
- b->release();
-}
-
-void QQmlTypeLoaderThread::callCompletedMain(QQmlDataBlob *b)
-{
- QML_MEMORY_SCOPE_URL(b->url());
-#ifdef DATABLOB_DEBUG
- qWarning("QQmlTypeLoaderThread: %s completed() callback", qPrintable(b->urlString()));
-#endif
- b->completed();
- b->release();
-}
-
-void QQmlTypeLoaderThread::callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p)
-{
-#ifdef DATABLOB_DEBUG
- qWarning("QQmlTypeLoaderThread: %s downloadProgressChanged(%f) callback",
- qPrintable(b->urlString()), p);
-#endif
- b->downloadProgressChanged(p);
- b->release();
-}
-
-void QQmlTypeLoaderThread::initializeEngineMain(QQmlExtensionInterface *iface,
- const char *uri)
-{
- Q_ASSERT(m_loader->engine()->thread() == QThread::currentThread());
- iface->initializeEngine(m_loader->engine(), uri);
-}
-
/*!
\class QQmlTypeLoader
\brief The QQmlTypeLoader class abstracts loading files and their dependencies over the network.
@@ -1146,8 +282,6 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob)
return;
}
- QML_MEMORY_SCOPE_URL(blob->m_url);
-
if (QQmlFile::isSynchronous(blob->m_url)) {
const QString fileName = QQmlFile::urlToLocalFileOrQrc(blob->m_url);
if (!QQml_isFileCaseCorrect(fileName)) {
@@ -1277,7 +411,6 @@ void QQmlTypeLoader::initializeEngine(QQmlExtensionInterface *iface,
void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data)
{
- QML_MEMORY_SCOPE_URL(blob->url());
QQmlDataBlob::SourceCodeData d;
d.inlineSourceCode = QString::fromUtf8(data);
d.hasInlineSourceCode = true;
@@ -1286,7 +419,6 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data)
void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QString &fileName)
{
- QML_MEMORY_SCOPE_URL(blob->url());
QQmlDataBlob::SourceCodeData d;
d.fileInfo = QFileInfo(fileName);
setData(blob, d);
@@ -1294,7 +426,6 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QString &fileName)
void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::SourceCodeData &d)
{
- QML_MEMORY_SCOPE_URL(blob->url());
QQmlCompilingProfiler prof(profiler(), blob);
blob->m_inCallback = true;
@@ -1314,7 +445,6 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::SourceCodeD
void QQmlTypeLoader::setCachedUnit(QQmlDataBlob *blob, const QV4::CompiledData::Unit *unit)
{
- QML_MEMORY_SCOPE_URL(blob->url());
QQmlCompilingProfiler prof(profiler(), blob);
blob->m_inCallback = true;
@@ -1338,6 +468,16 @@ void QQmlTypeLoader::shutdownThread()
m_thread->shutdown();
}
+QQmlTypeLoader::Blob::PendingImport::PendingImport(QQmlTypeLoader::Blob *blob, const QV4::CompiledData::Import *import)
+{
+ type = static_cast<QV4::CompiledData::Import::ImportType>(quint32(import->type));
+ uri = blob->stringAt(import->uriIndex);
+ qualifier = blob->stringAt(import->qualifierIndex);
+ majorVersion = import->majorVersion;
+ minorVersion = import->minorVersion;
+ location = import->location;
+}
+
QQmlTypeLoader::Blob::Blob(const QUrl &url, QQmlDataBlob::Type type, QQmlTypeLoader *loader)
: QQmlDataBlob(url, type, loader), m_importCache(loader)
{
@@ -1347,11 +487,11 @@ QQmlTypeLoader::Blob::~Blob()
{
}
-bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, const QV4::CompiledData::Import *import, int priority, QList<QQmlError> *errors)
+bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, PendingImportPtr import, int priority, QList<QQmlError> *errors)
{
QQmlRefPointer<QQmlQmldirData> data = typeLoader()->getQmldir(url);
- data->setImport(this, import);
+ data->setImport(this, std::move(import));
data->setPriority(this, priority);
if (data->status() == Error) {
@@ -1367,26 +507,25 @@ bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, const QV4::CompiledData:
return true;
}
-bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors)
+bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, PendingImportPtr import, QList<QQmlError> *errors)
{
QString qmldirIdentifier = data->urlString();
QString qmldirUrl = qmldirIdentifier.left(qmldirIdentifier.lastIndexOf(QLatin1Char('/')) + 1);
typeLoader()->setQmldirContent(qmldirIdentifier, data->content());
- if (!m_importCache.updateQmldirContent(typeLoader()->importDatabase(), stringAt(import->uriIndex), stringAt(import->qualifierIndex), qmldirIdentifier, qmldirUrl, errors))
+ if (!m_importCache.updateQmldirContent(typeLoader()->importDatabase(), import->uri, import->qualifier, qmldirIdentifier, qmldirUrl, errors))
return false;
- QHash<const QV4::CompiledData::Import *, int>::iterator it = m_unresolvedImports.find(import);
- if (it != m_unresolvedImports.end()) {
- *it = data->priority(this);
- }
+ if (!loadImportDependencies(import, qmldirIdentifier, errors))
+ return false;
+
+ import->priority = data->priority(this);
// Release this reference at destruction
m_qmldirs << data;
- const QString &importQualifier = stringAt(import->qualifierIndex);
- if (!importQualifier.isEmpty()) {
+ if (!import->qualifier.isEmpty()) {
// Does this library contain any qualified scripts?
QUrl libraryUrl(qmldirUrl);
const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirIdentifier);
@@ -1396,7 +535,7 @@ bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &da
QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl);
addDependency(blob.data());
- scriptImported(blob, import->location, script.nameSpace, importQualifier);
+ scriptImported(blob, import->location, script.nameSpace, import->qualifier);
}
}
@@ -1405,36 +544,42 @@ bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &da
bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QList<QQmlError> *errors)
{
+ return addImport(std::make_shared<PendingImport>(this, import), errors);
+}
+
+bool QQmlTypeLoader::Blob::addImport(QQmlTypeLoader::Blob::PendingImportPtr import, QList<QQmlError> *errors)
+{
Q_ASSERT(errors);
QQmlImportDatabase *importDatabase = typeLoader()->importDatabase();
- const QString &importUri = stringAt(import->uriIndex);
- const QString &importQualifier = stringAt(import->qualifierIndex);
if (import->type == QV4::CompiledData::Import::ImportScript) {
- QUrl scriptUrl = finalUrl().resolved(QUrl(importUri));
+ QUrl scriptUrl = finalUrl().resolved(QUrl(import->uri));
QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl);
addDependency(blob.data());
- scriptImported(blob, import->location, importQualifier, QString());
+ scriptImported(blob, import->location, import->qualifier, QString());
} else if (import->type == QV4::CompiledData::Import::ImportLibrary) {
QString qmldirFilePath;
QString qmldirUrl;
- if (QQmlMetaType::isLockedModule(importUri, import->majorVersion)) {
+ if (QQmlMetaType::isLockedModule(import->uri, import->majorVersion)) {
//Locked modules are checked first, to save on filesystem checks
- if (!m_importCache.addLibraryImport(importDatabase, importUri, importQualifier, import->majorVersion,
+ if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion,
import->minorVersion, QString(), QString(), false, errors))
return false;
- } else if (m_importCache.locateQmldir(importDatabase, importUri, import->majorVersion, import->minorVersion,
+ } else if (m_importCache.locateQmldir(importDatabase, import->uri, import->majorVersion, import->minorVersion,
&qmldirFilePath, &qmldirUrl)) {
// This is a local library import
- if (!m_importCache.addLibraryImport(importDatabase, importUri, importQualifier, import->majorVersion,
+ if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion,
import->minorVersion, qmldirFilePath, qmldirUrl, false, errors))
return false;
- if (!importQualifier.isEmpty()) {
+ if (!loadImportDependencies(import, qmldirFilePath, errors))
+ return false;
+
+ if (!import->qualifier.isEmpty()) {
// Does this library contain any qualified scripts?
QUrl libraryUrl(qmldirUrl);
const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirFilePath);
@@ -1444,18 +589,18 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL
QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl);
addDependency(blob.data());
- scriptImported(blob, import->location, script.nameSpace, importQualifier);
+ scriptImported(blob, import->location, script.nameSpace, import->qualifier);
}
}
} else {
// Is this a module?
- if (QQmlMetaType::isAnyModule(importUri)) {
- if (!m_importCache.addLibraryImport(importDatabase, importUri, importQualifier, import->majorVersion,
+ if (QQmlMetaType::isAnyModule(import->uri)) {
+ if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion,
import->minorVersion, QString(), QString(), false, errors))
return false;
} else {
// We haven't yet resolved this import
- m_unresolvedImports.insert(import, 0);
+ m_unresolvedImports << import;
QQmlAbstractUrlInterceptor *interceptor = typeLoader()->engine()->urlInterceptor();
@@ -1466,13 +611,13 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL
: QQmlImportDatabase::Remote);
if (!remotePathList.isEmpty()) {
// Add this library and request the possible locations for it
- if (!m_importCache.addLibraryImport(importDatabase, importUri, importQualifier, import->majorVersion,
+ if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion,
import->minorVersion, QString(), QString(), true, errors))
return false;
// Probe for all possible locations
int priority = 0;
- const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(importUri, remotePathList, import->majorVersion, import->minorVersion);
+ const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(import->uri, remotePathList, import->majorVersion, import->minorVersion);
for (const QString &qmldirPath : qmlDirPaths) {
if (interceptor) {
QUrl url = interceptor->intercept(
@@ -1495,7 +640,7 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL
bool incomplete = false;
- QUrl importUrl(importUri);
+ QUrl importUrl(import->uri);
QString path = importUrl.path();
path.append(QLatin1String(path.endsWith(QLatin1Char('/')) ? "qmldir" : "/qmldir"));
importUrl.setPath(path);
@@ -1505,7 +650,7 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL
incomplete = true;
}
- if (!m_importCache.addFileImport(importDatabase, importUri, importQualifier, import->majorVersion,
+ if (!m_importCache.addFileImport(importDatabase, import->uri, import->qualifier, import->majorVersion,
import->minorVersion, incomplete, errors))
return false;
@@ -1523,7 +668,7 @@ void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob)
if (blob->type() == QQmlDataBlob::QmldirFile) {
QQmlQmldirData *data = static_cast<QQmlQmldirData *>(blob);
- const QV4::CompiledData::Import *import = data->import(this);
+ PendingImportPtr import = data->import(this);
QList<QQmlError> errors;
if (!qmldirDataAvailable(data, &errors)) {
@@ -1538,16 +683,34 @@ void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob)
}
}
+bool QQmlTypeLoader::Blob::loadImportDependencies(PendingImportPtr currentImport, const QString &qmldirUri, QList<QQmlError> *errors)
+{
+ const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirUri);
+ for (const QString &implicitImports: qmldir.imports()) {
+ auto dependencyImport = std::make_shared<PendingImport>();
+ dependencyImport->uri = implicitImports;
+ dependencyImport->qualifier = currentImport->qualifier;
+ dependencyImport->majorVersion = currentImport->majorVersion;
+ dependencyImport->minorVersion = currentImport->minorVersion;
+ if (!addImport(dependencyImport, errors))
+ return false;
+ }
+ return true;
+}
+
bool QQmlTypeLoader::Blob::isDebugging() const
{
return typeLoader()->engine()->handle()->debugger() != nullptr;
}
-bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &data, QList<QQmlError> *errors)
+bool QQmlTypeLoader::Blob::diskCacheEnabled() const
{
- bool resolve = true;
+ return (!disableDiskCache() || forceDiskCache()) && !isDebugging();
+}
- const QV4::CompiledData::Import *import = data->import(this);
+bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &data, QList<QQmlError> *errors)
+{
+ PendingImportPtr import = data->import(this);
data->setImport(this, nullptr);
int priority = data->priority(this);
@@ -1555,10 +718,7 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirDa
if (import) {
// Do we need to resolve this import?
- QHash<const QV4::CompiledData::Import *, int>::iterator it = m_unresolvedImports.find(import);
- if (it != m_unresolvedImports.end()) {
- resolve = (*it == 0) || (*it > priority);
- }
+ const bool resolve = (import->priority == 0) || (import->priority > priority);
if (resolve) {
// This is the (current) best resolution for this import
@@ -1566,8 +726,7 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirDa
return false;
}
- if (it != m_unresolvedImports.end())
- *it = priority;
+ import->priority = priority;
return true;
}
}
@@ -1575,63 +734,6 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirDa
return true;
}
-
-QQmlTypeLoaderQmldirContent::QQmlTypeLoaderQmldirContent()
-{
-}
-
-bool QQmlTypeLoaderQmldirContent::hasError() const
-{
- return m_parser.hasError();
-}
-
-QList<QQmlError> QQmlTypeLoaderQmldirContent::errors(const QString &uri) const
-{
- return m_parser.errors(uri);
-}
-
-QString QQmlTypeLoaderQmldirContent::typeNamespace() const
-{
- return m_parser.typeNamespace();
-}
-
-void QQmlTypeLoaderQmldirContent::setContent(const QString &location, const QString &content)
-{
- m_hasContent = true;
- m_location = location;
- m_parser.parse(content);
-}
-
-void QQmlTypeLoaderQmldirContent::setError(const QQmlError &error)
-{
- m_parser.setError(error);
-}
-
-QQmlDirComponents QQmlTypeLoaderQmldirContent::components() const
-{
- return m_parser.components();
-}
-
-QQmlDirScripts QQmlTypeLoaderQmldirContent::scripts() const
-{
- return m_parser.scripts();
-}
-
-QQmlDirPlugins QQmlTypeLoaderQmldirContent::plugins() const
-{
- return m_parser.plugins();
-}
-
-QString QQmlTypeLoaderQmldirContent::pluginLocation() const
-{
- return m_location;
-}
-
-bool QQmlTypeLoaderQmldirContent::designerSupported() const
-{
- return m_parser.designerSupported();
-}
-
/*!
Constructs a new type loader that uses the given \a engine.
*/
@@ -1785,17 +887,6 @@ QQmlRefPointer<QQmlQmldirData> QQmlTypeLoader::getQmldir(const QUrl &url)
return qmldirData;
}
-// #### Qt 6: Remove this function, it exists only for binary compatibility.
-/*!
- * \internal
- */
-bool QQmlEngine::addNamedBundle(const QString &name, const QString &fileName)
-{
- Q_UNUSED(name)
- Q_UNUSED(fileName)
- return false;
-}
-
/*!
Returns the absolute filename of path via a directory cache.
Returns a empty string if the path does not exist.
@@ -1868,8 +959,10 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path)
bool QQmlTypeLoader::fileExists(const QString &path, const QString &file)
{
- if (path.isEmpty())
+ const QChar nullChar(QChar::Null);
+ if (path.isEmpty() || path.contains(nullChar) || file.isEmpty() || file.contains(nullChar))
return false;
+
Q_ASSERT(path.endsWith(QLatin1Char('/')));
if (path.at(0) == QLatin1Char(':')) {
// qrc resource
@@ -2106,1215 +1199,4 @@ bool QQmlTypeLoader::isScriptLoaded(const QUrl &url) const
return m_scriptCache.contains(url);
}
-QQmlTypeData::TypeDataCallback::~TypeDataCallback()
-{
-}
-
-QString QQmlTypeData::TypeReference::qualifiedName() const
-{
- QString result;
- if (!prefix.isEmpty()) {
- result = prefix + QLatin1Char('.');
- }
- result.append(type.qmlTypeName());
- return result;
-}
-
-QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager)
-: QQmlTypeLoader::Blob(url, QmlFile, manager),
- m_typesResolved(false), m_implicitImportLoaded(false)
-{
-
-}
-
-QQmlTypeData::~QQmlTypeData()
-{
- m_scripts.clear();
- m_compositeSingletons.clear();
- m_resolvedTypes.clear();
-}
-
-const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const
-{
- return m_scripts;
-}
-
-QV4::CompiledData::CompilationUnit *QQmlTypeData::compilationUnit() const
-{
- return m_compiledData.data();
-}
-
-void QQmlTypeData::registerCallback(TypeDataCallback *callback)
-{
- Q_ASSERT(!m_callbacks.contains(callback));
- m_callbacks.append(callback);
-}
-
-void QQmlTypeData::unregisterCallback(TypeDataCallback *callback)
-{
- Q_ASSERT(m_callbacks.contains(callback));
- m_callbacks.removeOne(callback);
- Q_ASSERT(!m_callbacks.contains(callback));
-}
-
-bool QQmlTypeData::tryLoadFromDiskCache()
-{
- if (disableDiskCache() && !forceDiskCache())
- return false;
-
- if (isDebugging())
- return false;
-
- QV4::ExecutionEngine *v4 = typeLoader()->engine()->handle();
- if (!v4)
- return false;
-
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading();
- {
- QString error;
- if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) {
- qCDebug(DBG_DISK_CACHE) << "Error loading" << urlString() << "from disk cache:" << error;
- return false;
- }
- }
-
- if (unit->unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation) {
- restoreIR(unit);
- return true;
- }
-
- m_compiledData = unit;
-
- for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i)
- m_typeReferences.collectFromObject(m_compiledData->objectAt(i));
-
- m_importCache.setBaseUrl(finalUrl(), finalUrlString());
-
- // For remote URLs, we don't delay the loading of the implicit import
- // because the loading probably requires an asynchronous fetch of the
- // qmldir (so we can't load it just in time).
- if (!finalUrl().scheme().isEmpty()) {
- QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
- if (!QQmlImports::isLocal(qmldirUrl)) {
- if (!loadImplicitImport())
- return false;
-
- // find the implicit import
- for (quint32 i = 0, count = m_compiledData->importCount(); i < count; ++i) {
- const QV4::CompiledData::Import *import = m_compiledData->importAt(i);
- if (m_compiledData->stringAt(import->uriIndex) == QLatin1String(".")
- && import->qualifierIndex == 0
- && import->majorVersion == -1
- && import->minorVersion == -1) {
- QList<QQmlError> errors;
- if (!fetchQmldir(qmldirUrl, import, 1, &errors)) {
- setError(errors);
- return false;
- }
- break;
- }
- }
- }
- }
-
- for (int i = 0, count = m_compiledData->importCount(); i < count; ++i) {
- const QV4::CompiledData::Import *import = m_compiledData->importAt(i);
- QList<QQmlError> errors;
- if (!addImport(import, &errors)) {
- Q_ASSERT(errors.size());
- QQmlError error(errors.takeFirst());
- error.setUrl(m_importCache.baseUrl());
- error.setLine(import->location.line);
- error.setColumn(import->location.column);
- errors.prepend(error); // put it back on the list after filling out information.
- setError(errors);
- return false;
- }
- }
-
- return true;
-}
-
-void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache)
-{
- Q_ASSERT(m_compiledData);
- m_compiledData->typeNameCache = typeNameCache;
- m_compiledData->resolvedTypes = resolvedTypeCache;
-
- QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
-
- QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings;
-
- {
- QQmlPropertyCacheCreator<QV4::CompiledData::CompilationUnit> propertyCacheCreator(&m_compiledData->propertyCaches,
- &pendingGroupPropertyBindings,
- engine, m_compiledData.data(), &m_importCache);
- QQmlCompileError error = propertyCacheCreator.buildMetaObjects();
- if (error.isSet()) {
- setError(error);
- return;
- }
- }
-
- QQmlPropertyCacheAliasCreator<QV4::CompiledData::CompilationUnit> aliasCreator(&m_compiledData->propertyCaches, m_compiledData.data());
- aliasCreator.appendAliasPropertiesToMetaObjects();
-
- pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_compiledData->propertyCaches);
-}
-
-static bool addTypeReferenceChecksumsToHash(const QList<QQmlTypeData::TypeReference> &typeRefs, QCryptographicHash *hash, QQmlEngine *engine)
-{
- for (const auto &typeRef: typeRefs) {
- if (typeRef.typeData) {
- const auto unit = typeRef.typeData->compilationUnit()->unitData();
- hash->addData(unit->md5Checksum, sizeof(unit->md5Checksum));
- } else if (typeRef.type.isValid()) {
- const auto propertyCache = QQmlEnginePrivate::get(engine)->cache(typeRef.type.metaObject());
- bool ok = false;
- hash->addData(propertyCache->checksum(&ok));
- if (!ok)
- return false;
- }
- }
- return true;
-}
-
-void QQmlTypeData::done()
-{
- auto cleanup = qScopeGuard([this]{
- m_document.reset();
- m_typeReferences.clear();
- if (isError())
- m_compiledData = nullptr;
- });
-
- if (isError())
- return;
-
- // Check all script dependencies for errors
- for (int ii = 0; ii < m_scripts.count(); ++ii) {
- const ScriptReference &script = m_scripts.at(ii);
- Q_ASSERT(script.script->isCompleteOrError());
- if (script.script->isError()) {
- QList<QQmlError> errors = script.script->errors();
- QQmlError error;
- error.setUrl(url());
- error.setLine(script.location.line);
- error.setColumn(script.location.column);
- error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString()));
- errors.prepend(error);
- setError(errors);
- return;
- }
- }
-
- // Check all type dependencies for errors
- for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); it != end;
- ++it) {
- const TypeReference &type = *it;
- Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
- if (type.typeData && type.typeData->isError()) {
- const QString typeName = stringAt(it.key());
-
- QList<QQmlError> errors = type.typeData->errors();
- QQmlError error;
- error.setUrl(url());
- error.setLine(type.location.line);
- error.setColumn(type.location.column);
- error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
- errors.prepend(error);
- setError(errors);
- return;
- }
- }
-
- // Check all composite singleton type dependencies for errors
- for (int ii = 0; ii < m_compositeSingletons.count(); ++ii) {
- const TypeReference &type = m_compositeSingletons.at(ii);
- Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
- if (type.typeData && type.typeData->isError()) {
- QString typeName = type.type.qmlTypeName();
-
- QList<QQmlError> errors = type.typeData->errors();
- QQmlError error;
- error.setUrl(url());
- error.setLine(type.location.line);
- error.setColumn(type.location.column);
- error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
- errors.prepend(error);
- setError(errors);
- return;
- }
- }
-
- QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
- QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypeCache;
- {
- QQmlCompileError error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache);
- if (error.isSet()) {
- setError(error);
- return;
- }
- }
-
- QQmlEngine *const engine = typeLoader()->engine();
-
- const auto dependencyHasher = [engine, &resolvedTypeCache, this](QCryptographicHash *hash) {
- if (!resolvedTypeCache.addToHash(hash, engine))
- return false;
- return ::addTypeReferenceChecksumsToHash(m_compositeSingletons, hash, engine);
- };
-
- // verify if any dependencies changed if we're using a cache
- if (m_document.isNull() && !m_compiledData->verifyChecksum(dependencyHasher)) {
- qCDebug(DBG_DISK_CACHE) << "Checksum mismatch for cached version of" << m_compiledData->fileName();
- if (!loadFromSource())
- return;
- m_backupSourceCode = SourceCodeData();
- m_compiledData = nullptr;
- }
-
- if (!m_document.isNull()) {
- // Compile component
- compile(typeNameCache, &resolvedTypeCache, dependencyHasher);
- } else {
- createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache);
- }
-
- if (isError())
- return;
-
- {
- QQmlEnginePrivate *const enginePrivate = QQmlEnginePrivate::get(engine);
- {
- // Sanity check property bindings
- QQmlPropertyValidator validator(enginePrivate, m_importCache, m_compiledData);
- QVector<QQmlCompileError> errors = validator.validate();
- if (!errors.isEmpty()) {
- setError(errors);
- return;
- }
- }
-
- m_compiledData->finalizeCompositeType(enginePrivate);
- }
-
- {
- QQmlType type = QQmlMetaType::qmlType(finalUrl(), true);
- if (m_compiledData && m_compiledData->unitData()->flags & QV4::CompiledData::Unit::IsSingleton) {
- if (!type.isValid()) {
- QQmlError error;
- error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent."));
- setError(error);
- return;
- } else if (!type.isCompositeSingleton()) {
- QQmlError error;
- error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(type.qmlTypeName()));
- setError(error);
- return;
- }
- } else {
- // If the type is CompositeSingleton but there was no pragma Singleton in the
- // QML file, lets report an error.
- if (type.isValid() && type.isCompositeSingleton()) {
- QString typeName = type.qmlTypeName();
- setError(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName));
- return;
- }
- }
- }
-
- {
- // Collect imported scripts
- m_compiledData->dependentScripts.reserve(m_scripts.count());
- for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) {
- const QQmlTypeData::ScriptReference &script = m_scripts.at(scriptIndex);
-
- QStringRef qualifier(&script.qualifier);
- QString enclosingNamespace;
-
- const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.'));
- if (lastDotIndex != -1) {
- enclosingNamespace = qualifier.left(lastDotIndex).toString();
- qualifier = qualifier.mid(lastDotIndex+1);
- }
-
- m_compiledData->typeNameCache->add(qualifier.toString(), scriptIndex, enclosingNamespace);
- QQmlRefPointer<QQmlScriptData> scriptData = script.script->scriptData();
- m_compiledData->dependentScripts << scriptData;
- }
- }
-}
-
-void QQmlTypeData::completed()
-{
- // Notify callbacks
- while (!m_callbacks.isEmpty()) {
- TypeDataCallback *callback = m_callbacks.takeFirst();
- callback->typeDataReady(this);
- }
-}
-
-bool QQmlTypeData::loadImplicitImport()
-{
- m_implicitImportLoaded = true; // Even if we hit an error, count as loaded (we'd just keep hitting the error)
-
- m_importCache.setBaseUrl(finalUrl(), finalUrlString());
-
- QQmlImportDatabase *importDatabase = typeLoader()->importDatabase();
- // For local urls, add an implicit import "." as most overridden lookup.
- // This will also trigger the loading of the qmldir and the import of any native
- // types from available plugins.
- QList<QQmlError> implicitImportErrors;
- m_importCache.addImplicitImport(importDatabase, &implicitImportErrors);
-
- if (!implicitImportErrors.isEmpty()) {
- setError(implicitImportErrors);
- return false;
- }
-
- return true;
-}
-
-void QQmlTypeData::dataReceived(const SourceCodeData &data)
-{
- m_backupSourceCode = data;
-
- if (tryLoadFromDiskCache())
- return;
-
- if (isError())
- return;
-
- if (!m_backupSourceCode.exists() || m_backupSourceCode.isEmpty()) {
- if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch)
- setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile"));
- else if (!m_backupSourceCode.exists())
- setError(QQmlTypeLoader::tr("No such file or directory"));
- else
- setError(QQmlTypeLoader::tr("File is empty"));
- return;
- }
-
- if (!loadFromSource())
- return;
-
- continueLoadFromIR();
-}
-
-void QQmlTypeData::initializeFromCachedUnit(const QV4::CompiledData::Unit *unit)
-{
- m_document.reset(new QmlIR::Document(isDebugging()));
- QmlIR::IRLoader loader(unit, m_document.data());
- loader.load();
- m_document->jsModule.fileName = urlString();
- m_document->jsModule.finalUrl = finalUrlString();
- m_document->javaScriptCompilationUnit.adopt(new QV4::CompiledData::CompilationUnit(unit));
- continueLoadFromIR();
-}
-
-bool QQmlTypeData::loadFromSource()
-{
- m_document.reset(new QmlIR::Document(isDebugging()));
- m_document->jsModule.sourceTimeStamp = m_backupSourceCode.sourceTimeStamp();
- QQmlEngine *qmlEngine = typeLoader()->engine();
- QmlIR::IRBuilder compiler(qmlEngine->handle()->v8Engine->illegalNames());
-
- QString sourceError;
- const QString source = m_backupSourceCode.readAll(&sourceError);
- if (!sourceError.isEmpty()) {
- setError(sourceError);
- return false;
- }
-
- if (!compiler.generateFromQml(source, finalUrlString(), m_document.data())) {
- QList<QQmlError> errors;
- errors.reserve(compiler.errors.count());
- for (const QQmlJS::DiagnosticMessage &msg : qAsConst(compiler.errors)) {
- QQmlError e;
- e.setUrl(url());
- e.setLine(msg.loc.startLine);
- e.setColumn(msg.loc.startColumn);
- e.setDescription(msg.message);
- errors << e;
- }
- setError(errors);
- return false;
- }
- return true;
-}
-
-void QQmlTypeData::restoreIR(QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit)
-{
- m_document.reset(new QmlIR::Document(isDebugging()));
- QmlIR::IRLoader loader(unit->unitData(), m_document.data());
- loader.load();
- m_document->jsModule.fileName = urlString();
- m_document->jsModule.finalUrl = finalUrlString();
- m_document->javaScriptCompilationUnit = unit;
- continueLoadFromIR();
-}
-
-void QQmlTypeData::continueLoadFromIR()
-{
- m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd());
- m_importCache.setBaseUrl(finalUrl(), finalUrlString());
-
- // For remote URLs, we don't delay the loading of the implicit import
- // because the loading probably requires an asynchronous fetch of the
- // qmldir (so we can't load it just in time).
- if (!finalUrl().scheme().isEmpty()) {
- QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
- if (!QQmlImports::isLocal(qmldirUrl)) {
- if (!loadImplicitImport())
- return;
- // This qmldir is for the implicit import
- QQmlJS::MemoryPool *pool = m_document->jsParserEngine.pool();
- auto implicitImport = pool->New<QV4::CompiledData::Import>();
- implicitImport->uriIndex = m_document->registerString(QLatin1String("."));
- implicitImport->qualifierIndex = 0; // empty string
- implicitImport->majorVersion = -1;
- implicitImport->minorVersion = -1;
- QList<QQmlError> errors;
-
- if (!fetchQmldir(qmldirUrl, implicitImport, 1, &errors)) {
- setError(errors);
- return;
- }
- }
- }
-
- QList<QQmlError> errors;
-
- for (const QV4::CompiledData::Import *import : qAsConst(m_document->imports)) {
- if (!addImport(import, &errors)) {
- Q_ASSERT(errors.size());
- QQmlError error(errors.takeFirst());
- error.setUrl(m_importCache.baseUrl());
- error.setLine(import->location.line);
- error.setColumn(import->location.column);
- errors.prepend(error); // put it back on the list after filling out information.
- setError(errors);
- return;
- }
- }
-}
-
-void QQmlTypeData::allDependenciesDone()
-{
- QQmlTypeLoader::Blob::allDependenciesDone();
-
- if (!m_typesResolved) {
- // Check that all imports were resolved
- QList<QQmlError> errors;
- QHash<const QV4::CompiledData::Import *, int>::const_iterator it = m_unresolvedImports.constBegin(), end = m_unresolvedImports.constEnd();
- for ( ; it != end; ++it) {
- if (*it == 0) {
- // This import was not resolved
- for (auto keyIt = m_unresolvedImports.keyBegin(),
- keyEnd = m_unresolvedImports.keyEnd();
- keyIt != keyEnd; ++keyIt) {
- const QV4::CompiledData::Import *import = *keyIt;
- QQmlError error;
- error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(stringAt(import->uriIndex)));
- error.setUrl(m_importCache.baseUrl());
- error.setLine(import->location.line);
- error.setColumn(import->location.column);
- errors.prepend(error);
- }
- }
- }
- if (errors.size()) {
- setError(errors);
- return;
- }
-
- resolveTypes();
- m_typesResolved = true;
- }
-}
-
-void QQmlTypeData::downloadProgressChanged(qreal p)
-{
- for (int ii = 0; ii < m_callbacks.count(); ++ii) {
- TypeDataCallback *callback = m_callbacks.at(ii);
- callback->typeDataProgress(this, p);
- }
-}
-
-QString QQmlTypeData::stringAt(int index) const
-{
- if (m_compiledData)
- return m_compiledData->stringAt(index);
- return m_document->jsGenerator.stringTable.stringForIndex(index);
-}
-
-void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
- const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
-{
- Q_ASSERT(m_compiledData.isNull());
-
- const bool typeRecompilation = m_document && m_document->javaScriptCompilationUnit && m_document->javaScriptCompilationUnit->unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation;
-
- QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine());
- QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), typeNameCache, resolvedTypeCache, dependencyHasher);
- m_compiledData = compiler.compile();
- if (!m_compiledData) {
- setError(compiler.compilationErrors());
- return;
- }
-
- const bool trySaveToDisk = (!disableDiskCache() || forceDiskCache()) && !m_document->jsModule.debugMode && !typeRecompilation;
- if (trySaveToDisk) {
- QString errorString;
- if (m_compiledData->saveToDisk(url(), &errorString)) {
- QString error;
- if (!m_compiledData->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) {
- // ignore error, keep using the in-memory compilation unit.
- }
- } else {
- qCDebug(DBG_DISK_CACHE) << "Error saving cached version of" << m_compiledData->fileName() << "to disk:" << errorString;
- }
- }
-}
-
-void QQmlTypeData::resolveTypes()
-{
- // Add any imported scripts to our resolved set
- const auto resolvedScripts = m_importCache.resolvedScripts();
- for (const QQmlImports::ScriptReference &script : resolvedScripts) {
- QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(script.location);
- addDependency(blob.data());
-
- ScriptReference ref;
- //ref.location = ...
- if (!script.qualifier.isEmpty())
- {
- ref.qualifier = script.qualifier + QLatin1Char('.') + script.nameSpace;
- // Add a reference to the enclosing namespace
- m_namespaces.insert(script.qualifier);
- } else {
- ref.qualifier = script.nameSpace;
- }
-
- ref.script = blob;
- m_scripts << ref;
- }
-
- // Lets handle resolved composite singleton types
- const auto resolvedCompositeSingletons = m_importCache.resolvedCompositeSingletons();
- for (const QQmlImports::CompositeSingletonReference &csRef : resolvedCompositeSingletons) {
- TypeReference ref;
- QString typeName;
- if (!csRef.prefix.isEmpty()) {
- typeName = csRef.prefix + QLatin1Char('.') + csRef.typeName;
- // Add a reference to the enclosing namespace
- m_namespaces.insert(csRef.prefix);
- } else {
- typeName = csRef.typeName;
- }
-
- int majorVersion = csRef.majorVersion > -1 ? csRef.majorVersion : -1;
- int minorVersion = csRef.minorVersion > -1 ? csRef.minorVersion : -1;
-
- if (!resolveType(typeName, majorVersion, minorVersion, ref, -1, -1, true,
- QQmlType::CompositeSingletonType))
- return;
-
- if (ref.type.isCompositeSingleton()) {
- ref.typeData = typeLoader()->getType(ref.type.sourceUrl());
- if (ref.typeData->status() == QQmlDataBlob::ResolvingDependencies) {
- // TODO: give an error message? If so, we should record and show the path of the cycle.
- continue;
- }
- addDependency(ref.typeData.data());
- ref.prefix = csRef.prefix;
-
- m_compositeSingletons << ref;
- }
- }
-
- for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_typeReferences.constBegin(), end = m_typeReferences.constEnd();
- unresolvedRef != end; ++unresolvedRef) {
-
- TypeReference ref; // resolved reference
-
- const bool reportErrors = unresolvedRef->errorWhenNotFound;
-
- int majorVersion = -1;
- int minorVersion = -1;
-
- const QString name = stringAt(unresolvedRef.key());
-
- if (!resolveType(name, majorVersion, minorVersion, ref, unresolvedRef->location.line,
- unresolvedRef->location.column, reportErrors,
- QQmlType::AnyRegistrationType) && reportErrors)
- return;
-
- if (ref.type.isComposite()) {
- ref.typeData = typeLoader()->getType(ref.type.sourceUrl());
- addDependency(ref.typeData.data());
- }
- ref.majorVersion = majorVersion;
- ref.minorVersion = minorVersion;
-
- ref.location.line = unresolvedRef->location.line;
- ref.location.column = unresolvedRef->location.column;
-
- ref.needsCreation = unresolvedRef->needsCreation;
-
- m_resolvedTypes.insert(unresolvedRef.key(), ref);
- }
-
- // ### this allows enums to work without explicit import or instantiation of the type
- if (!m_implicitImportLoaded)
- loadImplicitImport();
-}
-
-QQmlCompileError QQmlTypeData::buildTypeResolutionCaches(
- QQmlRefPointer<QQmlTypeNameCache> *typeNameCache,
- QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache
- ) const
-{
- typeNameCache->adopt(new QQmlTypeNameCache(m_importCache));
-
- for (const QString &ns: m_namespaces)
- (*typeNameCache)->add(ns);
-
- // Add any Composite Singletons that were used to the import cache
- for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons)
- (*typeNameCache)->add(singleton.type.qmlTypeName(), singleton.type.sourceUrl(), singleton.prefix);
-
- m_importCache.populateCache(typeNameCache->data());
-
- QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
-
- for (auto resolvedType = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); resolvedType != end; ++resolvedType) {
- QScopedPointer<QV4::CompiledData::ResolvedTypeReference> ref(new QV4::CompiledData::ResolvedTypeReference);
- QQmlType qmlType = resolvedType->type;
- if (resolvedType->typeData) {
- if (resolvedType->needsCreation && qmlType.isCompositeSingleton()) {
- return QQmlCompileError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType.qmlTypeName()));
- }
- ref->compilationUnit = resolvedType->typeData->compilationUnit();
- } else if (qmlType.isValid()) {
- ref->type = qmlType;
- Q_ASSERT(ref->type.isValid());
-
- if (resolvedType->needsCreation && !ref->type.isCreatable()) {
- QString reason = ref->type.noCreationReason();
- if (reason.isEmpty())
- reason = tr("Element is not creatable.");
- return QQmlCompileError(resolvedType->location, reason);
- }
-
- if (ref->type.containsRevisionedAttributes()) {
- ref->typePropertyCache = engine->cache(ref->type,
- resolvedType->minorVersion);
- }
- }
- ref->majorVersion = resolvedType->majorVersion;
- ref->minorVersion = resolvedType->minorVersion;
- ref->doDynamicTypeCheck();
- resolvedTypeCache->insert(resolvedType.key(), ref.take());
- }
- QQmlCompileError noError;
- return noError;
-}
-
-bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int &minorVersion,
- TypeReference &ref, int lineNumber, int columnNumber,
- bool reportErrors, QQmlType::RegistrationType registrationType)
-{
- QQmlImportNamespace *typeNamespace = nullptr;
- QList<QQmlError> errors;
-
- bool typeFound = m_importCache.resolveType(typeName, &ref.type, &majorVersion, &minorVersion,
- &typeNamespace, &errors, registrationType);
- if (!typeNamespace && !typeFound && !m_implicitImportLoaded) {
- // Lazy loading of implicit import
- if (loadImplicitImport()) {
- // Try again to find the type
- errors.clear();
- typeFound = m_importCache.resolveType(typeName, &ref.type, &majorVersion, &minorVersion,
- &typeNamespace, &errors, registrationType);
- } else {
- return false; //loadImplicitImport() hit an error, and called setError already
- }
- }
-
- if ((!typeFound || typeNamespace) && reportErrors) {
- // Known to not be a type:
- // - known to be a namespace (Namespace {})
- // - type with unknown namespace (UnknownNamespace.SomeType {})
- QQmlError error;
- if (typeNamespace) {
- error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(typeName));
- } else {
- if (errors.size()) {
- error = errors.takeFirst();
- } else {
- // this should not be possible!
- // Description should come from error provided by addImport() function.
- error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database"));
- }
- error.setUrl(m_importCache.baseUrl());
- error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(typeName).arg(error.description()));
- }
-
- if (lineNumber != -1)
- error.setLine(lineNumber);
- if (columnNumber != -1)
- error.setColumn(columnNumber);
-
- errors.prepend(error);
- setError(errors);
- return false;
- }
-
- return true;
-}
-
-void QQmlTypeData::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &/*nameSpace*/)
-{
- ScriptReference ref;
- ref.script = blob;
- ref.location = location;
- ref.qualifier = qualifier;
-
- m_scripts << ref;
-}
-
-QQmlScriptData::QQmlScriptData()
- : typeNameCache(nullptr)
- , m_loaded(false)
-{
-}
-
-QQmlContextData *QQmlScriptData::qmlContextDataForContext(QQmlContextData *parentQmlContextData)
-{
- Q_ASSERT(parentQmlContextData && parentQmlContextData->engine);
-
- if (m_precompiledScript->isESModule())
- return nullptr;
-
- auto qmlContextData = new QQmlContextData();
-
- qmlContextData->isInternal = true;
- qmlContextData->isJSContext = true;
- if (m_precompiledScript->isSharedLibrary())
- qmlContextData->isPragmaLibraryContext = true;
- else
- qmlContextData->isPragmaLibraryContext = parentQmlContextData->isPragmaLibraryContext;
- qmlContextData->baseUrl = url;
- qmlContextData->baseUrlString = urlString;
-
- // For backward compatibility, if there are no imports, we need to use the
- // imports from the parent context. See QTBUG-17518.
- if (!typeNameCache->isEmpty()) {
- qmlContextData->imports = typeNameCache;
- } else if (!m_precompiledScript->isSharedLibrary()) {
- qmlContextData->imports = parentQmlContextData->imports;
- qmlContextData->importedScripts = parentQmlContextData->importedScripts;
- }
-
- if (!m_precompiledScript->isSharedLibrary()) {
- qmlContextData->setParent(parentQmlContextData);
- } else {
- qmlContextData->engine = parentQmlContextData->engine; // Fix for QTBUG-21620
- }
-
- QV4::ExecutionEngine *v4 = parentQmlContextData->engine->handle();
- QV4::Scope scope(v4);
- QV4::ScopedObject scriptsArray(scope);
- if (qmlContextData->importedScripts.isNullOrUndefined()) {
- scriptsArray = v4->newArrayObject(scripts.count());
- qmlContextData->importedScripts.set(v4, scriptsArray);
- } else {
- scriptsArray = qmlContextData->importedScripts.valueRef();
- }
- QV4::ScopedValue v(scope);
- for (int ii = 0; ii < scripts.count(); ++ii)
- scriptsArray->put(ii, (v = scripts.at(ii)->scriptData()->scriptValueForContext(qmlContextData)));
-
- return qmlContextData;
-}
-
-QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parentQmlContextData)
-{
- if (m_loaded)
- return m_value.value();
-
- Q_ASSERT(parentQmlContextData && parentQmlContextData->engine);
- QV4::ExecutionEngine *v4 = parentQmlContextData->engine->handle();
- QV4::Scope scope(v4);
-
- if (!hasEngine()) {
- addToEngine(parentQmlContextData->engine);
- addref();
- }
-
- QQmlContextDataRef qmlContextData = qmlContextDataForContext(parentQmlContextData);
- QV4::Scoped<QV4::QmlContext> qmlExecutionContext(scope);
- if (qmlContextData)
- qmlExecutionContext =
- QV4::QmlContext::create(v4->rootContext(), qmlContextData, /* scopeObject: */ nullptr);
-
- QV4::Scoped<QV4::Module> module(scope, m_precompiledScript->instantiate(v4));
- if (module) {
- if (qmlContextData) {
- module->d()->scope->outer.set(v4, qmlExecutionContext->d());
- qmlExecutionContext->d()->qml()->module.set(v4, module->d());
- }
-
- module->evaluate();
- }
-
- if (v4->hasException) {
- QQmlError error = v4->catchExceptionAsQmlError();
- if (error.isValid())
- QQmlEnginePrivate::get(v4)->warning(error);
- }
-
- QV4::ScopedValue value(scope);
- if (qmlContextData)
- value = qmlExecutionContext->d()->qml();
- else if (module)
- value = module->d();
-
- if (m_precompiledScript->isSharedLibrary() || m_precompiledScript->isESModule()) {
- m_loaded = true;
- m_value.set(v4, value);
- }
-
- return value->asReturnedValue();
-}
-
-void QQmlScriptData::clear()
-{
- if (typeNameCache) {
- typeNameCache->release();
- typeNameCache = nullptr;
- }
-
- scripts.clear();
-
- // An addref() was made when the QQmlCleanup was added to the engine.
- release();
-}
-
-QQmlScriptBlob::QQmlScriptBlob(const QUrl &url, QQmlTypeLoader *loader)
- : QQmlTypeLoader::Blob(url, JavaScriptFile, loader)
- , m_isModule(url.path().endsWith(QLatin1String(".mjs")))
-{
-}
-
-QQmlScriptBlob::~QQmlScriptBlob()
-{
-}
-
-QQmlRefPointer<QQmlScriptData> QQmlScriptBlob::scriptData() const
-{
- return m_scriptData;
-}
-
-void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
-{
- if (!disableDiskCache() || forceDiskCache()) {
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading();
- QString error;
- if (unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) {
- initializeFromCompilationUnit(unit);
- return;
- } else {
- qCDebug(DBG_DISK_CACHE()) << "Error loading" << urlString() << "from disk cache:" << error;
- }
- }
-
- if (!data.exists()) {
- if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch)
- setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile"));
- else
- setError(QQmlTypeLoader::tr("No such file or directory"));
- return;
- }
-
- QString error;
- QString source = data.readAll(&error);
- if (!error.isEmpty()) {
- setError(error);
- return;
- }
-
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit;
-
- if (m_isModule) {
- QList<QQmlJS::DiagnosticMessage> diagnostics;
- unit = QV4::ExecutionEngine::compileModule(isDebugging(), urlString(), source, data.sourceTimeStamp(), &diagnostics);
- QList<QQmlError> errors = QQmlEnginePrivate::qmlErrorFromDiagnostics(urlString(), diagnostics);
- if (!errors.isEmpty()) {
- setError(errors);
- return;
- }
- } else {
- QmlIR::Document irUnit(isDebugging());
-
- irUnit.jsModule.sourceTimeStamp = data.sourceTimeStamp();
-
- QmlIR::ScriptDirectivesCollector collector(&irUnit);
- irUnit.jsParserEngine.setDirectives(&collector);
-
- QList<QQmlError> errors;
- unit = QV4::Script::precompile(
- &irUnit.jsModule, &irUnit.jsParserEngine, &irUnit.jsGenerator, urlString(), finalUrlString(),
- source, &errors, QV4::Compiler::ContextType::ScriptImportedByQML);
- // No need to addref on unit, it's initial refcount is 1
- source.clear();
- if (!errors.isEmpty()) {
- setError(errors);
- return;
- }
- if (!unit) {
- unit.adopt(new QV4::CompiledData::CompilationUnit);
- }
- irUnit.javaScriptCompilationUnit = unit;
-
- QmlIR::QmlUnitGenerator qmlGenerator;
- qmlGenerator.generate(irUnit);
- }
-
- if ((!disableDiskCache() || forceDiskCache()) && !isDebugging()) {
- QString errorString;
- if (unit->saveToDisk(url(), &errorString)) {
- QString error;
- if (!unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) {
- // ignore error, keep using the in-memory compilation unit.
- }
- } else {
- qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of" << unit->fileName() << "to disk:" << errorString;
- }
- }
-
- initializeFromCompilationUnit(unit);
-}
-
-void QQmlScriptBlob::initializeFromCachedUnit(const QV4::CompiledData::Unit *unit)
-{
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
- compilationUnit.adopt(new QV4::CompiledData::CompilationUnit(unit, urlString(), finalUrlString()));
- initializeFromCompilationUnit(compilationUnit);
-}
-
-void QQmlScriptBlob::done()
-{
- if (isError())
- return;
-
- // Check all script dependencies for errors
- for (int ii = 0; ii < m_scripts.count(); ++ii) {
- const ScriptReference &script = m_scripts.at(ii);
- Q_ASSERT(script.script->isCompleteOrError());
- if (script.script->isError()) {
- QList<QQmlError> errors = script.script->errors();
- QQmlError error;
- error.setUrl(url());
- error.setLine(script.location.line);
- error.setColumn(script.location.column);
- error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString()));
- errors.prepend(error);
- setError(errors);
- return;
- }
- }
-
- if (!m_isModule) {
- m_scriptData->typeNameCache = new QQmlTypeNameCache(m_importCache);
-
- QSet<QString> ns;
-
- for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) {
- const ScriptReference &script = m_scripts.at(scriptIndex);
-
- m_scriptData->scripts.append(script.script);
-
- if (!script.nameSpace.isNull()) {
- if (!ns.contains(script.nameSpace)) {
- ns.insert(script.nameSpace);
- m_scriptData->typeNameCache->add(script.nameSpace);
- }
- }
- m_scriptData->typeNameCache->add(script.qualifier, scriptIndex, script.nameSpace);
- }
-
- m_importCache.populateCache(m_scriptData->typeNameCache);
- }
- m_scripts.clear();
-}
-
-QString QQmlScriptBlob::stringAt(int index) const
-{
- return m_scriptData->m_precompiledScript->stringAt(index);
-}
-
-void QQmlScriptBlob::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace)
-{
- ScriptReference ref;
- ref.script = blob;
- ref.location = location;
- ref.qualifier = qualifier;
- ref.nameSpace = nameSpace;
-
- m_scripts << ref;
-}
-
-void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit)
-{
- Q_ASSERT(!m_scriptData);
- m_scriptData.adopt(new QQmlScriptData());
- m_scriptData->url = finalUrl();
- m_scriptData->urlString = finalUrlString();
- m_scriptData->m_precompiledScript = unit;
-
- m_importCache.setBaseUrl(finalUrl(), finalUrlString());
-
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> script = m_scriptData->m_precompiledScript;
-
- if (!m_isModule) {
- QList<QQmlError> errors;
- for (quint32 i = 0, count = script->importCount(); i < count; ++i) {
- const QV4::CompiledData::Import *import = script->importAt(i);
- if (!addImport(import, &errors)) {
- Q_ASSERT(errors.size());
- QQmlError error(errors.takeFirst());
- error.setUrl(m_importCache.baseUrl());
- error.setLine(import->location.line);
- error.setColumn(import->location.column);
- errors.prepend(error); // put it back on the list after filling out information.
- setError(errors);
- return;
- }
- }
- }
-
- auto *v4 = QQmlEnginePrivate::getV4Engine(typeLoader()->engine());
-
- v4->injectModule(unit);
-
- for (const QString &request: unit->moduleRequests()) {
- if (v4->moduleForUrl(QUrl(request), unit.data()))
- continue;
-
- const QUrl absoluteRequest = unit->finalUrl().resolved(QUrl(request));
- QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(absoluteRequest);
- addDependency(blob.data());
- scriptImported(blob, /* ### */QV4::CompiledData::Location(), /*qualifier*/QString(), /*namespace*/QString());
- }
-}
-
-QQmlQmldirData::QQmlQmldirData(const QUrl &url, QQmlTypeLoader *loader)
-: QQmlTypeLoader::Blob(url, QmldirFile, loader)
-{
-}
-
-const QString &QQmlQmldirData::content() const
-{
- return m_content;
-}
-
-const QV4::CompiledData::Import *QQmlQmldirData::import(QQmlTypeLoader::Blob *blob) const
-{
- QHash<QQmlTypeLoader::Blob *, const QV4::CompiledData::Import *>::const_iterator it =
- m_imports.find(blob);
- if (it == m_imports.end())
- return nullptr;
- return *it;
-}
-
-void QQmlQmldirData::setImport(QQmlTypeLoader::Blob *blob, const QV4::CompiledData::Import *import)
-{
- m_imports[blob] = import;
-}
-
-int QQmlQmldirData::priority(QQmlTypeLoader::Blob *blob) const
-{
- QHash<QQmlTypeLoader::Blob *, int>::const_iterator it = m_priorities.find(blob);
- if (it == m_priorities.end())
- return 0;
- return *it;
-}
-
-void QQmlQmldirData::setPriority(QQmlTypeLoader::Blob *blob, int priority)
-{
- m_priorities[blob] = priority;
-}
-
-void QQmlQmldirData::dataReceived(const SourceCodeData &data)
-{
- QString error;
- m_content = data.readAll(&error);
- if (!error.isEmpty()) {
- setError(error);
- return;
- }
-}
-
-void QQmlQmldirData::initializeFromCachedUnit(const QV4::CompiledData::Unit *)
-{
- Q_UNIMPLEMENTED();
-}
-
-QString QQmlDataBlob::SourceCodeData::readAll(QString *error) const
-{
- error->clear();
- if (hasInlineSourceCode)
- return inlineSourceCode;
-
- QFile f(fileInfo.absoluteFilePath());
- if (!f.open(QIODevice::ReadOnly)) {
- *error = f.errorString();
- return QString();
- }
-
- const qint64 fileSize = fileInfo.size();
-
- if (uchar *mappedData = f.map(0, fileSize)) {
- QString source = QString::fromUtf8(reinterpret_cast<const char *>(mappedData), fileSize);
- f.unmap(mappedData);
- return source;
- }
-
- QByteArray data(fileSize, Qt::Uninitialized);
- if (f.read(data.data(), data.length()) != data.length()) {
- *error = f.errorString();
- return QString();
- }
- return QString::fromUtf8(data);
-}
-
-QDateTime QQmlDataBlob::SourceCodeData::sourceTimeStamp() const
-{
- if (hasInlineSourceCode)
- return QDateTime();
-
- return fileInfo.lastModified();
-}
-
-bool QQmlDataBlob::SourceCodeData::exists() const
-{
- if (hasInlineSourceCode)
- return true;
- return fileInfo.exists();
-}
-
-bool QQmlDataBlob::SourceCodeData::isEmpty() const
-{
- if (hasInlineSourceCode)
- return inlineSourceCode.isEmpty();
- return fileInfo.size() == 0;
-}
-
QT_END_NAMESPACE
-
-#include "qqmltypeloader.moc"
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index 987e47222d..38a7e05961 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -51,213 +51,27 @@
// We mean it.
//
+#include <private/qqmldatablob_p.h>
+#include <private/qqmlimport_p.h>
+#include <private/qqmlmetatype_p.h>
+
#include <QtQml/qtqmlglobal.h>
-#include <QtCore/qobject.h>
-#include <QtCore/qatomic.h>
-#include <QtCore/qfileinfo.h>
-#include <QtCore/qcache.h>
-#if QT_CONFIG(qml_network)
-#include <QtNetwork/qnetworkreply.h>
-#endif
#include <QtQml/qqmlerror.h>
-#include <QtQml/qqmlengine.h>
-#include <QtQml/qqmlfile.h>
-#include <QtQml/qqmlabstracturlinterceptor.h>
-#include <private/qhashedstring_p.h>
-#include <private/qqmlimport_p.h>
-#include <private/qqmlcleanup_p.h>
-#include <private/qqmldirparser_p.h>
-#include <private/qflagpointer_p.h>
-#include <private/qqmlirbuilder_p.h>
+#include <QtCore/qcache.h>
+#include <QtCore/qmutex.h>
-#include <private/qv4value_p.h>
-#include <private/qv4script_p.h>
+#include <memory>
QT_BEGIN_NAMESPACE
-class QQmlScriptData;
class QQmlScriptBlob;
class QQmlQmldirData;
-class QQmlTypeLoader;
-class QQmlComponentPrivate;
class QQmlTypeData;
-class QQmlTypeLoader;
class QQmlExtensionInterface;
class QQmlProfiler;
-struct QQmlCompileError;
-
-namespace QmlIR {
-struct Document;
-}
-
-class Q_QML_PRIVATE_EXPORT QQmlDataBlob : public QQmlRefCount
-{
-public:
- enum Status {
- Null, // Prior to QQmlTypeLoader::load()
- Loading, // Prior to data being received and dataReceived() being called
- WaitingForDependencies, // While there are outstanding addDependency()s
- ResolvingDependencies, // While resolving outstanding dependencies, to detect cycles
- Complete, // Finished
- Error // Error
- };
-
- enum Type { //Matched in QQmlAbstractUrlInterceptor
- QmlFile = QQmlAbstractUrlInterceptor::QmlFile,
- JavaScriptFile = QQmlAbstractUrlInterceptor::JavaScriptFile,
- QmldirFile = QQmlAbstractUrlInterceptor::QmldirFile
- };
-
- QQmlDataBlob(const QUrl &, Type, QQmlTypeLoader* manager);
- ~QQmlDataBlob() override;
-
- void startLoading();
-
- QQmlTypeLoader *typeLoader() const { return m_typeLoader; }
-
- Type type() const;
-
- Status status() const;
- bool isNull() const;
- bool isLoading() const;
- bool isWaiting() const;
- bool isComplete() const;
- bool isError() const;
- bool isCompleteOrError() const;
-
- qreal progress() const;
-
- QUrl url() const;
- QString urlString() const;
- QUrl finalUrl() const;
- QString finalUrlString() const;
-
- QList<QQmlError> errors() const;
-
- class SourceCodeData {
- public:
- QString readAll(QString *error) const;
- QDateTime sourceTimeStamp() const;
- bool exists() const;
- bool isEmpty() const;
- private:
- friend class QQmlDataBlob;
- friend class QQmlTypeLoader;
- QString inlineSourceCode;
- QFileInfo fileInfo;
- bool hasInlineSourceCode = false;
- };
-
-protected:
- // Can be called from within callbacks
- void setError(const QQmlError &);
- void setError(const QList<QQmlError> &errors);
- void setError(const QQmlCompileError &error);
- void setError(const QVector<QQmlCompileError> &errors);
- void setError(const QString &description);
- void addDependency(QQmlDataBlob *);
-
- // Callbacks made in load thread
- virtual void dataReceived(const SourceCodeData &) = 0;
- virtual void initializeFromCachedUnit(const QV4::CompiledData::Unit*) = 0;
- virtual void done();
-#if QT_CONFIG(qml_network)
- virtual void networkError(QNetworkReply::NetworkError);
-#endif
- virtual void dependencyError(QQmlDataBlob *);
- virtual void dependencyComplete(QQmlDataBlob *);
- virtual void allDependenciesDone();
-
- // Callbacks made in main thread
- virtual void downloadProgressChanged(qreal);
- virtual void completed();
-
-protected:
- // Manager that is currently fetching data for me
- QQmlTypeLoader *m_typeLoader;
-
-private:
- friend class QQmlTypeLoader;
- friend class QQmlTypeLoaderThread;
-
- void tryDone();
- void cancelAllWaitingFor();
- void notifyAllWaitingOnMe();
- void notifyComplete(QQmlDataBlob *);
-
- struct ThreadData {
- inline ThreadData();
- inline QQmlDataBlob::Status status() const;
- inline void setStatus(QQmlDataBlob::Status);
- inline bool isAsync() const;
- inline void setIsAsync(bool);
- inline quint8 progress() const;
- inline void setProgress(quint8);
-
- private:
- QAtomicInt _p;
- };
- ThreadData m_data;
-
- // m_errors should *always* be written before the status is set to Error.
- // We use the status change as a memory fence around m_errors so that locking
- // isn't required. Once the status is set to Error (or Complete), m_errors
- // cannot be changed.
- QList<QQmlError> m_errors;
-
- Type m_type;
-
- QUrl m_url;
- QUrl m_finalUrl;
- mutable QString m_urlString;
- mutable QString m_finalUrlString;
-
- // List of QQmlDataBlob's that are waiting for me to complete.
- QList<QQmlDataBlob *> m_waitingOnMe;
-
- // List of QQmlDataBlob's that I am waiting for to complete.
- QVector<QQmlRefPointer<QQmlDataBlob>> m_waitingFor;
-
- int m_redirectCount:30;
- bool m_inCallback:1;
- bool m_isDone:1;
-};
-
class QQmlTypeLoaderThread;
-
-class QQmlTypeLoaderQmldirContent
-{
-private:
- friend class QQmlTypeLoader;
-
- void setContent(const QString &location, const QString &content);
- void setError(const QQmlError &);
-
-public:
- QQmlTypeLoaderQmldirContent();
- QQmlTypeLoaderQmldirContent(const QQmlTypeLoaderQmldirContent &) = default;
- QQmlTypeLoaderQmldirContent &operator=(const QQmlTypeLoaderQmldirContent &) = default;
-
- bool hasContent() const { return m_hasContent; }
- bool hasError() const;
- QList<QQmlError> errors(const QString &uri) const;
-
- QString typeNamespace() const;
-
- QQmlDirComponents components() const;
- QQmlDirScripts scripts() const;
- QQmlDirPlugins plugins() const;
-
- QString pluginLocation() const;
-
- bool designerSupported() const;
-
-private:
- QQmlDirParser m_parser;
- QString m_location;
- bool m_hasContent = false;
-};
+class QQmlEngine;
class Q_QML_PRIVATE_EXPORT QQmlTypeLoader
{
@@ -275,11 +89,31 @@ public:
void setCachedUnitStatus(QQmlMetaType::CachedUnitLookupError status) { m_cachedUnitStatus = status; }
+ struct PendingImport
+ {
+ QV4::CompiledData::Import::ImportType type = QV4::CompiledData::Import::ImportType::ImportLibrary;
+
+ QString uri;
+ QString qualifier;
+
+ int majorVersion = -1;
+ int minorVersion = -1;
+
+ QV4::CompiledData::Location location;
+
+ int priority = 0;
+
+ PendingImport() = default;
+ PendingImport(Blob *blob, const QV4::CompiledData::Import *import);
+ };
+ using PendingImportPtr = std::shared_ptr<PendingImport>;
+
protected:
bool addImport(const QV4::CompiledData::Import *import, QList<QQmlError> *errors);
+ bool addImport(PendingImportPtr import, QList<QQmlError> *errors);
- bool fetchQmldir(const QUrl &url, const QV4::CompiledData::Import *import, int priority, QList<QQmlError> *errors);
- bool updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors);
+ bool fetchQmldir(const QUrl &url, PendingImportPtr import, int priority, QList<QQmlError> *errors);
+ bool updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, PendingImportPtr import, QList<QQmlError> *errors);
private:
virtual bool qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &, QList<QQmlError> *);
@@ -288,13 +122,16 @@ public:
void dependencyComplete(QQmlDataBlob *) override;
+ bool loadImportDependencies(PendingImportPtr currentImport, const QString &qmldirUri, QList<QQmlError> *errors);
+
protected:
virtual QString stringAt(int) const { return QString(); }
bool isDebugging() const;
+ bool diskCacheEnabled() const;
QQmlImports m_importCache;
- QHash<const QV4::CompiledData::Import*, int> m_unresolvedImports;
+ QVector<PendingImportPtr> m_unresolvedImports;
QVector<QQmlRefPointer<QQmlQmldirData>> m_qmldirs;
QQmlMetaType::CachedUnitLookupError m_cachedUnitStatus = QQmlMetaType::CachedUnitLookupError::NoError;
};
@@ -418,209 +255,6 @@ private:
friend struct StaticLoader;
};
-class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob
-{
- Q_DECLARE_TR_FUNCTIONS(QQmlTypeData)
-public:
- struct TypeReference
- {
- TypeReference() : majorVersion(0), minorVersion(0), needsCreation(true) {}
-
- QV4::CompiledData::Location location;
- QQmlType type;
- int majorVersion;
- int minorVersion;
- QQmlRefPointer<QQmlTypeData> typeData;
- QString prefix; // used by CompositeSingleton types
- QString qualifiedName() const;
- bool needsCreation;
- };
-
- struct ScriptReference
- {
- QV4::CompiledData::Location location;
- QString qualifier;
- QQmlRefPointer<QQmlScriptBlob> script;
- };
-
-private:
- friend class QQmlTypeLoader;
-
- QQmlTypeData(const QUrl &, QQmlTypeLoader *);
-
-public:
- ~QQmlTypeData() override;
-
- const QList<ScriptReference> &resolvedScripts() const;
-
- QV4::CompiledData::CompilationUnit *compilationUnit() const;
-
- // Used by QQmlComponent to get notifications
- struct TypeDataCallback {
- virtual ~TypeDataCallback();
- virtual void typeDataProgress(QQmlTypeData *, qreal) {}
- virtual void typeDataReady(QQmlTypeData *) {}
- };
- void registerCallback(TypeDataCallback *);
- void unregisterCallback(TypeDataCallback *);
-
-protected:
- void done() override;
- void completed() override;
- void dataReceived(const SourceCodeData &) override;
- void initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) override;
- void allDependenciesDone() override;
- void downloadProgressChanged(qreal) override;
-
- QString stringAt(int index) const override;
-
-private:
- bool tryLoadFromDiskCache();
- bool loadFromSource();
- void restoreIR(QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit);
- void continueLoadFromIR();
- void resolveTypes();
- QQmlCompileError buildTypeResolutionCaches(
- QQmlRefPointer<QQmlTypeNameCache> *typeNameCache,
- QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache
- ) const;
- void compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
- const QV4::CompiledData::DependentTypesHasher &dependencyHasher);
- void createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache);
- bool resolveType(const QString &typeName, int &majorVersion, int &minorVersion,
- TypeReference &ref, int lineNumber = -1, int columnNumber = -1,
- bool reportErrors = true,
- QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType);
-
- void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override;
-
-
- SourceCodeData m_backupSourceCode; // used when cache verification fails.
- QScopedPointer<QmlIR::Document> m_document;
- QV4::CompiledData::TypeReferenceMap m_typeReferences;
-
- QList<ScriptReference> m_scripts;
-
- QSet<QString> m_namespaces;
- QList<TypeReference> m_compositeSingletons;
-
- // map from name index to resolved type
- // While this could be a hash, a map is chosen here to provide a stable
- // order, which is used to calculating a check-sum on dependent meta-objects.
- QMap<int, TypeReference> m_resolvedTypes;
- bool m_typesResolved:1;
-
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compiledData;
-
- QList<TypeDataCallback *> m_callbacks;
-
- bool m_implicitImportLoaded;
- bool loadImplicitImport();
-};
-
-// QQmlScriptData instances are created, uninitialized, by the loader in the
-// load thread. The first time they are used by the VME, they are initialized which
-// creates their v8 objects and they are referenced and added to the engine's cleanup
-// list. During QQmlCleanup::clear() all v8 resources are destroyed, and the
-// reference that was created is released but final deletion only occurs once all the
-// references as released. This is all intended to ensure that the v8 resources are
-// only created and destroyed in the main thread :)
-class Q_AUTOTEST_EXPORT QQmlScriptData : public QQmlCleanup, public QQmlRefCount
-{
-private:
- friend class QQmlTypeLoader;
-
- QQmlScriptData();
-
-public:
- QUrl url;
- QString urlString;
- QQmlTypeNameCache *typeNameCache;
- QVector<QQmlRefPointer<QQmlScriptBlob>> scripts;
-
- QV4::ReturnedValue scriptValueForContext(QQmlContextData *parentCtxt);
-
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit() const { return m_precompiledScript; }
-
-protected:
- void clear() override; // From QQmlCleanup
-
-private:
- friend class QQmlScriptBlob;
-
- void initialize(QQmlEngine *);
- QQmlContextData *qmlContextDataForContext(QQmlContextData *parentQmlContextData);
-
- bool m_loaded;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_precompiledScript;
- QV4::PersistentValue m_value;
-};
-
-class Q_AUTOTEST_EXPORT QQmlScriptBlob : public QQmlTypeLoader::Blob
-{
-private:
- friend class QQmlTypeLoader;
-
- QQmlScriptBlob(const QUrl &, QQmlTypeLoader *);
-
-public:
- ~QQmlScriptBlob() override;
-
- struct ScriptReference
- {
- QV4::CompiledData::Location location;
- QString qualifier;
- QString nameSpace;
- QQmlRefPointer<QQmlScriptBlob> script;
- };
-
- QQmlRefPointer<QQmlScriptData> scriptData() const;
-
-protected:
- void dataReceived(const SourceCodeData &) override;
- void initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) override;
- void done() override;
-
- QString stringAt(int index) const override;
-
-private:
- void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override;
- void initializeFromCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit);
-
- QList<ScriptReference> m_scripts;
- QQmlRefPointer<QQmlScriptData> m_scriptData;
- const bool m_isModule;
-};
-
-class Q_AUTOTEST_EXPORT QQmlQmldirData : public QQmlTypeLoader::Blob
-{
-private:
- friend class QQmlTypeLoader;
-
- QQmlQmldirData(const QUrl &, QQmlTypeLoader *);
-
-public:
- const QString &content() const;
-
- const QV4::CompiledData::Import *import(QQmlTypeLoader::Blob *) const;
- void setImport(QQmlTypeLoader::Blob *, const QV4::CompiledData::Import *);
-
- int priority(QQmlTypeLoader::Blob *) const;
- void setPriority(QQmlTypeLoader::Blob *, int);
-
-protected:
- void dataReceived(const SourceCodeData &) override;
- void initializeFromCachedUnit(const QV4::CompiledData::Unit *) override;
-
-private:
- QString m_content;
- QHash<QQmlTypeLoader::Blob *, const QV4::CompiledData::Import *> m_imports;
- QHash<QQmlTypeLoader::Blob *, int> m_priorities;
-};
-
-
QT_END_NAMESPACE
#endif // QQMLTYPELOADER_P_H
diff --git a/src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp b/src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp
new file mode 100644
index 0000000000..af97643163
--- /dev/null
+++ b/src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qqmltypeloadernetworkreplyproxy_p.h>
+#include <private/qqmltypeloader_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlTypeLoaderNetworkReplyProxy::QQmlTypeLoaderNetworkReplyProxy(QQmlTypeLoader *l)
+ : l(l)
+{
+}
+
+void QQmlTypeLoaderNetworkReplyProxy::finished()
+{
+ Q_ASSERT(sender());
+ Q_ASSERT(qobject_cast<QNetworkReply *>(sender()));
+ QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
+ l->networkReplyFinished(reply);
+}
+
+void QQmlTypeLoaderNetworkReplyProxy::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
+{
+ Q_ASSERT(sender());
+ Q_ASSERT(qobject_cast<QNetworkReply *>(sender()));
+ QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
+ l->networkReplyProgress(reply, bytesReceived, bytesTotal);
+}
+
+// This function is for when you want to shortcut the signals and call directly
+void QQmlTypeLoaderNetworkReplyProxy::manualFinished(QNetworkReply *reply)
+{
+ qint64 replySize = reply->size();
+ l->networkReplyProgress(reply, replySize, replySize);
+ l->networkReplyFinished(reply);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltypeloadernetworkreplyproxy_p.h b/src/qml/qml/qqmltypeloadernetworkreplyproxy_p.h
new file mode 100644
index 0000000000..ed87a2b508
--- /dev/null
+++ b/src/qml/qml/qqmltypeloadernetworkreplyproxy_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLTYPELOADERNETWORKREPLYPROXY_P_H
+#define QQMLTYPELOADERNETWORKREPLYPROXY_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 <QtQml/qtqmlglobal.h>
+#include <QtCore/qobject.h>
+
+QT_REQUIRE_CONFIG(qml_network);
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkReply;
+class QQmlTypeLoader;
+
+// This is a lame object that we need to ensure that slots connected to
+// QNetworkReply get called in the correct thread (the loader thread).
+// As QQmlTypeLoader lives in the main thread, and we can't use
+// Qt::DirectConnection connections from a QNetworkReply (because then
+// sender() wont work), we need to insert this object in the middle.
+class QQmlTypeLoaderNetworkReplyProxy : public QObject
+{
+ Q_OBJECT
+public:
+ QQmlTypeLoaderNetworkReplyProxy(QQmlTypeLoader *l);
+
+public slots:
+ void finished();
+ void downloadProgress(qint64, qint64);
+ void manualFinished(QNetworkReply*);
+
+private:
+ QQmlTypeLoader *l;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLTYPELOADERNETWORKREPLYPROXY_P_H
diff --git a/src/qml/qml/qqmltypeloaderqmldircontent.cpp b/src/qml/qml/qqmltypeloaderqmldircontent.cpp
new file mode 100644
index 0000000000..8e983db756
--- /dev/null
+++ b/src/qml/qml/qqmltypeloaderqmldircontent.cpp
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qqmltypeloaderqmldircontent_p.h>
+#include <QtQml/qqmlerror.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlTypeLoaderQmldirContent::QQmlTypeLoaderQmldirContent()
+{
+}
+
+bool QQmlTypeLoaderQmldirContent::hasError() const
+{
+ return m_parser.hasError();
+}
+
+QList<QQmlError> QQmlTypeLoaderQmldirContent::errors(const QString &uri) const
+{
+ QList<QQmlError> errors;
+ const QUrl url(uri);
+ const auto parseErrors = m_parser.errors(uri);
+ for (const auto &parseError : parseErrors) {
+ QQmlError error;
+ error.setUrl(url);
+ error.setLine(parseError.line);
+ error.setColumn(parseError.column);
+ error.setDescription(parseError.message);
+ errors.append(error);
+ }
+ return errors;
+}
+
+QString QQmlTypeLoaderQmldirContent::typeNamespace() const
+{
+ return m_parser.typeNamespace();
+}
+
+void QQmlTypeLoaderQmldirContent::setContent(const QString &location, const QString &content)
+{
+ m_hasContent = true;
+ m_location = location;
+ m_parser.parse(content);
+}
+
+void QQmlTypeLoaderQmldirContent::setError(const QQmlError &error)
+{
+ QQmlJS::DiagnosticMessage parseError;
+ parseError.line = error.line();
+ parseError.column = error.column();
+ parseError.message = error.description();
+ m_parser.setError(parseError);
+}
+
+QQmlDirComponents QQmlTypeLoaderQmldirContent::components() const
+{
+ return m_parser.components();
+}
+
+QQmlDirScripts QQmlTypeLoaderQmldirContent::scripts() const
+{
+ return m_parser.scripts();
+}
+
+QQmlDirPlugins QQmlTypeLoaderQmldirContent::plugins() const
+{
+ return m_parser.plugins();
+}
+
+QStringList QQmlTypeLoaderQmldirContent::imports() const
+{
+ return m_parser.imports();
+}
+
+QString QQmlTypeLoaderQmldirContent::pluginLocation() const
+{
+ return m_location;
+}
+
+bool QQmlTypeLoaderQmldirContent::designerSupported() const
+{
+ return m_parser.designerSupported();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltypeloaderqmldircontent_p.h b/src/qml/qml/qqmltypeloaderqmldircontent_p.h
new file mode 100644
index 0000000000..698643c7ec
--- /dev/null
+++ b/src/qml/qml/qqmltypeloaderqmldircontent_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLTYPELOADERQMLDIRCONTENT_P_H
+#define QQMLTYPELOADERQMLDIRCONTENT_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/qqmldirparser_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlError;
+class QQmlTypeLoaderQmldirContent
+{
+private:
+ friend class QQmlTypeLoader;
+
+ void setContent(const QString &location, const QString &content);
+ void setError(const QQmlError &);
+
+public:
+ QQmlTypeLoaderQmldirContent();
+ QQmlTypeLoaderQmldirContent(const QQmlTypeLoaderQmldirContent &) = default;
+ QQmlTypeLoaderQmldirContent &operator=(const QQmlTypeLoaderQmldirContent &) = default;
+
+ bool hasContent() const { return m_hasContent; }
+ bool hasError() const;
+ QList<QQmlError> errors(const QString &uri) const;
+
+ QString typeNamespace() const;
+
+ QQmlDirComponents components() const;
+ QQmlDirScripts scripts() const;
+ QQmlDirPlugins plugins() const;
+ QStringList imports() const;
+
+ QString pluginLocation() const;
+
+ bool designerSupported() const;
+
+private:
+ QQmlDirParser m_parser;
+ QString m_location;
+ bool m_hasContent = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLTYPELOADERQMLDIRCONTENT_P_H
diff --git a/src/qml/qml/qqmltypeloaderthread.cpp b/src/qml/qml/qqmltypeloaderthread.cpp
new file mode 100644
index 0000000000..0e1cecd1e5
--- /dev/null
+++ b/src/qml/qml/qqmltypeloaderthread.cpp
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qqmlengine_p.h>
+#include <private/qqmlextensionplugin_p.h>
+#include <private/qqmltypeloaderthread_p.h>
+
+#if QT_CONFIG(qml_network)
+#include <private/qqmltypeloadernetworkreplyproxy_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QQmlTypeLoaderThread::QQmlTypeLoaderThread(QQmlTypeLoader *loader)
+ : m_loader(loader)
+#if QT_CONFIG(qml_network)
+ , m_networkAccessManager(nullptr), m_networkReplyProxy(nullptr)
+#endif // qml_network
+{
+ // Do that after initializing all the members.
+ startup();
+}
+
+#if QT_CONFIG(qml_network)
+QNetworkAccessManager *QQmlTypeLoaderThread::networkAccessManager() const
+{
+ Q_ASSERT(isThisThread());
+ if (!m_networkAccessManager) {
+ m_networkAccessManager = QQmlEnginePrivate::get(m_loader->engine())->createNetworkAccessManager(nullptr);
+ m_networkReplyProxy = new QQmlTypeLoaderNetworkReplyProxy(m_loader);
+ }
+
+ return m_networkAccessManager;
+}
+
+QQmlTypeLoaderNetworkReplyProxy *QQmlTypeLoaderThread::networkReplyProxy() const
+{
+ Q_ASSERT(isThisThread());
+ Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first
+ return m_networkReplyProxy;
+}
+#endif // qml_network
+
+void QQmlTypeLoaderThread::load(QQmlDataBlob *b)
+{
+ b->addref();
+ callMethodInThread(&This::loadThread, b);
+}
+
+void QQmlTypeLoaderThread::loadAsync(QQmlDataBlob *b)
+{
+ b->addref();
+ postMethodToThread(&This::loadThread, b);
+}
+
+void QQmlTypeLoaderThread::loadWithStaticData(QQmlDataBlob *b, const QByteArray &d)
+{
+ b->addref();
+ callMethodInThread(&This::loadWithStaticDataThread, b, d);
+}
+
+void QQmlTypeLoaderThread::loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &d)
+{
+ b->addref();
+ postMethodToThread(&This::loadWithStaticDataThread, b, d);
+}
+
+void QQmlTypeLoaderThread::loadWithCachedUnit(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit)
+{
+ b->addref();
+ callMethodInThread(&This::loadWithCachedUnitThread, b, unit);
+}
+
+void QQmlTypeLoaderThread::loadWithCachedUnitAsync(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit)
+{
+ b->addref();
+ postMethodToThread(&This::loadWithCachedUnitThread, b, unit);
+}
+
+void QQmlTypeLoaderThread::callCompleted(QQmlDataBlob *b)
+{
+ b->addref();
+#if !QT_CONFIG(thread)
+ if (!isThisThread())
+ postMethodToThread(&This::callCompletedMain, b);
+#else
+ postMethodToMain(&This::callCompletedMain, b);
+#endif
+}
+
+void QQmlTypeLoaderThread::callDownloadProgressChanged(QQmlDataBlob *b, qreal p)
+{
+ b->addref();
+#if !QT_CONFIG(thread)
+ if (!isThisThread())
+ postMethodToThread(&This::callDownloadProgressChangedMain, b, p);
+#else
+ postMethodToMain(&This::callDownloadProgressChangedMain, b, p);
+#endif
+}
+
+void QQmlTypeLoaderThread::initializeEngine(QQmlExtensionInterface *iface,
+ const char *uri)
+{
+ callMethodInMain(&This::initializeEngineMain, iface, uri);
+}
+
+void QQmlTypeLoaderThread::shutdownThread()
+{
+#if QT_CONFIG(qml_network)
+ delete m_networkAccessManager;
+ m_networkAccessManager = nullptr;
+ delete m_networkReplyProxy;
+ m_networkReplyProxy = nullptr;
+#endif // qml_network
+}
+
+void QQmlTypeLoaderThread::loadThread(QQmlDataBlob *b)
+{
+ m_loader->loadThread(b);
+ b->release();
+}
+
+void QQmlTypeLoaderThread::loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &d)
+{
+ m_loader->loadWithStaticDataThread(b, d);
+ b->release();
+}
+
+void QQmlTypeLoaderThread::loadWithCachedUnitThread(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit)
+{
+ m_loader->loadWithCachedUnitThread(b, unit);
+ b->release();
+}
+
+void QQmlTypeLoaderThread::callCompletedMain(QQmlDataBlob *b)
+{
+#ifdef DATABLOB_DEBUG
+ qWarning("QQmlTypeLoaderThread: %s completed() callback", qPrintable(b->urlString()));
+#endif
+ b->completed();
+ b->release();
+}
+
+void QQmlTypeLoaderThread::callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p)
+{
+#ifdef DATABLOB_DEBUG
+ qWarning("QQmlTypeLoaderThread: %s downloadProgressChanged(%f) callback",
+ qPrintable(b->urlString()), p);
+#endif
+ b->downloadProgressChanged(p);
+ b->release();
+}
+
+void QQmlTypeLoaderThread::initializeEngineMain(QQmlExtensionInterface *iface,
+ const char *uri)
+{
+ Q_ASSERT(m_loader->engine()->thread() == QThread::currentThread());
+ iface->initializeEngine(m_loader->engine(), uri);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltypeloaderthread_p.h b/src/qml/qml/qqmltypeloaderthread_p.h
new file mode 100644
index 0000000000..67e47e86de
--- /dev/null
+++ b/src/qml/qml/qqmltypeloaderthread_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLTYPELOADERTHREAD_P_H
+#define QQMLTYPELOADERTHREAD_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/qqmlthread_p.h>
+#include <private/qv4compileddata_p.h>
+
+#include <QtQml/qtqmlglobal.h>
+
+#if QT_CONFIG(qml_network)
+#include <private/qqmltypeloadernetworkreplyproxy_p.h>
+#include <QtNetwork/qnetworkaccessmanager.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QQmlDataBlob;
+class QQmlTypeLoader;
+class QQmlExtensionInterface;
+
+class QQmlTypeLoaderThread : public QQmlThread
+{
+ typedef QQmlTypeLoaderThread This;
+
+public:
+ QQmlTypeLoaderThread(QQmlTypeLoader *loader);
+#if QT_CONFIG(qml_network)
+ QNetworkAccessManager *networkAccessManager() const;
+ QQmlTypeLoaderNetworkReplyProxy *networkReplyProxy() const;
+#endif // qml_network
+ void load(QQmlDataBlob *b);
+ void loadAsync(QQmlDataBlob *b);
+ void loadWithStaticData(QQmlDataBlob *b, const QByteArray &);
+ void loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &);
+ void loadWithCachedUnit(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit);
+ void loadWithCachedUnitAsync(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit);
+ void callCompleted(QQmlDataBlob *b);
+ void callDownloadProgressChanged(QQmlDataBlob *b, qreal p);
+ void initializeEngine(QQmlExtensionInterface *, const char *);
+
+protected:
+ void shutdownThread() override;
+
+private:
+ void loadThread(QQmlDataBlob *b);
+ void loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &);
+ void loadWithCachedUnitThread(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit);
+ void callCompletedMain(QQmlDataBlob *b);
+ void callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p);
+ void initializeEngineMain(QQmlExtensionInterface *iface, const char *uri);
+
+ QQmlTypeLoader *m_loader;
+#if QT_CONFIG(qml_network)
+ mutable QNetworkAccessManager *m_networkAccessManager;
+ mutable QQmlTypeLoaderNetworkReplyProxy *m_networkReplyProxy;
+#endif // qml_network
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLTYPELOADERTHREAD_P_H
diff --git a/src/qml/qml/qqmltypemodule.cpp b/src/qml/qml/qqmltypemodule.cpp
new file mode 100644
index 0000000000..9c9bf3e48f
--- /dev/null
+++ b/src/qml/qml/qqmltypemodule.cpp
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmltypemodule_p_p.h"
+
+#include <private/qqmltype_p_p.h>
+
+#include <QtCore/qmutex.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlTypeModule::QQmlTypeModule(const QString &module, int majorVersion)
+ : d(new QQmlTypeModulePrivate(module, majorVersion))
+{
+}
+
+QQmlTypeModule::~QQmlTypeModule()
+{
+ delete d;
+}
+
+QString QQmlTypeModule::module() const
+{
+ // No need to lock. d->module is const
+ return d->module;
+}
+
+int QQmlTypeModule::majorVersion() const
+{
+ // No need to lock. d->majorVersion is const
+ return d->majorVersion;
+}
+
+int QQmlTypeModule::minimumMinorVersion() const
+{
+ return d->minMinorVersion.loadRelaxed();
+}
+
+int QQmlTypeModule::maximumMinorVersion() const
+{
+ return d->maxMinorVersion.loadRelaxed();
+}
+
+void QQmlTypeModule::addMinorVersion(int version)
+{
+ for (int oldVersion = d->minMinorVersion.loadRelaxed();
+ oldVersion > version && !d->minMinorVersion.testAndSetOrdered(oldVersion, version);
+ oldVersion = d->minMinorVersion.loadRelaxed()) {
+ }
+
+ for (int oldVersion = d->maxMinorVersion.loadRelaxed();
+ oldVersion < version && !d->maxMinorVersion.testAndSetOrdered(oldVersion, version);
+ oldVersion = d->maxMinorVersion.loadRelaxed()) {
+ }
+}
+
+void QQmlTypeModule::add(QQmlTypePrivate *type)
+{
+ QMutexLocker lock(&d->mutex);
+ addMinorVersion(type->version_min);
+
+ QList<QQmlTypePrivate *> &list = d->typeHash[type->elementName];
+ for (int ii = 0; ii < list.count(); ++ii) {
+ Q_ASSERT(list.at(ii));
+ if (list.at(ii)->version_min < type->version_min) {
+ list.insert(ii, type);
+ return;
+ }
+ }
+ list.append(type);
+}
+
+void QQmlTypeModule::remove(const QQmlTypePrivate *type)
+{
+ QMutexLocker lock(&d->mutex);
+ for (auto elementIt = d->typeHash.begin(); elementIt != d->typeHash.end();) {
+ QQmlMetaType::removeQQmlTypePrivate(elementIt.value(), type);
+
+#if 0
+ if (list.isEmpty())
+ elementIt = typeHash.erase(elementIt);
+ else
+ ++elementIt;
+#else
+ ++elementIt;
+#endif
+ }
+}
+
+bool QQmlTypeModule::isLocked() const
+{
+ return d->locked.loadRelaxed() != 0;
+}
+
+void QQmlTypeModule::lock()
+{
+ d->locked.storeRelaxed(1);
+}
+
+QQmlType QQmlTypeModule::type(const QHashedStringRef &name, int minor) const
+{
+ QMutexLocker lock(&d->mutex);
+ QList<QQmlTypePrivate *> *types = d->typeHash.value(name);
+ if (types) {
+ for (int ii = 0; ii < types->count(); ++ii)
+ if (types->at(ii)->version_min <= minor)
+ return QQmlType(types->at(ii));
+ }
+
+ return QQmlType();
+}
+
+QQmlType QQmlTypeModule::type(const QV4::String *name, int minor) const
+{
+ QMutexLocker lock(&d->mutex);
+ QList<QQmlTypePrivate *> *types = d->typeHash.value(name);
+ if (types) {
+ for (int ii = 0; ii < types->count(); ++ii)
+ if (types->at(ii)->version_min <= minor)
+ return QQmlType(types->at(ii));
+ }
+
+ return QQmlType();
+}
+
+void QQmlTypeModule::walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const
+{
+ QMutexLocker lock(&d->mutex);
+ for (auto typeCandidates = d->typeHash.begin(), end = d->typeHash.end();
+ typeCandidates != end; ++typeCandidates) {
+ for (auto type: typeCandidates.value()) {
+ if (type->regType == QQmlType::CompositeSingletonType)
+ callback(QQmlType(type));
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltypemodule_p.h b/src/qml/qml/qqmltypemodule_p.h
new file mode 100644
index 0000000000..b84a91b5db
--- /dev/null
+++ b/src/qml/qml/qqmltypemodule_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLTYPEMODULE_P_H
+#define QQMLTYPEMODULE_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 <QtQml/qtqmlglobal.h>
+#include <QtCore/qstring.h>
+
+#include <functional>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlType;
+class QQmlTypePrivate;
+struct QQmlMetaTypeData;
+class QHashedString;
+class QHashedStringRef;
+
+namespace QV4 {
+struct String;
+}
+
+class QQmlTypeModulePrivate;
+class QQmlTypeModule
+{
+public:
+ QQmlTypeModule(const QString &uri = QString(), int majorVersion = 0);
+ ~QQmlTypeModule();
+
+ void add(QQmlTypePrivate *);
+ void remove(const QQmlTypePrivate *type);
+
+ bool isLocked() const;
+ void lock();
+
+ QString module() const;
+ int majorVersion() const;
+
+ void addMinorVersion(int minorVersion);
+ int minimumMinorVersion() const;
+ int maximumMinorVersion() const;
+
+ QQmlType type(const QHashedStringRef &, int) const;
+ QQmlType type(const QV4::String *, int) const;
+
+ void walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const;
+
+private:
+ QQmlTypeModulePrivate *d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLTYPEMODULE_P_H
diff --git a/src/qml/qml/qqmltypemodule_p_p.h b/src/qml/qml/qqmltypemodule_p_p.h
new file mode 100644
index 0000000000..b1dab1c4a0
--- /dev/null
+++ b/src/qml/qml/qqmltypemodule_p_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLTYPEMODULE_P_P_H
+#define QQMLTYPEMODULE_P_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/qqmltypemodule_p.h>
+#include <private/qstringhash_p.h>
+#include <private/qqmlmetatypedata_p.h>
+
+#include <QtCore/qmutex.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlTypeModulePrivate
+{
+public:
+ QQmlTypeModulePrivate(QString module, int majorVersion) :
+ module(std::move(module)), majorVersion(majorVersion)
+ {}
+
+ const QString module;
+ const int majorVersion = 0;
+
+ // Can only ever decrease
+ QAtomicInt minMinorVersion = std::numeric_limits<int>::max();
+
+ // Can only ever increase
+ QAtomicInt maxMinorVersion = 0;
+
+ // Bool. Can only be set to 1 once.
+ QAtomicInt locked = 0;
+
+ typedef QStringHash<QList<QQmlTypePrivate *> > TypeHash;
+ TypeHash typeHash;
+
+ QMutex mutex;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLTYPEMODULE_P_P_H
diff --git a/src/qml/qml/qqmltypemoduleversion.cpp b/src/qml/qml/qqmltypemoduleversion.cpp
new file mode 100644
index 0000000000..bbbfa1a7b6
--- /dev/null
+++ b/src/qml/qml/qqmltypemoduleversion.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmltypemoduleversion_p.h"
+
+#include <private/qqmltype_p.h>
+#include <private/qqmltypemodule_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlTypeModuleVersion::QQmlTypeModuleVersion()
+ : m_module(nullptr), m_minor(0)
+{
+}
+
+QQmlTypeModuleVersion::QQmlTypeModuleVersion(QQmlTypeModule *module, int minor)
+ : m_module(module), m_minor(minor)
+{
+ Q_ASSERT(m_module);
+ Q_ASSERT(m_minor >= 0);
+}
+
+QQmlTypeModuleVersion::QQmlTypeModuleVersion(const QQmlTypeModuleVersion &o)
+ : m_module(o.m_module), m_minor(o.m_minor)
+{
+}
+
+QQmlTypeModuleVersion &QQmlTypeModuleVersion::operator=(const QQmlTypeModuleVersion &o)
+{
+ m_module = o.m_module;
+ m_minor = o.m_minor;
+ return *this;
+}
+
+QQmlTypeModule *QQmlTypeModuleVersion::module() const
+{
+ return m_module;
+}
+
+int QQmlTypeModuleVersion::minorVersion() const
+{
+ return m_minor;
+}
+
+QQmlType QQmlTypeModuleVersion::type(const QHashedStringRef &name) const
+{
+ if (!m_module)
+ return QQmlType();
+ return m_module->type(name, m_minor);
+}
+
+QQmlType QQmlTypeModuleVersion::type(const QV4::String *name) const
+{
+ if (!m_module)
+ return QQmlType();
+ return m_module->type(name, m_minor);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltypemoduleversion_p.h b/src/qml/qml/qqmltypemoduleversion_p.h
new file mode 100644
index 0000000000..20f4709ecb
--- /dev/null
+++ b/src/qml/qml/qqmltypemoduleversion_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLTYPEMODULEVERSION_P_H
+#define QQMLTYPEMODULEVERSION_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 <QtQml/qtqmlglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlTypeModule;
+class QQmlType;
+class QHashedStringRef;
+
+namespace QV4 {
+struct String;
+}
+
+class QQmlTypeModuleVersion
+{
+public:
+ QQmlTypeModuleVersion();
+ QQmlTypeModuleVersion(QQmlTypeModule *, int);
+ QQmlTypeModuleVersion(const QQmlTypeModuleVersion &);
+ QQmlTypeModuleVersion &operator=(const QQmlTypeModuleVersion &);
+
+ QQmlTypeModule *module() const;
+ int minorVersion() const;
+
+ QQmlType type(const QHashedStringRef &) const;
+ QQmlType type(const QV4::String *) const;
+
+private:
+ QQmlTypeModule *m_module;
+ int m_minor;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLTYPEMODULEVERSION_P_H
diff --git a/src/qml/qml/qqmltypenamecache_p.h b/src/qml/qml/qqmltypenamecache_p.h
index 28b5e7f0ad..b98fe77ed5 100644
--- a/src/qml/qml/qqmltypenamecache_p.h
+++ b/src/qml/qml/qqmltypenamecache_p.h
@@ -55,8 +55,9 @@
#include "qqmlcleanup_p.h"
#include "qqmlmetatype_p.h"
-#include <private/qhashedstring_p.h>
+#include <private/qstringhash_p.h>
#include <private/qqmlimport_p.h>
+#include <private/qqmltypemoduleversion_p.h>
#include <QtCore/qvector.h>
diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp
index c5fa4a04ec..ef4a628a04 100644
--- a/src/qml/qml/qqmltypewrapper.cpp
+++ b/src/qml/qml/qqmltypewrapper.cpp
@@ -38,10 +38,11 @@
****************************************************************************/
#include "qqmltypewrapper_p.h"
-#include <private/qv8engine_p.h>
#include <private/qqmlengine_p.h>
#include <private/qqmlcontext_p.h>
+#include <private/qqmlmetaobject_p.h>
+#include <private/qqmltypedata_p.h>
#include <private/qjsvalue_p.h>
#include <private/qv4functionobject_p.h>
@@ -89,10 +90,8 @@ QObject* QQmlTypeWrapper::singletonObject() const
if (!isSingleton())
return nullptr;
- QQmlEngine *e = engine()->qmlEngine();
- QQmlType::SingletonInstanceInfo *siinfo = d()->type().singletonInstanceInfo();
- siinfo->init(e);
- return siinfo->qobjectApi(e);
+ QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine()->qmlEngine());
+ return e->singletonInstance<QObject*>(d()->type());
}
QVariant QQmlTypeWrapper::toVariant() const
@@ -100,13 +99,12 @@ QVariant QQmlTypeWrapper::toVariant() const
if (!isSingleton())
return QVariant::fromValue<QObject *>(d()->object);
- QQmlEngine *e = engine()->qmlEngine();
- QQmlType::SingletonInstanceInfo *siinfo = d()->type().singletonInstanceInfo();
- siinfo->init(e);
- if (QObject *qobjectSingleton = siinfo->qobjectApi(e))
- return QVariant::fromValue<QObject*>(qobjectSingleton);
+ QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine()->qmlEngine());
+ const QQmlType type = d()->type();
+ if (type.isQJSValueSingleton())
+ return QVariant::fromValue<QJSValue>(e->singletonInstance<QJSValue>(type));
- return QVariant::fromValue<QJSValue>(siinfo->scriptApi(e));
+ return QVariant::fromValue<QObject*>(e->singletonInstance<QObject*>(type));
}
@@ -194,50 +192,51 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons
// singleton types are handled differently to other types.
if (type.isSingleton()) {
- QQmlEngine *e = v4->qmlEngine();
- QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo();
- siinfo->init(e);
-
- QObject *qobjectSingleton = siinfo->qobjectApi(e);
- if (qobjectSingleton) {
-
- // check for enum value
- const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums;
- if (includeEnums && name->startsWithUpper()) {
- bool ok = false;
- int value = enumForSingleton(v4, name, qobjectSingleton, type, &ok);
- if (ok)
- return QV4::Value::fromInt32(value).asReturnedValue();
-
- value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok);
- if (ok) {
- Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>());
- enumWrapper->d()->typePrivate = type.priv();
- QQmlType::refHandle(enumWrapper->d()->typePrivate);
- enumWrapper->d()->scopeEnumIndex = value;
- return enumWrapper.asReturnedValue();
+ QQmlEnginePrivate *e = QQmlEnginePrivate::get(v4->qmlEngine());
+ QJSValue scriptSingleton;
+ if (type.isQObjectSingleton() || type.isCompositeSingleton()) {
+ if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) {
+ // check for enum value
+ const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums;
+ if (includeEnums && name->startsWithUpper()) {
+ bool ok = false;
+ int value = enumForSingleton(v4, name, qobjectSingleton, type, &ok);
+ if (ok)
+ return QV4::Value::fromInt32(value).asReturnedValue();
+
+ value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok);
+ if (ok) {
+ Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>());
+ enumWrapper->d()->typePrivate = type.priv();
+ QQmlType::refHandle(enumWrapper->d()->typePrivate);
+ enumWrapper->d()->scopeEnumIndex = value;
+ return enumWrapper.asReturnedValue();
+ }
}
- }
- // check for property.
- bool ok;
- const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, &ok);
- if (hasProperty)
- *hasProperty = ok;
-
- // Warn when attempting to access a lowercased enum value, singleton case
- if (!ok && includeEnums && !name->startsWithUpper()) {
- enumForSingleton(v4, name, qobjectSingleton, type, &ok);
- if (ok)
- return throwLowercaseEnumError(v4, name, type);
- }
+ // check for property.
+ bool ok;
+ const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, &ok);
+ if (hasProperty)
+ *hasProperty = ok;
+
+ // Warn when attempting to access a lowercased enum value, singleton case
+ if (!ok && includeEnums && !name->startsWithUpper()) {
+ enumForSingleton(v4, name, qobjectSingleton, type, &ok);
+ if (ok)
+ return throwLowercaseEnumError(v4, name, type);
+ }
- return result;
- } else if (!siinfo->scriptApi(e).isUndefined()) {
- // NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable.
- QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e)));
- if (!!o)
- return o->get(name);
+ return result;
+ }
+ } else if (type.isQJSValueSingleton()) {
+ QJSValue scriptSingleton = e->singletonInstance<QJSValue>(type);
+ if (!scriptSingleton.isUndefined()) {
+ // NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable.
+ QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, scriptSingleton));
+ if (!!o)
+ return o->get(name);
+ }
}
// Fall through to base implementation
@@ -342,21 +341,22 @@ bool QQmlTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value,
return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value);
return false;
} else if (type.isSingleton()) {
- QQmlEngine *e = scope.engine->qmlEngine();
- QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo();
- siinfo->init(e);
-
- QObject *qobjectSingleton = siinfo->qobjectApi(e);
- if (qobjectSingleton) {
- return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value);
- } else if (!siinfo->scriptApi(e).isUndefined()) {
- QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(scope.engine, siinfo->scriptApi(e)));
- if (!apiprivate) {
- QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"');
- scope.engine->throwError(error);
- return false;
- } else {
- return apiprivate->put(name, value);
+ QQmlEnginePrivate *e = QQmlEnginePrivate::get(scope.engine->qmlEngine());
+ if (type.isQObjectSingleton() || type.isCompositeSingleton()) {
+ if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type))
+ return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value);
+
+ } else {
+ QJSValue scriptSingleton = e->singletonInstance<QJSValue>(type);
+ if (!scriptSingleton.isUndefined()) {
+ QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(scope.engine, scriptSingleton));
+ if (!apiprivate) {
+ QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"');
+ scope.engine->throwError(error);
+ return false;
+ } else {
+ return apiprivate->put(name, value);
+ }
}
}
}
@@ -419,7 +419,7 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const
return Encode(false);
QQmlRefPointer<QQmlTypeData> td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl());
- CompiledData::CompilationUnit *cu = td->compilationUnit();
+ ExecutableCompilationUnit *cu = td->compilationUnit();
myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId);
} else {
myQmlType = qenginepriv->metaObjectForType(myTypeId);
@@ -448,27 +448,25 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object,
if (type.isValid()) {
if (type.isSingleton()) {
- QQmlEngine *e = engine->qmlEngine();
- QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo();
- siinfo->init(e);
-
- QObject *qobjectSingleton = siinfo->qobjectApi(e);
- if (qobjectSingleton) {
-
- const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums;
- if (!includeEnums || !name->startsWithUpper()) {
- QQmlData *ddata = QQmlData::get(qobjectSingleton, false);
- if (ddata && ddata->propertyCache) {
- QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext);
- if (property) {
- ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton)));
- lookup->qobjectLookup.qmlTypeIc = This->internalClass();
- lookup->qobjectLookup.ic = val->objectValue()->internalClass();
- lookup->qobjectLookup.propertyCache = ddata->propertyCache;
- lookup->qobjectLookup.propertyCache->addref();
- lookup->qobjectLookup.propertyData = property;
- lookup->getter = QQmlTypeWrapper::lookupSingletonProperty;
- return lookup->getter(lookup, engine, *object);
+ QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine->qmlEngine());
+ if (type.isQObjectSingleton() || type.isCompositeSingleton()) {
+ if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) {
+ const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums;
+ if (!includeEnums || !name->startsWithUpper()) {
+ QQmlData *ddata = QQmlData::get(qobjectSingleton, false);
+ if (ddata && ddata->propertyCache) {
+ QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext);
+ if (property) {
+ ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton)));
+ lookup->qobjectLookup.qmlTypeIc = This->internalClass();
+ lookup->qobjectLookup.ic = val->objectValue()->internalClass();
+ lookup->qobjectLookup.propertyCache = ddata->propertyCache;
+ lookup->qobjectLookup.propertyCache->addref();
+ lookup->qobjectLookup.propertyData = property;
+ lookup->getter = QQmlTypeWrapper::lookupSingletonProperty;
+ return lookup->getter(lookup, engine, *object);
+ }
+ // Fall through to base implementation
}
// Fall through to base implementation
}
@@ -478,6 +476,34 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object,
}
// Fall through to base implementation
}
+
+ if (name->startsWithUpper()) {
+ bool ok = false;
+ int value = type.enumValue(QQmlEnginePrivate::get(engine->qmlEngine()), name, &ok);
+ if (ok) {
+ lookup->qmlEnumValueLookup.ic = This->internalClass();
+ lookup->qmlEnumValueLookup.encodedEnumValue
+ = QV4::Value::fromInt32(value).asReturnedValue();
+ lookup->getter = QQmlTypeWrapper::lookupEnumValue;
+ return lookup->getter(lookup, engine, *object);
+ }
+
+ value = type.scopedEnumIndex(QQmlEnginePrivate::get(engine->qmlEngine()), name, &ok);
+ if (ok) {
+ Scoped<QQmlScopedEnumWrapper> enumWrapper(
+ scope, engine->memoryManager->allocate<QQmlScopedEnumWrapper>());
+ enumWrapper->d()->typePrivate = type.priv();
+ QQmlType::refHandle(enumWrapper->d()->typePrivate);
+ enumWrapper->d()->scopeEnumIndex = value;
+
+ lookup->qmlScopedEnumWrapperLookup.ic = This->internalClass();
+ lookup->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper
+ = static_cast<Heap::Object*>(enumWrapper->heapObject());
+ lookup->getter = QQmlTypeWrapper::lookupScopedEnum;
+ return enumWrapper.asReturnedValue();
+ }
+ // Fall through to base implementation
+ }
// Fall through to base implementation
}
return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
@@ -509,22 +535,46 @@ ReturnedValue QQmlTypeWrapper::lookupSingletonProperty(Lookup *l, ExecutionEngin
if (!type.isValid())
return revertLookup();
- if (!type.isSingleton())
+ if (!type.isQObjectSingleton() && !type.isCompositeSingleton())
return revertLookup();
- QQmlEngine *e = engine->qmlEngine();
- QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo();
- siinfo->init(e);
-
- QObject *qobjectSingleton = siinfo->qobjectApi(e);
- if (!qobjectSingleton)
- return revertLookup();
+ QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine->qmlEngine());
+ QObject *qobjectSingleton = e->singletonInstance<QObject *>(type);
+ Q_ASSERT(qobjectSingleton);
Scope scope(engine);
ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, qobjectSingleton));
return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup);
}
+ReturnedValue QQmlTypeWrapper::lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base)
+{
+ auto *o = static_cast<Heap::Object *>(base.heapObject());
+ if (!o || o->internalClass != l->qmlEnumValueLookup.ic) {
+ l->getter = Lookup::getterGeneric;
+ return Lookup::getterGeneric(l, engine, base);
+ }
+
+ return l->qmlEnumValueLookup.encodedEnumValue;
+}
+
+ReturnedValue QQmlTypeWrapper::lookupScopedEnum(Lookup *l, ExecutionEngine *engine, const Value &base)
+{
+ Scope scope(engine);
+ Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, static_cast<Heap::QQmlScopedEnumWrapper *>(
+ l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper));
+
+ auto *o = static_cast<Heap::Object *>(base.heapObject());
+ if (!o || o->internalClass != l->qmlScopedEnumWrapperLookup.ic) {
+ QQmlType::derefHandle(enumWrapper->d()->typePrivate);
+ l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper = nullptr;
+ l->getter = Lookup::getterGeneric;
+ return Lookup::getterGeneric(l, engine, base);
+ }
+
+ return enumWrapper.asReturnedValue();
+}
+
void Heap::QQmlScopedEnumWrapper::destroy()
{
QQmlType::derefHandle(typePrivate);
diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h
index 5f3cb2523f..7dc3f55310 100644
--- a/src/qml/qml/qqmltypewrapper_p.h
+++ b/src/qml/qml/qqmltypewrapper_p.h
@@ -81,7 +81,7 @@ struct QQmlTypeWrapper : Object {
QQmlType type() const;
- QQmlTypePrivate *typePrivate;
+ const QQmlTypePrivate *typePrivate;
QQmlTypeNameCache *typeNamespace;
const QQmlImportRef *importNamespace;
};
@@ -90,7 +90,7 @@ struct QQmlScopedEnumWrapper : Object {
void init() { Object::init(); }
void destroy();
int scopeEnumIndex;
- QQmlTypePrivate *typePrivate;
+ const QQmlTypePrivate *typePrivate;
QQmlType type() const;
};
@@ -115,6 +115,8 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object
static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
static ReturnedValue lookupSingletonProperty(Lookup *l, ExecutionEngine *engine, const Value &base);
+ static ReturnedValue lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base);
+ static ReturnedValue lookupScopedEnum(Lookup *l, ExecutionEngine *engine, const Value &base);
protected:
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp
index 6fd0f0d37c..a4737b3da9 100644
--- a/src/qml/qml/qqmlvaluetype.cpp
+++ b/src/qml/qml/qqmlvaluetype.cpp
@@ -38,12 +38,14 @@
****************************************************************************/
#include "qqmlvaluetype_p.h"
-#include "qqmlmetatype_p.h"
+#include <QtCore/qmutex.h>
#include <private/qqmlglobal_p.h>
#include <QtCore/qdebug.h>
#include <private/qmetaobjectbuilder_p.h>
+#if QT_CONFIG(qml_itemmodel)
#include <private/qqmlmodelindexvaluetype_p.h>
+#endif
#include <private/qmetatype_p.h>
QT_BEGIN_NAMESPACE
@@ -63,32 +65,34 @@ struct QQmlValueTypeFactoryImpl
QQmlValueType *valueTypes[QVariant::UserType];
QHash<int, QQmlValueType *> userTypes;
QMutex mutex;
+
+ QQmlValueType invalidValueType;
};
QQmlValueTypeFactoryImpl::QQmlValueTypeFactoryImpl()
{
- std::fill_n(valueTypes, int(QVariant::UserType), nullptr);
+ std::fill_n(valueTypes, int(QVariant::UserType), &invalidValueType);
+#if QT_CONFIG(qml_itemmodel)
// See types wrapped in qqmlmodelindexvaluetype_p.h
qRegisterMetaType<QItemSelectionRange>();
+#endif
}
QQmlValueTypeFactoryImpl::~QQmlValueTypeFactoryImpl()
{
- qDeleteAll(valueTypes, valueTypes + QVariant::UserType);
+ for (QQmlValueType *type : valueTypes) {
+ if (type != &invalidValueType)
+ delete type;
+ }
qDeleteAll(userTypes);
}
-bool QQmlValueTypeFactoryImpl::isValueType(int idx)
+bool isInternalType(int idx)
{
- if (idx >= QMetaType::User)
- return valueType(idx) != nullptr;
-
- if (idx < 0)
- return false;
-
// Qt internal types
switch (idx) {
+ case QMetaType::UnknownType:
case QMetaType::QStringList:
case QMetaType::QObjectStar:
case QMetaType::VoidStar:
@@ -97,12 +101,20 @@ bool QQmlValueTypeFactoryImpl::isValueType(int idx)
case QMetaType::QLocale:
case QMetaType::QImage: // scarce type, keep as QVariant
case QMetaType::QPixmap: // scarce type, keep as QVariant
- return false;
- default:
return true;
+ default:
+ return false;
}
}
+bool QQmlValueTypeFactoryImpl::isValueType(int idx)
+{
+ if (idx < 0 || isInternalType(idx))
+ return false;
+
+ return valueType(idx) != nullptr;
+}
+
const QMetaObject *QQmlValueTypeFactoryImpl::metaObjectForMetaType(int t)
{
switch (t) {
@@ -120,13 +132,17 @@ const QMetaObject *QQmlValueTypeFactoryImpl::metaObjectForMetaType(int t)
return &QQmlRectFValueType::staticMetaObject;
case QVariant::EasingCurve:
return &QQmlEasingValueType::staticMetaObject;
+#if QT_CONFIG(qml_itemmodel)
case QVariant::ModelIndex:
return &QQmlModelIndexValueType::staticMetaObject;
case QVariant::PersistentModelIndex:
return &QQmlPersistentModelIndexValueType::staticMetaObject;
+#endif
default:
+#if QT_CONFIG(qml_itemmodel)
if (t == qMetaTypeId<QItemSelectionRange>())
return &QQmlItemSelectionRangeValueType::staticMetaObject;
+#endif
if (const QMetaObject *mo = QQml_valueTypeProvider()->metaObjectForMetaType(t))
return mo;
@@ -158,15 +174,17 @@ QQmlValueType *QQmlValueTypeFactoryImpl::valueType(int idx)
}
QQmlValueType *rv = valueTypes[idx];
- if (!rv) {
+ if (rv == &invalidValueType) {
// No need for mutex protection - the most we can lose is a valueType instance
// TODO: Investigate the performance/memory characteristics of
// removing the preallocated array
- if (const QMetaObject *mo = metaObjectForMetaType(idx)) {
- rv = new QQmlValueType(idx, mo);
- valueTypes[idx] = rv;
- }
+ if (isInternalType(idx))
+ rv = valueTypes[idx] = nullptr;
+ else if (const QMetaObject *mo = metaObjectForMetaType(idx))
+ rv = valueTypes[idx] = new QQmlValueType(idx, mo);
+ else
+ rv = valueTypes[idx] = nullptr;
}
return rv;
@@ -196,6 +214,13 @@ void QQmlValueTypeFactory::registerValueTypes(const char *uri, int versionMajor,
qmlRegisterValueTypeEnums<QQmlEasingValueType>(uri, versionMajor, versionMinor, "Easing");
}
+QQmlValueType::QQmlValueType() :
+ _metaObject(nullptr),
+ gadgetPtr(nullptr),
+ metaType(QMetaType::UnknownType)
+{
+}
+
QQmlValueType::QQmlValueType(int typeId, const QMetaObject *gadgetMetaObject)
: gadgetPtr(QMetaType::create(typeId))
, metaType(typeId)
@@ -213,7 +238,7 @@ QQmlValueType::QQmlValueType(int typeId, const QMetaObject *gadgetMetaObject)
QQmlValueType::~QQmlValueType()
{
QObjectPrivate *op = QObjectPrivate::get(this);
- Q_ASSERT(op->metaObject == this);
+ Q_ASSERT(op->metaObject == nullptr || op->metaObject == this);
op->metaObject = nullptr;
::free(const_cast<QMetaObject *>(_metaObject));
metaType.destroy(gadgetPtr);
diff --git a/src/qml/qml/qqmlvaluetype_p.h b/src/qml/qml/qqmlvaluetype_p.h
index 89f1b71d61..cf53b8cb4a 100644
--- a/src/qml/qml/qqmlvaluetype_p.h
+++ b/src/qml/qml/qqmlvaluetype_p.h
@@ -66,6 +66,7 @@ QT_BEGIN_NAMESPACE
class Q_QML_PRIVATE_EXPORT QQmlValueType : public QObject, public QAbstractDynamicMetaObject
{
public:
+ QQmlValueType();
QQmlValueType(int userType, const QMetaObject *metaObject);
~QQmlValueType() override;
void read(QObject *, int);
@@ -90,7 +91,7 @@ public:
class Q_QML_PRIVATE_EXPORT QQmlValueTypeFactory
{
public:
- static bool isValueType(int);
+ static bool isValueType(int idx);
static QQmlValueType *valueType(int idx);
static const QMetaObject *metaObjectForMetaType(int type);
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index 7df5757b95..cf6553d129 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -38,7 +38,7 @@
****************************************************************************/
#include "qqmlvaluetypewrapper_p.h"
-#include <private/qv8engine_p.h>
+
#include <private/qqmlvaluetype_p.h>
#include <private/qqmlbinding_p.h>
#include <private/qqmlglobal_p.h>
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index 6bc469c836..abdc686ec2 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -57,6 +57,8 @@
#include <private/qv4scopedvalue_p.h>
#include <private/qv4jscall_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qqmlpropertycachecreator_p.h>
+#include <private/qqmlpropertycachemethodarguments_p.h>
QT_BEGIN_NAMESPACE
@@ -162,8 +164,19 @@ void QQmlVMEMetaObjectEndpoint::tryConnect()
QQmlData *targetDData = QQmlData::get(target, /*create*/false);
if (!targetDData)
return;
- int coreIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex).coreIndex();
+ QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex);
+ int coreIndex = encodedIndex.coreIndex();
+ int valueTypeIndex = encodedIndex.valueTypeIndex();
const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex);
+ if (pd && valueTypeIndex != -1 && !QQmlValueTypeFactory::valueType(pd->propType())) {
+ // deep alias
+ QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(metaObject->compilationUnit->engine->qmlEngine());
+ auto const *newPropertyCache = enginePriv->propertyCacheForType(pd->propType());
+ void *argv[1] = { &target };
+ QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, argv);
+ Q_ASSERT(newPropertyCache);
+ pd = newPropertyCache->property(valueTypeIndex);
+ }
if (!pd)
return;
@@ -316,7 +329,7 @@ QAbstractDynamicMetaObject *QQmlInterceptorMetaObject::toDynamicMetaObject(QObje
QQmlVMEMetaObject::QQmlVMEMetaObject(QV4::ExecutionEngine *engine,
QObject *obj,
- const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &qmlCompilationUnit, int qmlObjectId)
+ const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &qmlCompilationUnit, int qmlObjectId)
: QQmlInterceptorMetaObject(obj, cache),
engine(engine),
ctxt(QQmlData::get(obj, true)->outerContext),
@@ -594,7 +607,7 @@ QList<QObject *> *QQmlVMEMetaObject::readPropertyAsList(int id) const
QV4::Scope scope(engine);
QV4::Scoped<QV4::VariantObject> v(scope, *(md->data() + id));
if (!v || (int)v->d()->data().userType() != qMetaTypeId<QList<QObject *> >()) {
- QVariant variant(qVariantFromValue(QList<QObject*>()));
+ QVariant variant(QVariant::fromValue(QList<QObject*>()));
v = engine->newVariantObject(variant);
md->set(engine, id, v);
}
@@ -638,205 +651,170 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
id -= propOffset();
if (id < propertyCount) {
- const QV4::CompiledData::Property::Type t = static_cast<QV4::CompiledData::Property::Type>(qint32(compiledObject->propertyTable()[id].type));
- bool needActivate = false;
-
- if (t == QV4::CompiledData::Property::Var) {
- // the context can be null if accessing var properties from cpp after re-parenting an item.
- QQmlEnginePrivate *ep = (ctxt == nullptr || ctxt->engine == nullptr) ? nullptr : QQmlEnginePrivate::get(ctxt->engine);
- if (ep) {
- if (c == QMetaObject::ReadProperty) {
- *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id);
- } else if (c == QMetaObject::WriteProperty) {
- writeProperty(id, *reinterpret_cast<QVariant *>(a[0]));
- }
- } else if (c == QMetaObject::ReadProperty) {
- // if the context was disposed, we just return an invalid variant from read.
- *reinterpret_cast<QVariant *>(a[0]) = QVariant();
- }
+ const QV4::CompiledData::Property &property = compiledObject->propertyTable()[id];
+ const QV4::CompiledData::BuiltinType t = property.builtinType();
- } else {
- int fallbackMetaType = QMetaType::UnknownType;
+ // the context can be null if accessing var properties from cpp after re-parenting an item.
+ QQmlEnginePrivate *ep = (ctxt == nullptr || ctxt->engine == nullptr) ? nullptr : QQmlEnginePrivate::get(ctxt->engine);
+
+ const int fallbackMetaType = QQmlPropertyCacheCreatorBase::metaTypeForPropertyType(t);
+
+ if (c == QMetaObject::ReadProperty) {
switch (t) {
- case QV4::CompiledData::Property::Font:
- fallbackMetaType = QMetaType::QFont;
+ case QV4::CompiledData::BuiltinType::Int:
+ *reinterpret_cast<int *>(a[0]) = readPropertyAsInt(id);
break;
- case QV4::CompiledData::Property::Time:
- fallbackMetaType = QMetaType::QTime;
+ case QV4::CompiledData::BuiltinType::Bool:
+ *reinterpret_cast<bool *>(a[0]) = readPropertyAsBool(id);
break;
- case QV4::CompiledData::Property::Color:
- fallbackMetaType = QMetaType::QColor;
+ case QV4::CompiledData::BuiltinType::Real:
+ *reinterpret_cast<double *>(a[0]) = readPropertyAsDouble(id);
break;
- case QV4::CompiledData::Property::Vector2D:
- fallbackMetaType = QMetaType::QVector2D;
+ case QV4::CompiledData::BuiltinType::String:
+ *reinterpret_cast<QString *>(a[0]) = readPropertyAsString(id);
break;
- case QV4::CompiledData::Property::Vector3D:
- fallbackMetaType = QMetaType::QVector3D;
+ case QV4::CompiledData::BuiltinType::Url:
+ *reinterpret_cast<QUrl *>(a[0]) = readPropertyAsUrl(id);
break;
- case QV4::CompiledData::Property::Vector4D:
- fallbackMetaType = QMetaType::QVector4D;
+ case QV4::CompiledData::BuiltinType::Date:
+ *reinterpret_cast<QDate *>(a[0]) = readPropertyAsDate(id);
break;
- case QV4::CompiledData::Property::Matrix4x4:
- fallbackMetaType = QMetaType::QMatrix4x4;
+ case QV4::CompiledData::BuiltinType::DateTime:
+ *reinterpret_cast<QDateTime *>(a[0]) = readPropertyAsDateTime(id);
break;
- case QV4::CompiledData::Property::Quaternion:
- fallbackMetaType = QMetaType::QQuaternion;
+ case QV4::CompiledData::BuiltinType::Rect:
+ *reinterpret_cast<QRectF *>(a[0]) = readPropertyAsRectF(id);
break;
- default: break;
- }
-
-
- if (c == QMetaObject::ReadProperty) {
- switch (t) {
- case QV4::CompiledData::Property::Int:
- *reinterpret_cast<int *>(a[0]) = readPropertyAsInt(id);
- break;
- case QV4::CompiledData::Property::Bool:
- *reinterpret_cast<bool *>(a[0]) = readPropertyAsBool(id);
- break;
- case QV4::CompiledData::Property::Real:
- *reinterpret_cast<double *>(a[0]) = readPropertyAsDouble(id);
- break;
- case QV4::CompiledData::Property::String:
- *reinterpret_cast<QString *>(a[0]) = readPropertyAsString(id);
- break;
- case QV4::CompiledData::Property::Url:
- *reinterpret_cast<QUrl *>(a[0]) = readPropertyAsUrl(id);
- break;
- case QV4::CompiledData::Property::Date:
- *reinterpret_cast<QDate *>(a[0]) = readPropertyAsDate(id);
- break;
- case QV4::CompiledData::Property::DateTime:
- *reinterpret_cast<QDateTime *>(a[0]) = readPropertyAsDateTime(id);
- break;
- case QV4::CompiledData::Property::Rect:
- *reinterpret_cast<QRectF *>(a[0]) = readPropertyAsRectF(id);
- break;
- case QV4::CompiledData::Property::Size:
- *reinterpret_cast<QSizeF *>(a[0]) = readPropertyAsSizeF(id);
- break;
- case QV4::CompiledData::Property::Point:
- *reinterpret_cast<QPointF *>(a[0]) = readPropertyAsPointF(id);
- break;
- case QV4::CompiledData::Property::Custom:
- *reinterpret_cast<QObject **>(a[0]) = readPropertyAsQObject(id);
- break;
- case QV4::CompiledData::Property::Variant:
+ case QV4::CompiledData::BuiltinType::Size:
+ *reinterpret_cast<QSizeF *>(a[0]) = readPropertyAsSizeF(id);
+ break;
+ case QV4::CompiledData::BuiltinType::Point:
+ *reinterpret_cast<QPointF *>(a[0]) = readPropertyAsPointF(id);
+ break;
+ case QV4::CompiledData::BuiltinType::Variant:
+ *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id);
+ break;
+ case QV4::CompiledData::BuiltinType::Font:
+ case QV4::CompiledData::BuiltinType::Time:
+ case QV4::CompiledData::BuiltinType::Color:
+ case QV4::CompiledData::BuiltinType::Vector2D:
+ case QV4::CompiledData::BuiltinType::Vector3D:
+ case QV4::CompiledData::BuiltinType::Vector4D:
+ case QV4::CompiledData::BuiltinType::Matrix4x4:
+ case QV4::CompiledData::BuiltinType::Quaternion:
+ Q_ASSERT(fallbackMetaType != QMetaType::UnknownType);
+ if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
+ QVariant propertyAsVariant;
+ if (const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>())
+ propertyAsVariant = v->d()->data();
+ QQml_valueTypeProvider()->readValueType(propertyAsVariant, a[0], fallbackMetaType);
+ }
+ break;
+ case QV4::CompiledData::BuiltinType::Var:
+ if (ep) {
*reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id);
- break;
- case QV4::CompiledData::Property::CustomList: {
+ } else {
+ // if the context was disposed, we just return an invalid variant from read.
+ *reinterpret_cast<QVariant *>(a[0]) = QVariant();
+ }
+ break;
+ case QV4::CompiledData::BuiltinType::InvalidBuiltin:
+ if (property.isList) {
QList<QObject *> *list = readPropertyAsList(id);
QQmlListProperty<QObject> *p = static_cast<QQmlListProperty<QObject> *>(a[0]);
*p = QQmlListProperty<QObject>(object, list,
- list_append, list_count, list_at,
- list_clear);
+ list_append, list_count, list_at,
+ list_clear);
p->dummy1 = this;
p->dummy2 = reinterpret_cast<void *>(quintptr(methodOffset() + id));
- break;
+ } else {
+ *reinterpret_cast<QObject **>(a[0]) = readPropertyAsQObject(id);
}
- case QV4::CompiledData::Property::Font:
- case QV4::CompiledData::Property::Time:
- case QV4::CompiledData::Property::Color:
- case QV4::CompiledData::Property::Vector2D:
- case QV4::CompiledData::Property::Vector3D:
- case QV4::CompiledData::Property::Vector4D:
- case QV4::CompiledData::Property::Matrix4x4:
- case QV4::CompiledData::Property::Quaternion:
- Q_ASSERT(fallbackMetaType != QMetaType::UnknownType);
- if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
- QVariant propertyAsVariant;
- if (const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>())
- propertyAsVariant = v->d()->data();
- QQml_valueTypeProvider()->readValueType(propertyAsVariant, a[0], fallbackMetaType);
+ }
+
+ } else if (c == QMetaObject::WriteProperty) {
+ bool needActivate = false;
+ switch (t) {
+ case QV4::CompiledData::BuiltinType::Int:
+ needActivate = *reinterpret_cast<int *>(a[0]) != readPropertyAsInt(id);
+ writeProperty(id, *reinterpret_cast<int *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::Bool:
+ needActivate = *reinterpret_cast<bool *>(a[0]) != readPropertyAsBool(id);
+ writeProperty(id, *reinterpret_cast<bool *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::Real:
+ needActivate = *reinterpret_cast<double *>(a[0]) != readPropertyAsDouble(id);
+ writeProperty(id, *reinterpret_cast<double *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::String:
+ needActivate = *reinterpret_cast<QString *>(a[0]) != readPropertyAsString(id);
+ writeProperty(id, *reinterpret_cast<QString *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::Url:
+ needActivate = *reinterpret_cast<QUrl *>(a[0]) != readPropertyAsUrl(id);
+ writeProperty(id, *reinterpret_cast<QUrl *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::Date:
+ needActivate = *reinterpret_cast<QDate *>(a[0]) != readPropertyAsDate(id);
+ writeProperty(id, *reinterpret_cast<QDate *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::DateTime:
+ needActivate = *reinterpret_cast<QDateTime *>(a[0]) != readPropertyAsDateTime(id);
+ writeProperty(id, *reinterpret_cast<QDateTime *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::Rect:
+ needActivate = *reinterpret_cast<QRectF *>(a[0]) != readPropertyAsRectF(id);
+ writeProperty(id, *reinterpret_cast<QRectF *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::Size:
+ needActivate = *reinterpret_cast<QSizeF *>(a[0]) != readPropertyAsSizeF(id);
+ writeProperty(id, *reinterpret_cast<QSizeF *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::Point:
+ needActivate = *reinterpret_cast<QPointF *>(a[0]) != readPropertyAsPointF(id);
+ writeProperty(id, *reinterpret_cast<QPointF *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::Variant:
+ writeProperty(id, *reinterpret_cast<QVariant *>(a[0]));
+ break;
+ case QV4::CompiledData::BuiltinType::Font:
+ case QV4::CompiledData::BuiltinType::Time:
+ case QV4::CompiledData::BuiltinType::Color:
+ case QV4::CompiledData::BuiltinType::Vector2D:
+ case QV4::CompiledData::BuiltinType::Vector3D:
+ case QV4::CompiledData::BuiltinType::Vector4D:
+ case QV4::CompiledData::BuiltinType::Matrix4x4:
+ case QV4::CompiledData::BuiltinType::Quaternion:
+ Q_ASSERT(fallbackMetaType != QMetaType::UnknownType);
+ if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
+ const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>();
+ if (!v) {
+ md->set(engine, id, engine->newVariantObject(QVariant()));
+ v = (md->data() + id)->as<QV4::VariantObject>();
+ QQml_valueTypeProvider()->initValueType(fallbackMetaType, v->d()->data());
}
- break;
- case QV4::CompiledData::Property::Var:
- Q_UNREACHABLE();
+ needActivate = !QQml_valueTypeProvider()->equalValueType(fallbackMetaType, a[0], v->d()->data());
+ QQml_valueTypeProvider()->writeValueType(fallbackMetaType, a[0], v->d()->data());
}
-
- } else if (c == QMetaObject::WriteProperty) {
-
- switch(t) {
- case QV4::CompiledData::Property::Int:
- needActivate = *reinterpret_cast<int *>(a[0]) != readPropertyAsInt(id);
- writeProperty(id, *reinterpret_cast<int *>(a[0]));
- break;
- case QV4::CompiledData::Property::Bool:
- needActivate = *reinterpret_cast<bool *>(a[0]) != readPropertyAsBool(id);
- writeProperty(id, *reinterpret_cast<bool *>(a[0]));
- break;
- case QV4::CompiledData::Property::Real:
- needActivate = *reinterpret_cast<double *>(a[0]) != readPropertyAsDouble(id);
- writeProperty(id, *reinterpret_cast<double *>(a[0]));
- break;
- case QV4::CompiledData::Property::String:
- needActivate = *reinterpret_cast<QString *>(a[0]) != readPropertyAsString(id);
- writeProperty(id, *reinterpret_cast<QString *>(a[0]));
- break;
- case QV4::CompiledData::Property::Url:
- needActivate = *reinterpret_cast<QUrl *>(a[0]) != readPropertyAsUrl(id);
- writeProperty(id, *reinterpret_cast<QUrl *>(a[0]));
- break;
- case QV4::CompiledData::Property::Date:
- needActivate = *reinterpret_cast<QDate *>(a[0]) != readPropertyAsDate(id);
- writeProperty(id, *reinterpret_cast<QDate *>(a[0]));
- break;
- case QV4::CompiledData::Property::DateTime:
- needActivate = *reinterpret_cast<QDateTime *>(a[0]) != readPropertyAsDateTime(id);
- writeProperty(id, *reinterpret_cast<QDateTime *>(a[0]));
- break;
- case QV4::CompiledData::Property::Rect:
- needActivate = *reinterpret_cast<QRectF *>(a[0]) != readPropertyAsRectF(id);
- writeProperty(id, *reinterpret_cast<QRectF *>(a[0]));
- break;
- case QV4::CompiledData::Property::Size:
- needActivate = *reinterpret_cast<QSizeF *>(a[0]) != readPropertyAsSizeF(id);
- writeProperty(id, *reinterpret_cast<QSizeF *>(a[0]));
- break;
- case QV4::CompiledData::Property::Point:
- needActivate = *reinterpret_cast<QPointF *>(a[0]) != readPropertyAsPointF(id);
- writeProperty(id, *reinterpret_cast<QPointF *>(a[0]));
- break;
- case QV4::CompiledData::Property::Custom:
- needActivate = *reinterpret_cast<QObject **>(a[0]) != readPropertyAsQObject(id);
- writeProperty(id, *reinterpret_cast<QObject **>(a[0]));
- break;
- case QV4::CompiledData::Property::Variant:
+ break;
+ case QV4::CompiledData::BuiltinType::Var:
+ if (ep)
writeProperty(id, *reinterpret_cast<QVariant *>(a[0]));
- break;
- case QV4::CompiledData::Property::CustomList:
+ break;
+ case QV4::CompiledData::BuiltinType::InvalidBuiltin:
+ if (property.isList) {
// Writing such a property is not supported. Content is added through the list property
// methods.
- break;
- case QV4::CompiledData::Property::Font:
- case QV4::CompiledData::Property::Time:
- case QV4::CompiledData::Property::Color:
- case QV4::CompiledData::Property::Vector2D:
- case QV4::CompiledData::Property::Vector3D:
- case QV4::CompiledData::Property::Vector4D:
- case QV4::CompiledData::Property::Matrix4x4:
- case QV4::CompiledData::Property::Quaternion:
- Q_ASSERT(fallbackMetaType != QMetaType::UnknownType);
- if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
- const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>();
- if (!v) {
- md->set(engine, id, engine->newVariantObject(QVariant()));
- v = (md->data() + id)->as<QV4::VariantObject>();
- QQml_valueTypeProvider()->initValueType(fallbackMetaType, v->d()->data());
- }
- needActivate = !QQml_valueTypeProvider()->equalValueType(fallbackMetaType, a[0], v->d()->data());
- QQml_valueTypeProvider()->writeValueType(fallbackMetaType, a[0], v->d()->data());
- }
- break;
- case QV4::CompiledData::Property::Var:
- Q_UNREACHABLE();
+ } else {
+ needActivate = *reinterpret_cast<QObject **>(a[0]) != readPropertyAsQObject(id);
+ writeProperty(id, *reinterpret_cast<QObject **>(a[0]));
}
- }
- }
+ }
- if (c == QMetaObject::WriteProperty && needActivate) {
- activate(object, methodOffset() + id, nullptr);
+ if (needActivate)
+ activate(object, methodOffset() + id, nullptr);
}
return -1;
@@ -891,17 +869,23 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
if (!targetDData->propertyCache)
return -1;
const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex);
- // Value type property
+ // Value type property or deep alias
QQmlValueType *valueType = QQmlValueTypeFactory::valueType(pd->propType());
- Q_ASSERT(valueType);
+ if (valueType) {
- valueType->read(target, coreIndex);
- int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a);
+ valueType->read(target, coreIndex);
+ int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a);
- if (c == QMetaObject::WriteProperty)
- valueType->write(target, coreIndex, nullptr);
+ if (c == QMetaObject::WriteProperty)
+ valueType->write(target, coreIndex, nullptr);
- return rv;
+ return rv;
+ } else {
+ // deep alias
+ void *argv[1] = { &target };
+ QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, argv);
+ return QMetaObject::metacall(target, c, valueTypePropertyIndex, a);
+ }
} else {
return QMetaObject::metacall(target, c, coreIndex, a);
@@ -951,21 +935,38 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
return -1; // The dynamic method with that id is not available.
}
- const unsigned int parameterCount = function->formalParameterCount();
+ auto methodData = cache->method(_id);
+ auto arguments = methodData->hasArguments() ? methodData->arguments() : nullptr;
+
+ const unsigned int parameterCount = (arguments && arguments->names) ? arguments->names->count() : 0;
+ Q_ASSERT(parameterCount == function->formalParameterCount());
+
QV4::JSCallData jsCallData(scope, parameterCount);
*jsCallData->thisObject = v4->global();
- for (uint ii = 0; ii < parameterCount; ++ii)
- jsCallData->args[ii] = scope.engine->fromVariant(*(QVariant *)a[ii + 1]);
+ for (uint ii = 0; ii < parameterCount; ++ii) {
+ jsCallData->args[ii] = scope.engine->metaTypeToJS(arguments->arguments[ii + 1], a[ii + 1]);
+ }
+ const int returnType = methodData->propType();
QV4::ScopedValue result(scope, function->call(jsCallData));
if (scope.hasException()) {
QQmlError error = scope.engine->catchExceptionAsQmlError();
if (error.isValid())
ep->warning(error);
- if (a[0]) *(QVariant *)a[0] = QVariant();
+ if (a[0]) {
+ QMetaType::destruct(returnType, a[0]);
+ QMetaType::construct(returnType, a[0], nullptr);
+ }
} else {
- if (a[0]) *(QVariant *)a[0] = scope.engine->toVariant(result, 0);
+ if (a[0]) {
+ // When the return type is QVariant, JS objects are to be returned as QJSValue wrapped in
+ // QVariant.
+ if (returnType == QMetaType::QVariant)
+ *(QVariant *)a[0] = scope.engine->toVariant(result, 0);
+ else
+ scope.engine->metaTypeFromJS(result, returnType, a[0]);
+ }
}
ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
@@ -1000,7 +1001,7 @@ QV4::ReturnedValue QQmlVMEMetaObject::method(int index) const
QV4::ReturnedValue QQmlVMEMetaObject::readVarProperty(int id) const
{
- Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].type == QV4::CompiledData::Property::Var);
+ Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].builtinType() == QV4::CompiledData::BuiltinType::Var);
QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
@@ -1025,7 +1026,7 @@ QVariant QQmlVMEMetaObject::readPropertyAsVariant(int id) const
void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value)
{
- Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].type == QV4::CompiledData::Property::Var);
+ Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].builtinType() == QV4::CompiledData::BuiltinType::Var);
QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
@@ -1065,7 +1066,7 @@ void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value)
void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value)
{
- if (compiledObject && compiledObject->propertyTable()[id].type == QV4::CompiledData::Property::Var) {
+ if (compiledObject && compiledObject->propertyTable()[id].builtinType() == QV4::CompiledData::BuiltinType::Var) {
QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return;
diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h
index dbcc9d2884..5025987586 100644
--- a/src/qml/qml/qqmlvmemetaobject_p.h
+++ b/src/qml/qml/qqmlvmemetaobject_p.h
@@ -65,9 +65,7 @@
#include "qqmlguard_p.h"
#include "qqmlcontext_p.h"
-#include "qqmlpropertycache_p.h"
-#include <private/qv8engine_p.h>
#include <private/qflagpointer_p.h>
#include <private/qv4object_p.h>
@@ -146,7 +144,7 @@ class QQmlVMEMetaObjectEndpoint;
class Q_QML_PRIVATE_EXPORT QQmlVMEMetaObject : public QQmlInterceptorMetaObject
{
public:
- QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &qmlCompilationUnit, int qmlObjectId);
+ QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &qmlCompilationUnit, int qmlObjectId);
~QQmlVMEMetaObject() override;
bool aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const;
@@ -228,7 +226,7 @@ public:
// keep a reference to the compilation unit in order to still
// do property access when the context has been invalidated.
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
const QV4::CompiledData::Object *compiledObject;
};
diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp
index 9877cc027f..4db0562c0e 100644
--- a/src/qml/qml/qqmlxmlhttprequest.cpp
+++ b/src/qml/qml/qqmlxmlhttprequest.cpp
@@ -39,8 +39,6 @@
#include "qqmlxmlhttprequest_p.h"
-#include <private/qv8engine_p.h>
-
#include "qqmlengine.h"
#include "qqmlengine_p.h"
#include <private/qqmlrefcount_p.h>
@@ -70,8 +68,6 @@
using namespace QV4;
-#if QT_CONFIG(xmlstreamreader) && QT_CONFIG(qml_network)
-
#define V4THROW_REFERENCE(string) \
do { \
ScopedObject error(scope, scope.engine->newReferenceErrorObject(QStringLiteral(string))); \
@@ -99,7 +95,7 @@ struct QQmlXMLHttpRequestData {
static inline QQmlXMLHttpRequestData *xhrdata(ExecutionEngine *v4)
{
- return (QQmlXMLHttpRequestData *)v4->v8Engine->xmlHttpRequestData();
+ return (QQmlXMLHttpRequestData *)v4->xmlHttpRequestData();
}
QQmlXMLHttpRequestData::QQmlXMLHttpRequestData()
@@ -595,7 +591,7 @@ ReturnedValue NodePrototype::getProto(ExecutionEngine *v4)
if (d->nodePrototype.isUndefined()) {
ScopedObject p(scope, v4->memoryManager->allocate<NodePrototype>());
d->nodePrototype.set(v4, p);
- v4->v8Engine->freezeObject(p);
+ v4->freezeObject(p);
}
return d->nodePrototype.value();
}
@@ -644,7 +640,7 @@ ReturnedValue Element::prototype(ExecutionEngine *engine)
p->setPrototypeUnchecked((pp = NodePrototype::getProto(engine)));
p->defineAccessorProperty(QStringLiteral("tagName"), NodePrototype::method_get_nodeName, nullptr);
d->elementPrototype.set(engine, p);
- engine->v8Engine->freezeObject(p);
+ engine->freezeObject(p);
}
return d->elementPrototype.value();
}
@@ -661,7 +657,7 @@ ReturnedValue Attr::prototype(ExecutionEngine *engine)
p->defineAccessorProperty(QStringLiteral("value"), method_value, nullptr);
p->defineAccessorProperty(QStringLiteral("ownerElement"), method_ownerElement, nullptr);
d->attrPrototype.set(engine, p);
- engine->v8Engine->freezeObject(p);
+ engine->freezeObject(p);
}
return d->attrPrototype.value();
}
@@ -717,7 +713,7 @@ ReturnedValue CharacterData::prototype(ExecutionEngine *v4)
p->defineAccessorProperty(QStringLiteral("data"), NodePrototype::method_get_nodeValue, nullptr);
p->defineAccessorProperty(QStringLiteral("length"), method_length, nullptr);
d->characterDataPrototype.set(v4, p);
- v4->v8Engine->freezeObject(p);
+ v4->freezeObject(p);
}
return d->characterDataPrototype.value();
}
@@ -753,7 +749,7 @@ ReturnedValue Text::prototype(ExecutionEngine *v4)
p->defineAccessorProperty(QStringLiteral("isElementContentWhitespace"), method_isElementContentWhitespace, nullptr);
p->defineAccessorProperty(QStringLiteral("wholeText"), method_wholeText, nullptr);
d->textPrototype.set(v4, p);
- v4->v8Engine->freezeObject(p);
+ v4->freezeObject(p);
}
return d->textPrototype.value();
}
@@ -768,7 +764,7 @@ ReturnedValue CDATA::prototype(ExecutionEngine *v4)
ScopedObject pp(scope);
p->setPrototypeUnchecked((pp = Text::prototype(v4)));
d->cdataPrototype.set(v4, p);
- v4->v8Engine->freezeObject(p);
+ v4->freezeObject(p);
}
return d->cdataPrototype.value();
}
@@ -786,7 +782,7 @@ ReturnedValue Document::prototype(ExecutionEngine *v4)
p->defineAccessorProperty(QStringLiteral("xmlStandalone"), method_xmlStandalone, nullptr);
p->defineAccessorProperty(QStringLiteral("documentElement"), method_documentElement, nullptr);
d->documentPrototype.set(v4, p);
- v4->v8Engine->freezeObject(p);
+ v4->freezeObject(p);
}
return d->documentPrototype.value();
}
@@ -1651,7 +1647,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject
Scope scope(f->engine());
const QQmlXMLHttpRequestCtor *ctor = static_cast<const QQmlXMLHttpRequestCtor *>(f);
- QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager(), scope.engine);
+ QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->qmlEngine()->networkAccessManager(), scope.engine);
Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->allocate<QQmlXMLHttpRequestWrapper>(r));
ScopedObject proto(scope, ctor->d()->proto);
w->setPrototypeUnchecked(proto);
@@ -2073,6 +2069,4 @@ void *qt_add_qmlxmlhttprequest(ExecutionEngine *v4)
QT_END_NAMESPACE
-#endif // xmlstreamreader && qml_network
-
#include <qqmlxmlhttprequest.moc>
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
index 4422195e3d..d634a48443 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
@@ -40,6 +40,7 @@
#include "qqmlbuiltinfunctions_p.h"
#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlfile.h>
#include <private/qqmlengine_p.h>
#include <private/qqmlcomponent_p.h>
#include <private/qqmlloggingcategory_p.h>
@@ -47,7 +48,6 @@
#if QT_CONFIG(qml_locale)
#include <private/qqmllocale_p.h>
#endif
-#include <private/qv8engine_p.h>
#include <private/qqmldelayedcallqueue_p.h>
#include <QFileInfo>
@@ -439,7 +439,7 @@ ReturnedValue QtObject::method_font(const FunctionObject *b, const Value *, cons
QV4::ExecutionEngine *v4 = scope.engine;
bool ok = false;
- QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QFont, QQmlV4Handle(argv[0]), v4, &ok);
+ QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QFont, argv[0], v4, &ok);
if (!ok)
THROW_GENERIC_ERROR("Qt.font(): Invalid argument: no valid font subproperties specified");
return scope.engine->fromVariant(v);
@@ -559,7 +559,7 @@ ReturnedValue QtObject::method_matrix4x4(const FunctionObject *b, const Value *,
if (argc == 1 && argv[0].isObject()) {
bool ok = false;
- QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QMatrix4x4, QQmlV4Handle(argv[0]), scope.engine, &ok);
+ QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QMatrix4x4, argv[0], scope.engine, &ok);
if (!ok)
THROW_GENERIC_ERROR("Qt.matrix4x4(): Invalid argument: not a valid matrix4x4 values array");
return scope.engine->fromVariant(v);
@@ -1710,10 +1710,8 @@ ReturnedValue ConsoleObject::method_time(const FunctionObject *b, const Value *,
if (argc != 1)
THROW_GENERIC_ERROR("console.time(): Invalid arguments");
- QV8Engine *v8engine = scope.engine->v8Engine;
-
QString name = argv[0].toQStringNoThrow();
- v8engine->startTimer(name);
+ scope.engine->startTimer(name);
return QV4::Encode::undefined();
}
@@ -1723,11 +1721,9 @@ ReturnedValue ConsoleObject::method_timeEnd(const FunctionObject *b, const Value
if (argc != 1)
THROW_GENERIC_ERROR("console.timeEnd(): Invalid arguments");
- QV8Engine *v8engine = scope.engine->v8Engine;
-
QString name = argv[0].toQStringNoThrow();
bool wasRunning;
- qint64 elapsed = v8engine->stopTimer(name, &wasRunning);
+ qint64 elapsed = scope.engine->stopTimer(name, &wasRunning);
if (wasRunning) {
qDebug("%s: %llims", qPrintable(name), elapsed);
}
@@ -1743,13 +1739,12 @@ ReturnedValue ConsoleObject::method_count(const FunctionObject *b, const Value *
Scope scope(b);
QV4::ExecutionEngine *v4 = scope.engine;
- QV8Engine *v8engine = scope.engine->v8Engine;
QV4::CppStackFrame *frame = v4->currentStackFrame;
QString scriptName = frame->source();
- int value = v8engine->consoleCountHelper(scriptName, frame->lineNumber(), 0);
+ int value = v4->consoleCountHelper(scriptName, frame->lineNumber(), 0);
QString message = name + QLatin1String(": ") + QString::number(value);
QMessageLogger(qPrintable(scriptName), frame->lineNumber(),
@@ -1972,29 +1967,32 @@ ReturnedValue GlobalExtensions::method_qsTr(const FunctionObject *b, const Value
THROW_GENERIC_ERROR("qsTr(): third argument (n) must be a number");
QString context;
- if (QQmlContextData *ctxt = scope.engine->callingQmlContext()) {
- QString path = ctxt->urlString();
- int lastSlash = path.lastIndexOf(QLatin1Char('/'));
- int lastDot = path.lastIndexOf(QLatin1Char('.'));
- int length = lastDot - (lastSlash + 1);
- context = (lastSlash > -1) ? path.mid(lastSlash + 1, (length > -1) ? length : -1) : QString();
- } else {
- CppStackFrame *frame = scope.engine->currentStackFrame;
- // The first non-empty source URL in the call stack determines the translation context.
- while (frame && context.isEmpty()) {
- if (CompiledData::CompilationUnit *unit = frame->v4Function->compilationUnit) {
- QString fileName = unit->fileName();
- QUrl url(unit->fileName());
- if (url.isValid() && url.isRelative()) {
- context = url.fileName();
- } else {
- context = QQmlFile::urlToLocalFileOrQrc(fileName);
- if (context.isEmpty() && fileName.startsWith(QLatin1String(":/")))
- context = fileName;
- }
- context = QFileInfo(context).baseName();
+ CppStackFrame *frame = scope.engine->currentStackFrame;
+ // The first non-empty source URL in the call stack determines the translation context.
+ while (frame && context.isEmpty()) {
+ if (CompiledData::CompilationUnitBase *baseUnit = frame->v4Function->compilationUnit) {
+ const auto *unit = static_cast<const CompiledData::CompilationUnit *>(baseUnit);
+ QString fileName = unit->fileName();
+ QUrl url(unit->fileName());
+ if (url.isValid() && url.isRelative()) {
+ context = url.fileName();
+ } else {
+ context = QQmlFile::urlToLocalFileOrQrc(fileName);
+ if (context.isEmpty() && fileName.startsWith(QLatin1String(":/")))
+ context = fileName;
}
- frame = frame->parent;
+ context = QFileInfo(context).baseName();
+ }
+ frame = frame->parent;
+ }
+
+ if (context.isEmpty()) {
+ if (QQmlContextData *ctxt = scope.engine->callingQmlContext()) {
+ QString path = ctxt->urlString();
+ int lastSlash = path.lastIndexOf(QLatin1Char('/'));
+ int lastDot = path.lastIndexOf(QLatin1Char('.'));
+ int length = lastDot - (lastSlash + 1);
+ context = (lastSlash > -1) ? path.mid(lastSlash + 1, (length > -1) ? length : -1) : QString();
}
}
@@ -2173,8 +2171,7 @@ function.
*/
ReturnedValue QtObject::method_callLater(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- QV8Engine *v8engine = b->engine()->v8Engine;
- return v8engine->delayedCallQueue()->addUniquelyAndExecuteLater(b, thisObject, argv, argc);
+ return b->engine()->delayedCallQueue()->addUniquelyAndExecuteLater(b, thisObject, argv, argc);
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp
deleted file mode 100644
index d76344b613..0000000000
--- a/src/qml/qml/v8/qv8engine.cpp
+++ /dev/null
@@ -1,331 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qv8engine_p.h"
-
-#if QT_CONFIG(qml_sequence_object)
-#include "qv4sequenceobject_p.h"
-#endif
-
-#include "private/qjsengine_p.h"
-
-#include <private/qqmlbuiltinfunctions_p.h>
-#include <private/qqmllist_p.h>
-#include <private/qqmlengine_p.h>
-#if QT_CONFIG(qml_xml_http_request)
-#include <private/qqmlxmlhttprequest_p.h>
-#endif
-#if QT_CONFIG(qml_locale)
-#include <private/qqmllocale_p.h>
-#endif
-#include <private/qqmlglobal_p.h>
-#include <private/qqmlmemoryprofiler_p.h>
-#include <private/qqmlplatform_p.h>
-#include <private/qjsvalue_p.h>
-#include <private/qqmltypewrapper_p.h>
-#include <private/qqmlvaluetypewrapper_p.h>
-#include <private/qqmllistwrapper_p.h>
-#include <private/qv4scopedvalue_p.h>
-
-#include "qv4domerrors_p.h"
-#include "qv4sqlerrors_p.h"
-
-#include <QtCore/qjsonarray.h>
-#include <QtCore/qjsonobject.h>
-#include <QtCore/qjsonvalue.h>
-#include <QtCore/qdatetime.h>
-#include <QtCore/qdatastream.h>
-#include <private/qsimd_p.h>
-
-#include <private/qv4value_p.h>
-#include <private/qv4dateobject_p.h>
-#include <private/qv4objectiterator_p.h>
-#include <private/qv4qobjectwrapper_p.h>
-#include <private/qv4mm_p.h>
-#include <private/qv4objectproto_p.h>
-#include <private/qv4globalobject_p.h>
-#include <private/qv4regexpobject_p.h>
-#include <private/qv4variantobject_p.h>
-#include <private/qv4script_p.h>
-#include <private/qv4include_p.h>
-#include <private/qv4jsonobject_p.h>
-#include <private/qv4identifiertable_p.h>
-
-Q_DECLARE_METATYPE(QList<int>)
-
-
-// XXX TODO: Need to check all the global functions will also work in a worker script where the
-// QQmlEngine is not available
-QT_BEGIN_NAMESPACE
-
-template <typename ReturnType>
-ReturnType convertJSValueToVariantType(const QJSValue &value)
-{
- return value.toVariant().value<ReturnType>();
-}
-
-static void saveJSValue(QDataStream &stream, const void *data)
-{
- const QJSValue *jsv = reinterpret_cast<const QJSValue *>(data);
- quint32 isNullOrUndefined = 0;
- if (jsv->isNull())
- isNullOrUndefined |= 0x1;
- if (jsv->isUndefined())
- isNullOrUndefined |= 0x2;
- stream << isNullOrUndefined;
- if (!isNullOrUndefined)
- reinterpret_cast<const QJSValue*>(data)->toVariant().save(stream);
-}
-
-static void restoreJSValue(QDataStream &stream, void *data)
-{
- QJSValue *jsv = reinterpret_cast<QJSValue*>(data);
-
- quint32 isNullOrUndefined;
- stream >> isNullOrUndefined;
-
- if (isNullOrUndefined & 0x1) {
- *jsv = QJSValue(QJSValue::NullValue);
- } else if (isNullOrUndefined & 0x2) {
- *jsv = QJSValue();
- } else {
- QVariant v;
- v.load(stream);
- QJSValuePrivate::setVariant(jsv, v);
- }
-}
-
-QV8Engine::QV8Engine(QV4::ExecutionEngine *v4)
- : m_engine(nullptr)
- , m_v4Engine(v4)
-#if QT_CONFIG(qml_xml_http_request)
- , m_xmlHttpRequestData(nullptr)
-#endif
-{
-#ifndef Q_OS_WASM // wasm does not have working simd QTBUG-63924
-#ifdef Q_PROCESSOR_X86_32
- if (!qCpuHasFeature(SSE2)) {
- qFatal("This program requires an X86 processor that supports SSE2 extension, at least a Pentium 4 or newer");
- }
-#endif
-#endif
-
- QML_MEMORY_SCOPE_STRING("QV8Engine::QV8Engine");
- 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>);
- QMetaType::registerStreamOperators(qMetaTypeId<QJSValue>(), saveJSValue, restoreJSValue);
-
- m_delayedCallQueue.init(m_v4Engine);
-
- QV4::QObjectWrapper::initializeBindings(m_v4Engine);
-}
-
-QV8Engine::~QV8Engine()
-{
- qDeleteAll(m_extensionData);
- m_extensionData.clear();
-
-#if QT_CONFIG(qml_xml_http_request)
- qt_rem_qmlxmlhttprequest(m_v4Engine, m_xmlHttpRequestData);
- m_xmlHttpRequestData = nullptr;
-#endif
-}
-
-#if QT_CONFIG(qml_network)
-QNetworkAccessManager *QV8Engine::networkAccessManager()
-{
- return QQmlEnginePrivate::get(m_engine)->getNetworkAccessManager();
-}
-#endif
-
-const QSet<QString> &QV8Engine::illegalNames() const
-{
- return m_illegalNames;
-}
-
-void QV8Engine::initializeGlobal()
-{
- QV4::Scope scope(m_v4Engine);
- QV4::GlobalExtensions::init(m_v4Engine->globalObject, QJSEngine::AllExtensions);
-
- QV4::ScopedObject qt(scope, m_v4Engine->memoryManager->allocate<QV4::QtObject>(m_engine));
- m_v4Engine->globalObject->defineDefaultProperty(QStringLiteral("Qt"), qt);
-
-#if QT_CONFIG(qml_locale)
- QQmlLocale::registerStringLocaleCompare(m_v4Engine);
- QQmlDateExtension::registerExtension(m_v4Engine);
- QQmlNumberExtension::registerExtension(m_v4Engine);
-#endif
-
-#if QT_CONFIG(qml_xml_http_request)
- qt_add_domexceptions(m_v4Engine);
- m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(m_v4Engine);
-#endif
-
- qt_add_sqlexceptions(m_v4Engine);
-
- {
- for (uint i = 0; i < m_v4Engine->globalObject->internalClass()->size; ++i) {
- if (m_v4Engine->globalObject->internalClass()->nameMap.at(i).isString()) {
- QV4::PropertyKey id = m_v4Engine->globalObject->internalClass()->nameMap.at(i);
- m_illegalNames.insert(id.toQString());
- }
- }
- }
-}
-
-static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object)
-{
- if (object->as<QV4::QObjectWrapper>())
- return;
-
- QV4::Scope scope(v4);
-
- bool instanceOfObject = false;
- QV4::ScopedObject p(scope, object->getPrototypeOf());
- while (p) {
- if (p->d() == v4->objectPrototype()->d()) {
- instanceOfObject = true;
- break;
- }
- p = p->getPrototypeOf();
- }
- if (!instanceOfObject)
- return;
-
- QV4::Heap::InternalClass *frozen = object->internalClass()->propertiesFrozen();
- if (object->internalClass() == frozen)
- return;
- object->setInternalClass(frozen);
-
- QV4::ScopedObject o(scope);
- for (uint i = 0; i < frozen->size; ++i) {
- if (!frozen->nameMap.at(i).isStringOrSymbol())
- continue;
- o = *object->propertyData(i);
- if (o)
- freeze_recursive(v4, o);
- }
-}
-
-void QV8Engine::freezeObject(const QV4::Value &value)
-{
- QV4::Scope scope(m_v4Engine);
- QV4::ScopedObject o(scope, value);
- freeze_recursive(m_v4Engine, o);
-}
-
-struct QV8EngineRegistrationData
-{
- QV8EngineRegistrationData() : extensionCount(0) {}
-
- QMutex mutex;
- int extensionCount;
-};
-Q_GLOBAL_STATIC(QV8EngineRegistrationData, registrationData);
-
-QMutex *QV8Engine::registrationMutex()
-{
- return &registrationData()->mutex;
-}
-
-int QV8Engine::registerExtension()
-{
- return registrationData()->extensionCount++;
-}
-
-void QV8Engine::setExtensionData(int index, Deletable *data)
-{
- if (m_extensionData.count() <= index)
- m_extensionData.resize(index + 1);
-
- if (m_extensionData.at(index))
- delete m_extensionData.at(index);
-
- m_extensionData[index] = data;
-}
-
-void QV8Engine::initQmlGlobalObject()
-{
- initializeGlobal();
- freezeObject(*m_v4Engine->globalObject);
-}
-
-void QV8Engine::setEngine(QQmlEngine *engine)
-{
- m_engine = engine;
- initQmlGlobalObject();
-}
-
-void QV8Engine::startTimer(const QString &timerName)
-{
- if (!m_time.isValid())
- m_time.start();
- m_startedTimers[timerName] = m_time.elapsed();
-}
-
-qint64 QV8Engine::stopTimer(const QString &timerName, bool *wasRunning)
-{
- if (!m_startedTimers.contains(timerName)) {
- *wasRunning = false;
- return 0;
- }
- *wasRunning = true;
- qint64 startedAt = m_startedTimers.take(timerName);
- return m_time.elapsed() - startedAt;
-}
-
-int QV8Engine::consoleCountHelper(const QString &file, quint16 line, quint16 column)
-{
- const QString key = file + QString::number(line) + QString::number(column);
- int number = m_consoleCount.value(key, 0);
- number++;
- m_consoleCount.insert(key, number);
- return number;
-}
-
-QT_END_NAMESPACE
-
diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h
deleted file mode 100644
index 23559618ef..0000000000
--- a/src/qml/qml/v8/qv8engine_p.h
+++ /dev/null
@@ -1,243 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QQMLV8ENGINE_P_H
-#define QQMLV8ENGINE_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/qglobal.h>
-#include <QtCore/qvariant.h>
-#include <QtCore/qset.h>
-#include <QtCore/qmutex.h>
-#include <QtCore/qstack.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/QElapsedTimer>
-#include <QtCore/QThreadStorage>
-
-#include <QtQml/qjsengine.h>
-#include "private/qintrusivelist_p.h"
-
-
-#include <private/qv4value_p.h>
-#include <private/qv4identifier_p.h>
-#include <private/qv4context_p.h>
-#include <private/qv4stackframe_p.h>
-#include <private/qqmldelayedcallqueue_p.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
- struct ArrayObject;
- struct ExecutionEngine;
- struct QObjectMethod;
-}
-
-#define V4_DEFINE_EXTENSION(dataclass, datafunction) \
- static inline dataclass *datafunction(QV4::ExecutionEngine *engine) \
- { \
- static int extensionId = -1; \
- if (extensionId == -1) { \
- QV8Engine::registrationMutex()->lock(); \
- if (extensionId == -1) \
- extensionId = QV8Engine::registerExtension(); \
- QV8Engine::registrationMutex()->unlock(); \
- } \
- dataclass *rv = (dataclass *)engine->v8Engine->extensionData(extensionId); \
- if (!rv) { \
- rv = new dataclass(engine); \
- engine->v8Engine->setExtensionData(extensionId, rv); \
- } \
- return rv; \
- } \
-
-// Used to allow a QObject method take and return raw V4 handles without having to expose
-// 48 in the public API.
-// Use like this:
-// class MyClass : public QObject {
-// Q_OBJECT
-// ...
-// Q_INVOKABLE void myMethod(QQmlV4Function*);
-// };
-// The QQmlV8Function - and consequently the arguments and return value - only remains
-// valid during the call. If the return value isn't set within myMethod(), the will return
-// undefined.
-
-class QQmlV4Function
-{
-public:
- int length() const { return callData->argc(); }
- QV4::ReturnedValue operator[](int idx) const { return (idx < callData->argc() ? callData->args[idx].asReturnedValue() : QV4::Encode::undefined()); }
- void setReturnValue(QV4::ReturnedValue rv) { *retVal = rv; }
- QV4::ExecutionEngine *v4engine() const { return e; }
-private:
- friend struct QV4::QObjectMethod;
- QQmlV4Function();
- QQmlV4Function(const QQmlV4Function &);
- QQmlV4Function &operator=(const QQmlV4Function &);
-
- QQmlV4Function(QV4::CallData *callData, QV4::Value *retVal, QV4::ExecutionEngine *e)
- : callData(callData), retVal(retVal), e(e)
- {
- callData->thisObject = QV4::Encode::undefined();
- }
-
- QV4::CallData *callData;
- QV4::Value *retVal;
- QV4::ExecutionEngine *e;
-};
-
-class Q_QML_PRIVATE_EXPORT QQmlV4Handle
-{
-public:
- QQmlV4Handle() : d(QV4::Encode::undefined()) {}
- explicit QQmlV4Handle(const QV4::Value &v) : d(v.asReturnedValue()) {}
- explicit QQmlV4Handle(QV4::ReturnedValue v) : d(v) {}
-
- operator QV4::ReturnedValue() const { return d; }
-
-private:
- quint64 d;
-};
-
-class QObject;
-class QQmlEngine;
-class QNetworkAccessManager;
-class QQmlContextData;
-
-class Q_QML_PRIVATE_EXPORT QV8Engine
-{
- friend class QJSEngine;
-public:
-// static QJSEngine* get(QV8Engine* d) { Q_ASSERT(d); return d->q; }
- static QV4::ExecutionEngine *getV4(QV8Engine *d) { return d->m_v4Engine; }
-
- QV8Engine(QV4::ExecutionEngine *v4);
- virtual ~QV8Engine();
-
- // This enum should be in sync with QQmlEngine::ObjectOwnership
- enum ObjectOwnership { CppOwnership, JavaScriptOwnership };
-
- struct Deletable {
- virtual ~Deletable() {}
- };
-
- void initQmlGlobalObject();
- void setEngine(QQmlEngine *engine);
- QQmlEngine *engine() { return m_engine; }
- QQmlDelayedCallQueue *delayedCallQueue() { return &m_delayedCallQueue; }
-
-#if QT_CONFIG(qml_xml_http_request)
- void *xmlHttpRequestData() const { return m_xmlHttpRequestData; }
-#endif
-
- void freezeObject(const QV4::Value &value);
-
-#if QT_CONFIG(qml_network)
- // Return the network access manager for this engine. By default this returns the network
- // access manager of the QQmlEngine. It is overridden in the case of a threaded v8
- // instance (like in WorkerScript).
- virtual QNetworkAccessManager *networkAccessManager();
-#endif
-
- // Return the list of illegal id names (the names of the properties on the global object)
- const QSet<QString> &illegalNames() const;
-
- static QMutex *registrationMutex();
- static int registerExtension();
-
- inline Deletable *extensionData(int) const;
- void setExtensionData(int, Deletable *);
-
-public:
- // used for console.time(), console.timeEnd()
- void startTimer(const QString &timerName);
- qint64 stopTimer(const QString &timerName, bool *wasRunning);
-
- // used for console.count()
- int consoleCountHelper(const QString &file, quint16 line, quint16 column);
-
-protected:
- QQmlEngine *m_engine;
- QQmlDelayedCallQueue m_delayedCallQueue;
-
- QV4::ExecutionEngine *m_v4Engine;
-
-#if QT_CONFIG(qml_xml_http_request)
- void *m_xmlHttpRequestData;
-#endif
-
- QVector<Deletable *> m_extensionData;
-
- QSet<QString> m_illegalNames;
-
- QElapsedTimer m_time;
- QHash<QString, qint64> m_startedTimers;
-
- QHash<QString, quint32> m_consoleCount;
-
- void initializeGlobal();
-
-private:
- Q_DISABLE_COPY(QV8Engine)
-};
-
-inline QV8Engine::Deletable *QV8Engine::extensionData(int index) const
-{
- if (index < m_extensionData.count())
- return m_extensionData[index];
- else
- return nullptr;
-}
-
-
-QT_END_NAMESPACE
-
-Q_DECLARE_METATYPE(QQmlV4Handle)
-
-#endif // QQMLV8ENGINE_P_H
diff --git a/src/qml/qml/v8/v8.pri b/src/qml/qml/v8/v8.pri
index 4592022939..fbcc47de0c 100644
--- a/src/qml/qml/v8/v8.pri
+++ b/src/qml/qml/v8/v8.pri
@@ -1,11 +1,9 @@
HEADERS += \
- $$PWD/qv8engine_p.h \
$$PWD/qv4domerrors_p.h \
$$PWD/qv4sqlerrors_p.h \
$$PWD/qqmlbuiltinfunctions_p.h
SOURCES += \
- $$PWD/qv8engine.cpp \
$$PWD/qv4domerrors.cpp \
$$PWD/qv4sqlerrors.cpp \
$$PWD/qqmlbuiltinfunctions.cpp
diff --git a/src/qml/qmldirparser/qmldirparser.pri b/src/qml/qmldirparser/qmldirparser.pri
index 660e7b395a..fefe2e75be 100644
--- a/src/qml/qmldirparser/qmldirparser.pri
+++ b/src/qml/qmldirparser/qmldirparser.pri
@@ -2,10 +2,7 @@ INCLUDEPATH += $$PWD
INCLUDEPATH += $$OUT_PWD
HEADERS += \
- $$PWD/qqmldirparser_p.h \
- $$PWD/qqmlerror.h \
- $$PWD/qqmlsourcecoordinate_p.h
+ $$PWD/qqmldirparser_p.h
SOURCES += \
- $$PWD/qqmldirparser.cpp \
- $$PWD/qqmlerror.cpp
+ $$PWD/qqmldirparser.cpp
diff --git a/src/qml/qmldirparser/qqmldirparser.cpp b/src/qml/qmldirparser/qqmldirparser.cpp
index e944b52e47..5bf33d3602 100644
--- a/src/qml/qmldirparser/qqmldirparser.cpp
+++ b/src/qml/qmldirparser/qqmldirparser.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qqmldirparser_p.h"
-#include "qqmlerror.h"
#include <QtCore/QtDebug>
@@ -263,6 +262,13 @@ bool QQmlDirParser::parse(const QString &source)
} else {
reportError(lineNumber, 0, QStringLiteral("invalid version %1, expected <major>.<minor>").arg(sections[2]));
}
+ } else if (sections[0] == QLatin1String("import")) {
+ if (sectionCount != 2) {
+ reportError(lineNumber, 0,
+ QStringLiteral("import requires 2 arguments, but %1 were provided").arg(sectionCount - 1));
+ continue;
+ }
+ _imports << sections[1];
} else if (sectionCount == 2) {
// No version specified (should only be used for relative qmldir files)
const Component entry(sections[0], sections[1], -1, -1);
@@ -297,8 +303,8 @@ bool QQmlDirParser::parse(const QString &source)
void QQmlDirParser::reportError(quint16 line, quint16 column, const QString &description)
{
QQmlJS::DiagnosticMessage error;
- error.loc.startLine = line;
- error.loc.startColumn = column;
+ error.line = line;
+ error.column = column;
error.message = description;
_errors.append(error);
}
@@ -311,27 +317,20 @@ bool QQmlDirParser::hasError() const
return false;
}
-void QQmlDirParser::setError(const QQmlError &e)
+void QQmlDirParser::setError(const QQmlJS::DiagnosticMessage &e)
{
_errors.clear();
- reportError(e.line(), e.column(), e.description());
+ reportError(e.line, e.column, e.message);
}
-QList<QQmlError> QQmlDirParser::errors(const QString &uri) const
+QList<QQmlJS::DiagnosticMessage> QQmlDirParser::errors(const QString &uri) const
{
- QUrl url(uri);
- QList<QQmlError> errors;
+ QList<QQmlJS::DiagnosticMessage> errors;
const int numErrors = _errors.size();
errors.reserve(numErrors);
for (int i = 0; i < numErrors; ++i) {
- const QQmlJS::DiagnosticMessage &msg = _errors.at(i);
- QQmlError e;
- QString description = msg.message;
- description.replace(QLatin1String("$$URI$$"), uri);
- e.setDescription(description);
- e.setUrl(url);
- e.setLine(msg.loc.startLine);
- e.setColumn(msg.loc.startColumn);
+ QQmlJS::DiagnosticMessage e = _errors.at(i);
+ e.message.replace(QLatin1String("$$URI$$"), uri);
errors << e;
}
return errors;
@@ -362,6 +361,11 @@ QHash<QString, QQmlDirParser::Component> QQmlDirParser::dependencies() const
return _dependencies;
}
+QStringList QQmlDirParser::imports() const
+{
+ return _imports;
+}
+
QList<QQmlDirParser::Script> QQmlDirParser::scripts() const
{
return _scripts;
diff --git a/src/qml/qmldirparser/qqmldirparser_p.h b/src/qml/qmldirparser/qqmldirparser_p.h
index f7a91c8b81..37ca1ef2ce 100644
--- a/src/qml/qmldirparser/qqmldirparser_p.h
+++ b/src/qml/qmldirparser/qqmldirparser_p.h
@@ -54,14 +54,14 @@
#include <QtCore/QUrl>
#include <QtCore/QHash>
#include <QtCore/QDebug>
+#include <private/qtqmlcompilerglobal_p.h>
#include <private/qqmljsengine_p.h>
-#include <private/qv4global_p.h>
+#include <private/qqmljsdiagnosticmessage_p.h>
QT_BEGIN_NAMESPACE
-class QQmlError;
class QQmlEngine;
-class Q_QML_PRIVATE_EXPORT QQmlDirParser
+class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlDirParser
{
public:
QQmlDirParser();
@@ -70,8 +70,8 @@ public:
bool parse(const QString &source);
bool hasError() const;
- void setError(const QQmlError &);
- QList<QQmlError> errors(const QString &uri) const;
+ void setError(const QQmlJS::DiagnosticMessage &);
+ QList<QQmlJS::DiagnosticMessage> errors(const QString &uri) const;
QString typeNamespace() const;
void setTypeNamespace(const QString &s);
@@ -136,6 +136,7 @@ public:
QHash<QString,Component> components() const;
QHash<QString,Component> dependencies() const;
+ QStringList imports() const;
QList<Script> scripts() const;
QList<Plugin> plugins() const;
bool designerSupported() const;
@@ -162,6 +163,7 @@ private:
QString _typeNamespace;
QHash<QString,Component> _components; // multi hash
QHash<QString,Component> _dependencies;
+ QStringList _imports;
QList<Script> _scripts;
QList<Plugin> _plugins;
bool _designerSupported;
diff --git a/src/qml/qtqmlcompilerglobal.h b/src/qml/qtqmlcompilerglobal.h
new file mode 100644
index 0000000000..850d413372
--- /dev/null
+++ b/src/qml/qtqmlcompilerglobal.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTQMLCOMPILERGLOBAL_H
+#define QTQMLCOMPILERGLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+#if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB) || defined(QT_STATIC)
+# define Q_QMLCOMPILER_EXPORT
+#else
+# if defined(QT_BUILD_QML_LIB)
+# define Q_QMLCOMPILER_EXPORT Q_DECL_EXPORT
+# else
+# define Q_QMLCOMPILER_EXPORT Q_DECL_IMPORT
+# endif
+#endif
+
+QT_END_NAMESPACE
+#endif // QTQMLCOMPILERGLOBAL_H
diff --git a/src/qml/qtqmlcompilerglobal_p.h b/src/qml/qtqmlcompilerglobal_p.h
new file mode 100644
index 0000000000..9c8bce23d3
--- /dev/null
+++ b/src/qml/qtqmlcompilerglobal_p.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTQMLCOMPILERGLOBAL_P_H
+#define QTQMLCOMPILERGLOBAL_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/private/qglobal_p.h>
+#include <qtqmlcompilerglobal.h>
+
+#define Q_QMLCOMPILER_PRIVATE_EXPORT Q_QMLCOMPILER_EXPORT
+
+#endif // QTQMLCOMPILERGLOBAL_P_H
diff --git a/src/qml/qtqmlglobal.h b/src/qml/qtqmlglobal.h
index 090b830b3c..3e275c6359 100644
--- a/src/qml/qtqmlglobal.h
+++ b/src/qml/qtqmlglobal.h
@@ -53,7 +53,9 @@
#else
# define QT_FEATURE_qml_debug -1
# define QT_FEATURE_qml_sequence_object 1
-# define QT_FEATURE_qml_tracing -1
+# define QT_FEATURE_qml_jit -1
+# define QT_FEATURE_qml_worker_script -1
+# define QT_FEATURE_qml_xml_http_request -1
#endif
QT_BEGIN_NAMESPACE
diff --git a/src/qml/qtqmlglobal_p.h b/src/qml/qtqmlglobal_p.h
index 60988d12e6..9ca0cf2abe 100644
--- a/src/qml/qtqmlglobal_p.h
+++ b/src/qml/qtqmlglobal_p.h
@@ -56,8 +56,7 @@
#ifndef QT_QML_BOOTSTRAPPED
# include <QtQml/private/qtqml-config_p.h>
#endif
-
-#define Q_QML_PRIVATE_API_VERSION 3
+#include <private/qqmlapiversion_p.h>
#define Q_QML_PRIVATE_EXPORT Q_QML_EXPORT
diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp
index 4aed0f2fee..921d60caa1 100644
--- a/src/qml/types/qqmlbind.cpp
+++ b/src/qml/types/qqmlbind.cpp
@@ -43,6 +43,8 @@
#include <private/qqmlproperty_p.h>
#include <private/qqmlbinding_p.h>
#include <private/qqmlmetatype_p.h>
+#include <private/qqmlvmemetaobject_p.h>
+#include <private/qv4persistent_p.h>
#include <qqmlengine.h>
#include <qqmlcontext.h>
@@ -52,28 +54,50 @@
#include <QtCore/qfile.h>
#include <QtCore/qdebug.h>
#include <QtCore/qtimer.h>
+#include <QtCore/qloggingcategory.h>
#include <private/qobject_p.h>
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcBindingRemoval)
+
class QQmlBindPrivate : public QObjectPrivate
{
public:
- QQmlBindPrivate() : obj(nullptr), componentComplete(true), delayed(false), pendingEval(false) {}
+ QQmlBindPrivate()
+ : obj(nullptr)
+ , prevBind(QQmlAbstractBinding::Ptr())
+ , prevIsVariant(false)
+ , componentComplete(true)
+ , delayed(false)
+ , pendingEval(false)
+ , restoreBinding(true)
+ , restoreValue(false)
+ , restoreModeExplicit(false)
+ , writingProperty(false)
+ {}
~QQmlBindPrivate() { }
QQmlNullableValue<bool> when;
QPointer<QObject> obj;
QString propName;
- QQmlNullableValue<QVariant> value;
+ QQmlNullableValue<QJSValue> value;
QQmlProperty prop;
QQmlAbstractBinding::Ptr prevBind;
+ QV4::PersistentValue v4Value;
+ QVariant prevValue;
+ bool prevIsVariant:1;
bool componentComplete:1;
bool delayed:1;
bool pendingEval:1;
+ bool restoreBinding:1;
+ bool restoreValue:1;
+ bool restoreModeExplicit:1;
+ bool writingProperty: 1;
void validate(QObject *binding) const;
+ void clearPrev();
};
void QQmlBindPrivate::validate(QObject *binding) const
@@ -175,9 +199,12 @@ QQmlBind::~QQmlBind()
When the binding becomes inactive again, any direct bindings that were previously
set on the property will be restored.
- \note A previously set literal value is not restored when the Binding becomes inactive. Rather,
- the last value set by the now inactive Binding is retained. This behavior will change in future
- versions of Qt.
+ \note By default, a previously set literal value is not restored when the Binding becomes
+ inactive. Rather, the last value set by the now inactive Binding is retained. You can customize
+ the restoration behavior for literal values as well as bindings using the \l restoreMode
+ property. The default will change in Qt 6.0.
+
+ \sa restoreMode
*/
bool QQmlBind::when() const
{
@@ -220,7 +247,7 @@ void QQmlBind::setObject(QObject *obj)
}
d->obj = obj;
if (d->componentComplete) {
- d->prop = QQmlProperty(d->obj, d->propName);
+ setTarget(QQmlProperty(d->obj, d->propName, qmlContext(this)));
d->validate(this);
}
eval();
@@ -266,7 +293,7 @@ void QQmlBind::setProperty(const QString &p)
}
d->propName = p;
if (d->componentComplete) {
- d->prop = QQmlProperty(d->obj, d->propName);
+ setTarget(QQmlProperty(d->obj, d->propName, qmlContext(this)));
d->validate(this);
}
eval();
@@ -278,13 +305,13 @@ void QQmlBind::setProperty(const QString &p)
The value to be set on the target object and property. This can be a
constant (which isn't very useful), or a bound expression.
*/
-QVariant QQmlBind::value() const
+QJSValue QQmlBind::value() const
{
Q_D(const QQmlBind);
return d->value.value;
}
-void QQmlBind::setValue(const QVariant &v)
+void QQmlBind::setValue(const QJSValue &v)
{
Q_D(QQmlBind);
d->value = v;
@@ -327,9 +354,71 @@ void QQmlBind::setDelayed(bool delayed)
eval();
}
+/*!
+ \qmlproperty enumeration QtQml::Binding::restoreMode
+ \since 5.14
+
+ This property can be used to describe if and how the original value should
+ be restored when the binding is disabled.
+
+ The possible values are:
+ \list
+ \li Binding.RestoreNone The original value is not restored at all
+ \li Binding.RestoreBinding The original value is restored if it was another
+ binding. In that case the old binding is in effect again.
+ \li Binding.RestoreValue The original value is restored if it was a plain
+ value rather than a binding.
+ \li Binding.RestoreBindingOrValue The original value is always restored.
+ \endlist
+
+ \warning The default value is Binding.RestoreBinding. This will change in
+ Qt 6.0 to Binding.RestoreBindingOrValue.
+
+ If you rely on any specific behavior regarding the restoration of plain
+ values when bindings get disabled you should migrate to explicitly set the
+ restoreMode.
+
+ Reliance on a restoreMode that doesn't restore the previous binding or value
+ for a specific property results in a run-time warning.
+*/
+QQmlBind::RestorationMode QQmlBind::restoreMode() const
+{
+ Q_D(const QQmlBind);
+ unsigned result = RestoreNone;
+ if (d->restoreValue)
+ result |= RestoreValue;
+ if (d->restoreBinding)
+ result |= RestoreBinding;
+ return RestorationMode(result);
+}
+
+void QQmlBind::setRestoreMode(RestorationMode newMode)
+{
+ Q_D(QQmlBind);
+ d->restoreModeExplicit = true;
+ if (newMode != restoreMode()) {
+ d->restoreValue = (newMode & RestoreValue);
+ d->restoreBinding = (newMode & RestoreBinding);
+ emit restoreModeChanged();
+ }
+}
+
void QQmlBind::setTarget(const QQmlProperty &p)
{
Q_D(QQmlBind);
+
+ if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) {
+ if (QObject *oldObject = d->prop.object()) {
+ QMetaProperty prop = oldObject->metaObject()->property(d->prop.index());
+ if (prop.hasNotifySignal()) {
+ QByteArray signal('2' + prop.notifySignal().methodSignature());
+ QObject::disconnect(oldObject, signal.constData(),
+ this, SLOT(targetValueChanged()));
+ }
+ }
+ p.connectNotifySignal(this, SLOT(targetValueChanged()));
+ }
+
d->prop = p;
}
@@ -344,7 +433,7 @@ void QQmlBind::componentComplete()
Q_D(QQmlBind);
d->componentComplete = true;
if (!d->prop.isValid()) {
- d->prop = QQmlProperty(d->obj, d->propName);
+ setTarget(QQmlProperty(d->obj, d->propName, qmlContext(this)));
d->validate(this);
}
eval();
@@ -362,6 +451,14 @@ void QQmlBind::prepareEval()
}
}
+void QQmlBindPrivate::clearPrev()
+{
+ prevBind = nullptr;
+ v4Value.clear();
+ prevValue.clear();
+ prevIsVariant = false;
+}
+
void QQmlBind::eval()
{
Q_D(QQmlBind);
@@ -373,20 +470,91 @@ void QQmlBind::eval()
if (!d->when) {
//restore any previous binding
if (d->prevBind) {
- QQmlAbstractBinding::Ptr p = d->prevBind;
- d->prevBind = nullptr;
- QQmlPropertyPrivate::setBinding(p.data());
+ if (d->restoreBinding) {
+ QQmlAbstractBinding::Ptr p = d->prevBind;
+ d->clearPrev(); // Do that before setBinding(), as setBinding() may recurse.
+ QQmlPropertyPrivate::setBinding(p.data());
+ }
+ } else if (!d->v4Value.isEmpty()) {
+ if (d->restoreValue) {
+ auto propPriv = QQmlPropertyPrivate::get(d->prop);
+ QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(propPriv->object);
+ Q_ASSERT(vmemo);
+ vmemo->setVMEProperty(propPriv->core.coreIndex(), *d->v4Value.valueRef());
+ d->clearPrev();
+ } else if (!d->restoreModeExplicit) {
+ qmlWarning(this)
+ << "Not restoring previous value because restoreMode has not been set."
+ << "This behavior is deprecated."
+ << "In Qt < 6.0 the default is Binding.RestoreBinding."
+ << "In Qt >= 6.0 the default is Binding.RestoreBindingOrValue.";
+ }
+ } else if (d->prevIsVariant) {
+ if (d->restoreValue) {
+ d->prop.write(d->prevValue);
+ d->clearPrev();
+ } else if (!d->restoreModeExplicit) {
+ qmlWarning(this)
+ << "Not restoring previous value because restoreMode has not been set."
+ << "This behavior is deprecated."
+ << "In Qt < 6.0 the default is Binding.RestoreBinding."
+ << "In Qt >= 6.0 the default is Binding.RestoreBindingOrValue.";
+ }
}
return;
}
//save any set binding for restoration
- if (!d->prevBind)
+ if (!d->prevBind && d->v4Value.isEmpty() && !d->prevIsVariant) {
+ // try binding first
d->prevBind = QQmlPropertyPrivate::binding(d->prop);
+
+ if (!d->prevBind) { // nope, try a V4 value next
+ auto propPriv = QQmlPropertyPrivate::get(d->prop);
+ auto propData = propPriv->core;
+ if (!propPriv->valueTypeData.isValid() && propData.isVarProperty()) {
+ QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(propPriv->object);
+ Q_ASSERT(vmemo);
+ auto retVal = vmemo->vmeProperty(propData.coreIndex());
+ d->v4Value = QV4::PersistentValue(vmemo->engine, retVal);
+ } else { // nope, use the meta object to get a QVariant
+ d->prevValue = d->prop.read();
+ d->prevIsVariant = true;
+ }
+ }
+ }
+
QQmlPropertyPrivate::removeBinding(d->prop);
}
- d->prop.write(d->value.value);
+ d->writingProperty = true;
+ d->prop.write(d->value.value.toVariant());
+ d->writingProperty = false;
+}
+
+void QQmlBind::targetValueChanged()
+{
+ Q_D(QQmlBind);
+ if (d->writingProperty)
+ return;
+
+ if (d->when.isValid() && !d->when)
+ return;
+
+ QUrl url;
+ quint16 line = 0;
+
+ const QQmlData *ddata = QQmlData::get(this, false);
+ if (ddata && ddata->outerContext) {
+ url = ddata->outerContext->url();
+ line = ddata->lineNumber;
+ }
+
+ qCInfo(lcBindingRemoval,
+ "The target property of the Binding element created at %s:%d was changed from "
+ "elsewhere. This does not overwrite the binding. The target property will still be "
+ "updated when the value of the Binding element changes.",
+ qPrintable(url.toString()), line);
}
QT_END_NAMESPACE
diff --git a/src/qml/types/qqmlbind_p.h b/src/qml/types/qqmlbind_p.h
index 5bf9ef85c6..7bf4fc4dfd 100644
--- a/src/qml/types/qqmlbind_p.h
+++ b/src/qml/types/qqmlbind_p.h
@@ -60,15 +60,27 @@ QT_BEGIN_NAMESPACE
class QQmlBindPrivate;
class Q_AUTOTEST_EXPORT QQmlBind : public QObject, public QQmlPropertyValueSource, public QQmlParserStatus
{
+public:
+ enum RestorationMode {
+ RestoreNone = 0x0,
+ RestoreBinding = 0x1,
+ RestoreValue = 0x2,
+ RestoreBindingOrValue = RestoreBinding | RestoreValue
+ };
+
+private:
Q_OBJECT
Q_DECLARE_PRIVATE(QQmlBind)
Q_INTERFACES(QQmlParserStatus)
Q_INTERFACES(QQmlPropertyValueSource)
Q_PROPERTY(QObject *target READ object WRITE setObject)
Q_PROPERTY(QString property READ property WRITE setProperty)
- Q_PROPERTY(QVariant value READ value WRITE setValue)
+ Q_PROPERTY(QJSValue value READ value WRITE setValue)
Q_PROPERTY(bool when READ when WRITE setWhen)
Q_PROPERTY(bool delayed READ delayed WRITE setDelayed REVISION 8)
+ Q_PROPERTY(RestorationMode restoreMode READ restoreMode WRITE setRestoreMode
+ NOTIFY restoreModeChanged REVISION 14)
+ Q_ENUM(RestorationMode)
public:
QQmlBind(QObject *parent=nullptr);
@@ -83,12 +95,18 @@ public:
QString property() const;
void setProperty(const QString &);
- QVariant value() const;
- void setValue(const QVariant &);
+ QJSValue value() const;
+ void setValue(const QJSValue &);
bool delayed() const;
void setDelayed(bool);
+ RestorationMode restoreMode() const;
+ void setRestoreMode(RestorationMode);
+
+Q_SIGNALS:
+ void restoreModeChanged();
+
protected:
void setTarget(const QQmlProperty &) override;
void classBegin() override;
@@ -97,6 +115,9 @@ protected:
private:
void prepareEval();
void eval();
+
+private Q_SLOTS:
+ void targetValueChanged();
};
QT_END_NAMESPACE
diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp
index f601087690..1e801641e5 100644
--- a/src/qml/types/qqmlconnections.cpp
+++ b/src/qml/types/qqmlconnections.cpp
@@ -44,6 +44,7 @@
#include <private/qqmlboundsignal_p.h>
#include <qqmlcontext.h>
#include <private/qqmlcontext_p.h>
+#include <private/qqmlvmemetaobject_p.h>
#include <qqmlinfo.h>
#include <QtCore/qdebug.h>
@@ -66,7 +67,7 @@ public:
bool ignoreUnknownSignals;
bool componentcomplete;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
QList<const QV4::CompiledData::Binding *> bindings;
};
@@ -105,7 +106,7 @@ public:
\qml
MouseArea {
Connections {
- onClicked: foo(parameters)
+ function onClicked(mouse) { foo(mouse) }
}
}
\endqml
@@ -122,7 +123,7 @@ public:
\qml
Connections {
target: area
- onClicked: foo(parameters)
+ function onClicked(mouse) { foo(mouse) }
}
\endqml
@@ -231,7 +232,7 @@ void QQmlConnections::setIgnoreUnknownSignals(bool ignore)
d->ignoreUnknownSignals = ignore;
}
-void QQmlConnectionsParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props)
+void QQmlConnectionsParser::verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props)
{
for (int ii = 0; ii < props.count(); ++ii) {
const QV4::CompiledData::Binding *binding = props.at(ii);
@@ -256,7 +257,7 @@ void QQmlConnectionsParser::verifyBindings(const QQmlRefPointer<QV4::CompiledDat
}
}
-void QQmlConnectionsParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
+void QQmlConnectionsParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
{
QQmlConnectionsPrivate *p =
static_cast<QQmlConnectionsPrivate *>(QObjectPrivate::get(object));
@@ -270,8 +271,76 @@ void QQmlConnections::connectSignals()
if (!d->componentcomplete || (d->targetSet && !target()))
return;
- if (d->bindings.isEmpty())
+ if (d->bindings.isEmpty()) {
+ connectSignalsToMethods();
+ } else {
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+ qmlWarning(this) << tr("Implicitly defined onFoo properties in Connections are deprecated. "
+ "Use this syntax instead: function onFoo(<arguments>) { ... }");
+#endif
+ connectSignalsToBindings();
+ }
+}
+
+void QQmlConnections::connectSignalsToMethods()
+{
+ Q_D(QQmlConnections);
+
+ QObject *target = this->target();
+ QQmlData *ddata = QQmlData::get(this);
+ if (!ddata)
return;
+
+ QV4::ExecutionEngine *engine = ddata->context->engine->handle();
+
+ QQmlContextData *ctxtdata = ddata->outerContext;
+ for (int i = ddata->propertyCache->methodOffset(),
+ end = ddata->propertyCache->methodOffset() + ddata->propertyCache->methodCount();
+ i < end;
+ ++i) {
+
+ QQmlPropertyData *handler = ddata->propertyCache->method(i);
+ if (!handler || !handler->isVMEFunction())
+ continue;
+
+ const QString propName = handler->name(this);
+
+ QQmlProperty prop(target, propName);
+ if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) {
+ int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex();
+ auto *signal = new QQmlBoundSignal(target, signalIndex, this, qmlEngine(this));
+ signal->setEnabled(d->enabled);
+
+ QV4::Scope scope(engine);
+ QV4::ScopedContext global(scope, engine->rootContext());
+
+ QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(this);
+ Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this
+
+ QV4::ScopedFunctionObject method(scope, vmeMetaObject->vmeMethod(handler->coreIndex()));
+
+ QQmlBoundSignalExpression *expression =
+ ctxtdata ? new QQmlBoundSignalExpression(
+ target, signalIndex, ctxtdata, this,
+ method->as<QV4::FunctionObject>()->function())
+ : nullptr;
+
+ signal->takeExpression(expression);
+ d->boundsignals += signal;
+ } else if (!d->ignoreUnknownSignals
+ && propName.startsWith(QLatin1String("on")) && propName.length() > 2
+ && propName.at(2).isUpper()) {
+ qmlWarning(this) << tr("Detected function \"%1\" in Connections element. "
+ "This is probably intended to be a signal handler but no "
+ "signal of the target matches the name.").arg(propName);
+ }
+ }
+}
+
+// TODO: Drop this as soon as we can
+void QQmlConnections::connectSignalsToBindings()
+{
+ Q_D(QQmlConnections);
QObject *target = this->target();
QQmlData *ddata = QQmlData::get(this);
QQmlContextData *ctxtdata = ddata ? ddata->outerContext : nullptr;
diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h
index bd03d7e152..5d28e8e8be 100644
--- a/src/qml/types/qqmlconnections_p.h
+++ b/src/qml/types/qqmlconnections_p.h
@@ -69,7 +69,7 @@ class Q_AUTOTEST_EXPORT QQmlConnections : public QObject, public QQmlParserStatu
Q_INTERFACES(QQmlParserStatus)
Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged)
- Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged REVISION 1)
+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged REVISION 3)
Q_PROPERTY(bool ignoreUnknownSignals READ ignoreUnknownSignals WRITE setIgnoreUnknownSignals)
public:
@@ -87,19 +87,23 @@ public:
Q_SIGNALS:
void targetChanged();
- Q_REVISION(1) void enabledChanged();
+ Q_REVISION(3) void enabledChanged();
private:
void connectSignals();
+ void connectSignalsToMethods();
+ void connectSignalsToBindings();
+
void classBegin() override;
void componentComplete() override;
};
+// TODO: Drop this class as soon as we can
class QQmlConnectionsParser : public QQmlCustomParser
{
public:
- void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) override;
- void applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
+ void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) override;
+ void applyBindings(QObject *object, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
};
diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri
index e74c89b1f1..54cd8710b6 100644
--- a/src/qml/types/types.pri
+++ b/src/qml/types/types.pri
@@ -1,51 +1,16 @@
SOURCES += \
$$PWD/qqmlbind.cpp \
- $$PWD/qqmlconnections.cpp \
- $$PWD/qqmlmodelsmodule.cpp \
- $$PWD/qqmlmodelindexvaluetype.cpp \
- $$PWD/qqmlobjectmodel.cpp \
- $$PWD/qquickpackage.cpp \
- $$PWD/qqmlinstantiator.cpp \
- $$PWD/qqmltableinstancemodel.cpp
+ $$PWD/qqmlconnections.cpp
HEADERS += \
$$PWD/qqmlbind_p.h \
- $$PWD/qqmlconnections_p.h \
- $$PWD/qqmlmodelsmodule_p.h \
- $$PWD/qqmlmodelindexvaluetype_p.h \
- $$PWD/qqmlobjectmodel_p.h \
- $$PWD/qquickpackage_p.h \
- $$PWD/qqmlinstantiator_p.h \
- $$PWD/qqmlinstantiator_p_p.h \
- $$PWD/qqmltableinstancemodel_p.h
+ $$PWD/qqmlconnections_p.h
-qtConfig(qml-worker-script) {
+qtConfig(qml-itemmodel) {
SOURCES += \
- $$PWD/qquickworkerscript.cpp
+ $$PWD/qqmlmodelindexvaluetype.cpp
HEADERS += \
- $$PWD/qquickworkerscript_p.h
-}
-
-qtConfig(qml-list-model) {
- SOURCES += \
- $$PWD/qqmllistmodel.cpp \
- $$PWD/qqmllistmodelworkeragent.cpp
-
- HEADERS += \
- $$PWD/qqmllistmodel_p.h \
- $$PWD/qqmllistmodel_p_p.h \
- $$PWD/qqmllistmodelworkeragent_p.h
-}
-
-qtConfig(qml-delegate-model) {
- SOURCES += \
- $$PWD/qqmldelegatemodel.cpp \
- $$PWD/qqmldelegatecomponent.cpp
-
- HEADERS += \
- $$PWD/qqmldelegatemodel_p.h \
- $$PWD/qqmldelegatemodel_p_p.h \
- $$PWD/qqmldelegatecomponent_p.h
+ $$PWD/qqmlmodelindexvaluetype_p.h
}
qtConfig(qml-animation) {
diff --git a/src/qml/util/util.pri b/src/qml/util/util.pri
index bebb271f1b..3b121ba3cb 100644
--- a/src/qml/util/util.pri
+++ b/src/qml/util/util.pri
@@ -1,19 +1,5 @@
SOURCES += \
- $$PWD/qqmlchangeset.cpp \
- $$PWD/qqmllistaccessor.cpp \
- $$PWD/qqmllistcompositor.cpp \
$$PWD/qqmlpropertymap.cpp
HEADERS += \
- $$PWD/qqmlchangeset_p.h \
- $$PWD/qqmllistaccessor_p.h \
- $$PWD/qqmllistcompositor_p.h \
$$PWD/qqmlpropertymap.h
-
-qtConfig(qml-delegate-model) {
- SOURCES += \
- $$PWD/qqmladaptormodel.cpp
-
- HEADERS += \
- $$PWD/qqmladaptormodel_p.h
-}
diff --git a/src/qmldebug/qqmlenginedebugclient.cpp b/src/qmldebug/qqmlenginedebugclient.cpp
index ec45ec33bc..0ca3f573d9 100644
--- a/src/qmldebug/qqmlenginedebugclient.cpp
+++ b/src/qmldebug/qqmlenginedebugclient.cpp
@@ -33,14 +33,14 @@ QT_BEGIN_NAMESPACE
struct QQmlObjectData {
QUrl url;
- int lineNumber = -1;
- int columnNumber = -1;
+ qint32 lineNumber = -1;
+ qint32 columnNumber = -1;
QString idString;
QString objectName;
QString objectType;
- int objectId = -1;
- int contextId = -1;
- int parentId = -1;
+ qint32 objectId = -1;
+ qint32 contextId = -1;
+ qint32 parentId = -1;
};
QPacket &operator>>(QPacket &ds, QQmlObjectData &data)
@@ -63,10 +63,10 @@ struct QQmlObjectProperty {
QPacket &operator>>(QPacket &ds, QQmlObjectProperty &data)
{
- int type;
+ qint32 type;
ds >> type >> data.name >> data.value >> data.valueTypeName
>> data.binding >> data.hasNotifySignal;
- data.type = (QQmlObjectProperty::Type)type;
+ data.type = QQmlObjectProperty::Type(type);
return ds;
}
@@ -81,10 +81,10 @@ QQmlEngineDebugClientPrivate::QQmlEngineDebugClientPrivate(QQmlDebugConnection *
}
-quint32 QQmlEngineDebugClient::addWatch(
+qint32 QQmlEngineDebugClient::addWatch(
const QQmlEngineDebugPropertyReference &property, bool *success)
{
- quint32 id = -1;
+ qint32 id = -1;
*success = false;
if (state() == QQmlDebugClient::Enabled) {
id = getId();
@@ -97,19 +97,19 @@ quint32 QQmlEngineDebugClient::addWatch(
return id;
}
-quint32 QQmlEngineDebugClient::addWatch(
+qint32 QQmlEngineDebugClient::addWatch(
const QQmlEngineDebugContextReference &, const QString &, bool *success)
{
*success = false;
qWarning("QQmlEngineDebugClient::addWatch(): Not implemented");
- return 0;
+ return -1;
}
-quint32 QQmlEngineDebugClient::addWatch(
+qint32 QQmlEngineDebugClient::addWatch(
const QQmlEngineDebugObjectReference &object, const QString &expr,
bool *success)
{
- quint32 id = -1;
+ qint32 id = -1;
*success = false;
if (state() == QQmlDebugClient::Enabled) {
id = getId();
@@ -121,10 +121,10 @@ quint32 QQmlEngineDebugClient::addWatch(
return id;
}
-quint32 QQmlEngineDebugClient::addWatch(
+qint32 QQmlEngineDebugClient::addWatch(
const QQmlEngineDebugObjectReference &object, bool *success)
{
- quint32 id = -1;
+ qint32 id = -1;
*success = false;
if (state() == QQmlDebugClient::Enabled) {
id = getId();
@@ -136,15 +136,15 @@ quint32 QQmlEngineDebugClient::addWatch(
return id;
}
-quint32 QQmlEngineDebugClient::addWatch(
+qint32 QQmlEngineDebugClient::addWatch(
const QQmlEngineDebugFileReference &, bool *success)
{
*success = false;
qWarning("QQmlEngineDebugClient::addWatch(): Not implemented");
- return 0;
+ return -1;
}
-void QQmlEngineDebugClient::removeWatch(quint32 id, bool *success)
+void QQmlEngineDebugClient::removeWatch(qint32 id, bool *success)
{
*success = false;
if (state() == QQmlDebugClient::Enabled) {
@@ -155,11 +155,11 @@ void QQmlEngineDebugClient::removeWatch(quint32 id, bool *success)
}
}
-quint32 QQmlEngineDebugClient::queryAvailableEngines(bool *success)
+qint32 QQmlEngineDebugClient::queryAvailableEngines(bool *success)
{
Q_D(QQmlEngineDebugClient);
d->engines.clear();
- quint32 id = -1;
+ qint32 id = -1;
*success = false;
if (state() == QQmlDebugClient::Enabled) {
id = getId();
@@ -171,12 +171,12 @@ quint32 QQmlEngineDebugClient::queryAvailableEngines(bool *success)
return id;
}
-quint32 QQmlEngineDebugClient::queryRootContexts(
+qint32 QQmlEngineDebugClient::queryRootContexts(
const QQmlEngineDebugEngineReference &engine, bool *success)
{
Q_D(QQmlEngineDebugClient);
d->rootContext = QQmlEngineDebugContextReference();
- quint32 id = -1;
+ qint32 id = -1;
*success = false;
if (state() == QQmlDebugClient::Enabled && engine.debugId != -1) {
id = getId();
@@ -188,12 +188,12 @@ quint32 QQmlEngineDebugClient::queryRootContexts(
return id;
}
-quint32 QQmlEngineDebugClient::queryObject(
+qint32 QQmlEngineDebugClient::queryObject(
const QQmlEngineDebugObjectReference &object, bool *success)
{
Q_D(QQmlEngineDebugClient);
d->object = QQmlEngineDebugObjectReference();
- quint32 id = -1;
+ qint32 id = -1;
*success = false;
if (state() == QQmlDebugClient::Enabled && object.debugId != -1) {
id = getId();
@@ -205,12 +205,12 @@ quint32 QQmlEngineDebugClient::queryObject(
return id;
}
-quint32 QQmlEngineDebugClient::queryObjectsForLocation(
- const QString &file, int lineNumber, int columnNumber, bool *success)
+qint32 QQmlEngineDebugClient::queryObjectsForLocation(
+ const QString &file, qint32 lineNumber, qint32 columnNumber, bool *success)
{
Q_D(QQmlEngineDebugClient);
d->objects.clear();
- quint32 id = -1;
+ qint32 id = -1;
*success = false;
if (state() == QQmlDebugClient::Enabled) {
id = getId();
@@ -223,12 +223,12 @@ quint32 QQmlEngineDebugClient::queryObjectsForLocation(
return id;
}
-quint32 QQmlEngineDebugClient::queryObjectRecursive(
+qint32 QQmlEngineDebugClient::queryObjectRecursive(
const QQmlEngineDebugObjectReference &object, bool *success)
{
Q_D(QQmlEngineDebugClient);
d->object = QQmlEngineDebugObjectReference();
- quint32 id = -1;
+ qint32 id = -1;
*success = false;
if (state() == QQmlDebugClient::Enabled && object.debugId != -1) {
id = getId();
@@ -240,12 +240,12 @@ quint32 QQmlEngineDebugClient::queryObjectRecursive(
return id;
}
-quint32 QQmlEngineDebugClient::queryObjectsForLocationRecursive(const QString &file,
- int lineNumber, int columnNumber, bool *success)
+qint32 QQmlEngineDebugClient::queryObjectsForLocationRecursive(const QString &file,
+ qint32 lineNumber, qint32 columnNumber, bool *success)
{
Q_D(QQmlEngineDebugClient);
d->objects.clear();
- quint32 id = -1;
+ qint32 id = -1;
*success = false;
if (state() == QQmlDebugClient::Enabled) {
id = getId();
@@ -258,12 +258,12 @@ quint32 QQmlEngineDebugClient::queryObjectsForLocationRecursive(const QString &f
return id;
}
-quint32 QQmlEngineDebugClient::queryExpressionResult(
- int objectDebugId, const QString &expr, bool *success)
+qint32 QQmlEngineDebugClient::queryExpressionResult(
+ qint32 objectDebugId, const QString &expr, bool *success)
{
Q_D(QQmlEngineDebugClient);
d->exprResult = QVariant();
- quint32 id = -1;
+ qint32 id = -1;
*success = false;
if (state() == QQmlDebugClient::Enabled) {
id = getId();
@@ -276,12 +276,12 @@ quint32 QQmlEngineDebugClient::queryExpressionResult(
return id;
}
-quint32 QQmlEngineDebugClient::queryExpressionResultBC(
- int objectDebugId, const QString &expr, bool *success)
+qint32 QQmlEngineDebugClient::queryExpressionResultBC(
+ qint32 objectDebugId, const QString &expr, bool *success)
{
Q_D(QQmlEngineDebugClient);
d->exprResult = QVariant();
- quint32 id = -1;
+ qint32 id = -1;
*success = false;
if (state() == QQmlDebugClient::Enabled) {
id = getId();
@@ -293,15 +293,15 @@ quint32 QQmlEngineDebugClient::queryExpressionResultBC(
return id;
}
-quint32 QQmlEngineDebugClient::setBindingForObject(
- int objectDebugId,
+qint32 QQmlEngineDebugClient::setBindingForObject(
+ qint32 objectDebugId,
const QString &propertyName,
const QVariant &bindingExpression,
bool isLiteralValue,
- const QString &source, int line,
+ const QString &source, qint32 line,
bool *success)
{
- quint32 id = -1;
+ qint32 id = -1;
*success = false;
if (state() == QQmlDebugClient::Enabled && objectDebugId != -1) {
id = getId();
@@ -314,12 +314,12 @@ quint32 QQmlEngineDebugClient::setBindingForObject(
return id;
}
-quint32 QQmlEngineDebugClient::resetBindingForObject(
- int objectDebugId,
+qint32 QQmlEngineDebugClient::resetBindingForObject(
+ qint32 objectDebugId,
const QString &propertyName,
bool *success)
{
- quint32 id = -1;
+ qint32 id = -1;
*success = false;
if (state() == QQmlDebugClient::Enabled && objectDebugId != -1) {
id = getId();
@@ -331,11 +331,11 @@ quint32 QQmlEngineDebugClient::resetBindingForObject(
return id;
}
-quint32 QQmlEngineDebugClient::setMethodBody(
- int objectDebugId, const QString &methodName,
+qint32 QQmlEngineDebugClient::setMethodBody(
+ qint32 objectDebugId, const QString &methodName,
const QString &methodBody, bool *success)
{
- quint32 id = -1;
+ qint32 id = -1;
*success = false;
if (state() == QQmlDebugClient::Enabled && objectDebugId != -1) {
id = getId();
@@ -366,19 +366,19 @@ void QQmlEngineDebugClient::decode(QPacket &ds,
if (simple)
return;
- int childCount;
+ qint32 childCount;
bool recur;
ds >> childCount >> recur;
- for (int ii = 0; ii < childCount; ++ii) {
+ for (qint32 ii = 0; ii < childCount; ++ii) {
o.children.append(QQmlEngineDebugObjectReference());
decode(ds, o.children.last(), !recur);
}
- int propCount;
+ qint32 propCount;
ds >> propCount;
- for (int ii = 0; ii < propCount; ++ii) {
+ for (qint32 ii = 0; ii < propCount; ++ii) {
QQmlObjectProperty data;
ds >> data;
QQmlEngineDebugPropertyReference prop;
@@ -400,7 +400,7 @@ void QQmlEngineDebugClient::decode(QPacket &ds,
QQmlEngineDebugObjectReference obj;
obj.name = data.value.toString();
obj.className = prop.valueTypeName;
- prop.value = qVariantFromValue(obj);
+ prop.value = QVariant::fromValue(obj);
break;
}
case QQmlObjectProperty::Unknown:
@@ -414,9 +414,9 @@ void QQmlEngineDebugClient::decode(QPacket &ds,
QList<QQmlEngineDebugObjectReference> &o,
bool simple)
{
- int count;
+ qint32 count;
ds >> count;
- for (int i = 0; i < count; i++) {
+ for (qint32 i = 0; i < count; i++) {
QQmlEngineDebugObjectReference obj;
decode(ds, obj, simple);
o << obj;
@@ -464,18 +464,18 @@ void QQmlEngineDebugClient::decode(QPacket &ds,
{
ds >> c.name >> c.debugId;
- int contextCount;
+ qint32 contextCount;
ds >> contextCount;
- for (int ii = 0; ii < contextCount; ++ii) {
+ for (qint32 ii = 0; ii < contextCount; ++ii) {
c.contexts.append(QQmlEngineDebugContextReference());
decode(ds, c.contexts.last());
}
- int objectCount;
+ qint32 objectCount;
ds >> objectCount;
- for (int ii = 0; ii < objectCount; ++ii) {
+ for (qint32 ii = 0; ii < objectCount; ++ii) {
QQmlEngineDebugObjectReference obj;
decode(ds, obj, true);
@@ -490,18 +490,18 @@ void QQmlEngineDebugClient::messageReceived(const QByteArray &data)
d->valid = false;
QPacket ds(connection()->currentDataStreamVersion(), data);
- int queryId;
+ qint32 queryId;
QByteArray type;
ds >> type >> queryId;
//qDebug() << "QQmlEngineDebugPrivate::message()" << type;
if (type == "LIST_ENGINES_R") {
- int count;
+ qint32 count;
ds >> count;
d->engines.clear();
- for (int ii = 0; ii < count; ++ii) {
+ for (qint32 ii = 0; ii < count; ++ii) {
QQmlEngineDebugEngineReference eng;
ds >> eng.name;
ds >> eng.debugId;
@@ -532,7 +532,7 @@ void QQmlEngineDebugClient::messageReceived(const QByteArray &data)
ds >> d->valid;
} else if (type == "UPDATE_WATCH") {
- int debugId;
+ qint32 debugId;
QByteArray name;
QVariant value;
ds >> debugId >> name >> value;
@@ -540,7 +540,9 @@ void QQmlEngineDebugClient::messageReceived(const QByteArray &data)
return;
} else if (type == "OBJECT_CREATED") {
- int engineId, objectId, parentId;
+ qint32 engineId;
+ qint32 objectId;
+ qint32 parentId;
ds >> engineId >> objectId >> parentId;
emit newObject(objectId);
return;
@@ -557,7 +559,7 @@ void QQmlEngineDebugClient::messageReceived(const QByteArray &data)
}
-quint32 QQmlEngineDebugClient::getId()
+qint32 QQmlEngineDebugClient::getId()
{
Q_D(QQmlEngineDebugClient);
return d->nextId++;
diff --git a/src/qmldebug/qqmlenginedebugclient_p.h b/src/qmldebug/qqmlenginedebugclient_p.h
index 4a9cc3a020..0a01f2eac5 100644
--- a/src/qmldebug/qqmlenginedebugclient_p.h
+++ b/src/qmldebug/qqmlenginedebugclient_p.h
@@ -50,7 +50,7 @@ QT_BEGIN_NAMESPACE
struct QQmlEngineDebugPropertyReference
{
- int objectDebugId = -1;
+ qint32 objectDebugId = -1;
QString name;
QVariant value;
QString valueTypeName;
@@ -61,25 +61,25 @@ struct QQmlEngineDebugPropertyReference
struct QQmlEngineDebugFileReference
{
QUrl url;
- int lineNumber = -1;
- int columnNumber = -1;
+ qint32 lineNumber = -1;
+ qint32 columnNumber = -1;
};
struct QQmlEngineDebugObjectReference
{
- int debugId = -1;
+ qint32 debugId = -1;
QString className;
QString idString;
QString name;
QQmlEngineDebugFileReference source;
- int contextDebugId = -1;
+ qint32 contextDebugId = -1;
QList<QQmlEngineDebugPropertyReference> properties;
QList<QQmlEngineDebugObjectReference> children;
};
struct QQmlEngineDebugContextReference
{
- int debugId = -1;
+ qint32 debugId = -1;
QString name;
QList<QQmlEngineDebugObjectReference> objects;
QList<QQmlEngineDebugContextReference> contexts;
@@ -87,7 +87,7 @@ struct QQmlEngineDebugContextReference
struct QQmlEngineDebugEngineReference
{
- int debugId = -1;
+ qint32 debugId = -1;
QString name;
};
@@ -100,46 +100,46 @@ class QQmlEngineDebugClient : public QQmlDebugClient
public:
explicit QQmlEngineDebugClient(QQmlDebugConnection *conn);
- quint32 addWatch(const QQmlEngineDebugPropertyReference &,
- bool *success);
- quint32 addWatch(const QQmlEngineDebugContextReference &, const QString &,
- bool *success);
- quint32 addWatch(const QQmlEngineDebugObjectReference &, const QString &,
- bool *success);
- quint32 addWatch(const QQmlEngineDebugObjectReference &,
- bool *success);
- quint32 addWatch(const QQmlEngineDebugFileReference &,
+ qint32 addWatch(const QQmlEngineDebugPropertyReference &,
+ bool *success);
+ qint32 addWatch(const QQmlEngineDebugContextReference &, const QString &,
+ bool *success);
+ qint32 addWatch(const QQmlEngineDebugObjectReference &, const QString &,
+ bool *success);
+ qint32 addWatch(const QQmlEngineDebugObjectReference &,
+ bool *success);
+ qint32 addWatch(const QQmlEngineDebugFileReference &,
bool *success);
- void removeWatch(quint32 watch, bool *success);
-
- quint32 queryAvailableEngines(bool *success);
- quint32 queryRootContexts(const QQmlEngineDebugEngineReference &,
- bool *success);
- quint32 queryObject(const QQmlEngineDebugObjectReference &,
- bool *success);
- quint32 queryObjectsForLocation(const QString &file,
- int lineNumber, int columnNumber, bool *success);
- quint32 queryObjectRecursive(const QQmlEngineDebugObjectReference &,
+ void removeWatch(qint32 watch, bool *success);
+
+ qint32 queryAvailableEngines(bool *success);
+ qint32 queryRootContexts(const QQmlEngineDebugEngineReference &,
+ bool *success);
+ qint32 queryObject(const QQmlEngineDebugObjectReference &,
+ bool *success);
+ qint32 queryObjectsForLocation(const QString &file,
+ qint32 lineNumber, qint32 columnNumber, bool *success);
+ qint32 queryObjectRecursive(const QQmlEngineDebugObjectReference &,
+ bool *success);
+ qint32 queryObjectsForLocationRecursive(const QString &file,
+ qint32 lineNumber, qint32 columnNumber, bool *success);
+ qint32 queryExpressionResult(qint32 objectDebugId,
+ const QString &expr,
+ bool *success);
+ qint32 queryExpressionResultBC(qint32 objectDebugId,
+ const QString &expr,
bool *success);
- quint32 queryObjectsForLocationRecursive(const QString &file,
- int lineNumber, int columnNumber, bool *success);
- quint32 queryExpressionResult(int objectDebugId,
- const QString &expr,
- bool *success);
- quint32 queryExpressionResultBC(int objectDebugId,
- const QString &expr,
- bool *success);
- quint32 setBindingForObject(int objectDebugId, const QString &propertyName,
- const QVariant &bindingExpression,
- bool isLiteralValue,
- const QString &source, int line, bool *success);
- quint32 resetBindingForObject(int objectDebugId,
- const QString &propertyName, bool *success);
- quint32 setMethodBody(int objectDebugId, const QString &methodName,
- const QString &methodBody, bool *success);
-
- quint32 getId();
+ qint32 setBindingForObject(qint32 objectDebugId, const QString &propertyName,
+ const QVariant &bindingExpression,
+ bool isLiteralValue,
+ const QString &source, qint32 line, bool *success);
+ qint32 resetBindingForObject(qint32 objectDebugId,
+ const QString &propertyName, bool *success);
+ qint32 setMethodBody(qint32 objectDebugId, const QString &methodName,
+ const QString &methodBody, bool *success);
+
+ qint32 getId();
void decode(QPacket &ds, QQmlEngineDebugContextReference &);
void decode(QPacket &ds, QQmlEngineDebugObjectReference &, bool simple);
@@ -153,7 +153,7 @@ public:
bool valid() const;
signals:
- void newObject(int objectId);
+ void newObject(qint32 objectId);
void valueChanged(QByteArray,QVariant);
void result();
diff --git a/src/qmldebug/qqmlenginedebugclient_p_p.h b/src/qmldebug/qqmlenginedebugclient_p_p.h
index 7c992ad3ab..b73da15e9d 100644
--- a/src/qmldebug/qqmlenginedebugclient_p_p.h
+++ b/src/qmldebug/qqmlenginedebugclient_p_p.h
@@ -62,7 +62,7 @@ class QQmlEngineDebugClientPrivate : public QQmlDebugClientPrivate
public:
QQmlEngineDebugClientPrivate(QQmlDebugConnection *connection);
- quint32 nextId = 0;
+ qint32 nextId = 0;
bool valid = false;
QList<QQmlEngineDebugEngineReference> engines;
QQmlEngineDebugContextReference rootContext;
diff --git a/src/qmldevtools/qmldevtools.pro b/src/qmldevtools/qmldevtools.pro
index 45029400b9..bea3c864cd 100644
--- a/src/qmldevtools/qmldevtools.pro
+++ b/src/qmldevtools/qmldevtools.pro
@@ -1,7 +1,7 @@
option(host_build)
TARGET = QtQmlDevTools
QT = core-private
-CONFIG += minimal_syncqt internal_module qmldevtools_build generated_privates
+CONFIG += minimal_syncqt internal_module generated_privates
MODULE_INCNAME = QtQml
INCLUDEPATH += $$OUT_PWD/../qml
@@ -12,11 +12,9 @@ intel_icc: WERROR += -ww2415
clang:if(greaterThan(QT_CLANG_MAJOR_VERSION, 3)|greaterThan(QT_CLANG_MINOR_VERSION, 3)): \
WERROR += -Wno-error=unused-const-variable
-include(../3rdparty/masm/masm-defs.pri)
+include(../qml/common/common.pri)
include(../qml/parser/parser.pri)
-include(../qml/jsruntime/jsruntime.pri)
include(../qml/compiler/compiler.pri)
-include(../qml/memory/memory.pri)
include(../qml/qmldirparser/qmldirparser.pri)
load(qt_module)
diff --git a/src/qmlmodels/configure.json b/src/qmlmodels/configure.json
new file mode 100644
index 0000000000..1ccca5e35e
--- /dev/null
+++ b/src/qmlmodels/configure.json
@@ -0,0 +1,46 @@
+{
+ "module": "qmlmodels",
+ "depends": [
+ "core-private",
+ "qml-private"
+ ],
+
+ "features": {
+ "qml-object-model" : {
+ "label": "QML object model",
+ "purpose": "Provides the ObjectModel and Instantiator QML types.",
+ "section": "QML",
+ "output": [ "privateFeature" ]
+ },
+ "qml-list-model": {
+ "label": "QML list model",
+ "purpose": "Provides the ListModel QML type.",
+ "section": "QML",
+ "condition": "features.qml-itemmodel",
+ "output": [ "privateFeature" ]
+ },
+ "qml-delegate-model": {
+ "label": "QML delegate model",
+ "purpose": "Provides the DelegateModel QML type.",
+ "section": "QML",
+ "condition": "features.qml-object-model && features.qml-itemmodel",
+ "output": [ "privateFeature" ]
+ },
+ "qml-table-model": {
+ "label": "QML table model",
+ "purpose": "Provides the TableModel QML type.",
+ "section": "QML",
+ "condition": "features.qml-itemmodel && features.qml-delegate-model",
+ "output": [ "privateFeature" ]
+ }
+ },
+ "summary": [
+ {
+ "section": "Qt QML Models",
+ "entries": [
+ "qml-list-model",
+ "qml-delegate-model"
+ ]
+ }
+ ]
+}
diff --git a/src/qmlmodels/doc/qtqmlmodels.qdocconf b/src/qmlmodels/doc/qtqmlmodels.qdocconf
new file mode 100644
index 0000000000..a4153f4a53
--- /dev/null
+++ b/src/qmlmodels/doc/qtqmlmodels.qdocconf
@@ -0,0 +1,37 @@
+include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+include($QT_INSTALL_DOCS/config/exampleurl-qtdeclarative.qdocconf)
+
+project = QtQmlModels
+description = Qt Qml Models Reference Documentation
+version = $QT_VERSION
+moduleheader = QtQmlModels
+qhp.projects = QtQmlModels
+
+qhp.QtQmlModels.file = qtqmlmodels.qhp
+qhp.QtQmlModels.namespace = org.qt-project.qtqmlmodels.$QT_VERSION_TAG
+qhp.QtQmlModels.virtualFolder = qtqmlmodels
+qhp.QtQmlModels.indexRoot =
+
+qhp.QtQmlModels.filterAttributes = qtqmlmodels $QT_VERSION qtrefdoc
+qhp.QtQmlModels.customFilters.Qt.name = QtQmlModels $QT_VERSION
+qhp.QtQmlModels.customFilters.Qt.filterAttributes = qtqmlmodels $QT_VERSION
+
+qhp.QtQmlModels.title = QML Types
+qhp.QtQmlModels.indexTitle = Qt QML Models QML Types
+qhp.QtQmlModels.selectors = qmlclass
+qhp.QtQmlModels.sortPages = true
+
+tagfile = qtqmlmodels.tags
+
+depends += qtcore qtqml qtdoc
+
+headerdirs += ..
+
+sourcedirs += .. \
+ ../../imports/models
+
+exampledirs += ../../../examples/qml \
+ ../ \
+ snippets
+
+navigation.qmltypespage = "Qt Qml Models QML Types"
diff --git a/src/qml/doc/snippets/delegatemodel/delegatemodel.qml b/src/qmlmodels/doc/snippets/delegatemodel/delegatemodel.qml
index 1a7baa6b1e..1a7baa6b1e 100644
--- a/src/qml/doc/snippets/delegatemodel/delegatemodel.qml
+++ b/src/qmlmodels/doc/snippets/delegatemodel/delegatemodel.qml
diff --git a/src/qml/doc/snippets/delegatemodel/delegatemodel_rootindex/main.cpp b/src/qmlmodels/doc/snippets/delegatemodel/delegatemodel_rootindex/main.cpp
index a56eb69616..a56eb69616 100644
--- a/src/qml/doc/snippets/delegatemodel/delegatemodel_rootindex/main.cpp
+++ b/src/qmlmodels/doc/snippets/delegatemodel/delegatemodel_rootindex/main.cpp
diff --git a/src/qml/doc/snippets/delegatemodel/delegatemodel_rootindex/view.qml b/src/qmlmodels/doc/snippets/delegatemodel/delegatemodel_rootindex/view.qml
index 2e17eed8f0..2e17eed8f0 100644
--- a/src/qml/doc/snippets/delegatemodel/delegatemodel_rootindex/view.qml
+++ b/src/qmlmodels/doc/snippets/delegatemodel/delegatemodel_rootindex/view.qml
diff --git a/src/qml/doc/snippets/delegatemodel/delegatemodelgroup.qml b/src/qmlmodels/doc/snippets/delegatemodel/delegatemodelgroup.qml
index 8562deeeda..8562deeeda 100644
--- a/src/qml/doc/snippets/delegatemodel/delegatemodelgroup.qml
+++ b/src/qmlmodels/doc/snippets/delegatemodel/delegatemodelgroup.qml
diff --git a/src/qml/doc/snippets/package/Delegate.qml b/src/qmlmodels/doc/snippets/package/Delegate.qml
index 7c73f35c3d..7c73f35c3d 100644
--- a/src/qml/doc/snippets/package/Delegate.qml
+++ b/src/qmlmodels/doc/snippets/package/Delegate.qml
diff --git a/src/qml/doc/snippets/package/view.qml b/src/qmlmodels/doc/snippets/package/view.qml
index 311cc3be8e..311cc3be8e 100644
--- a/src/qml/doc/snippets/package/view.qml
+++ b/src/qmlmodels/doc/snippets/package/view.qml
diff --git a/src/qml/doc/snippets/qml/listmodel/listelements.qml b/src/qmlmodels/doc/snippets/qml/listmodel/listelements.qml
index 12146c1420..12146c1420 100644
--- a/src/qml/doc/snippets/qml/listmodel/listelements.qml
+++ b/src/qmlmodels/doc/snippets/qml/listmodel/listelements.qml
diff --git a/src/qml/doc/snippets/qml/listmodel/listmodel-modify.qml b/src/qmlmodels/doc/snippets/qml/listmodel/listmodel-modify.qml
index f293eff8ec..f293eff8ec 100644
--- a/src/qml/doc/snippets/qml/listmodel/listmodel-modify.qml
+++ b/src/qmlmodels/doc/snippets/qml/listmodel/listmodel-modify.qml
diff --git a/src/qml/doc/snippets/qml/listmodel/listmodel-nested.qml b/src/qmlmodels/doc/snippets/qml/listmodel/listmodel-nested.qml
index 8c193d6a5e..8c193d6a5e 100644
--- a/src/qml/doc/snippets/qml/listmodel/listmodel-nested.qml
+++ b/src/qmlmodels/doc/snippets/qml/listmodel/listmodel-nested.qml
diff --git a/src/qml/doc/snippets/qml/listmodel/listmodel-simple.qml b/src/qmlmodels/doc/snippets/qml/listmodel/listmodel-simple.qml
index d07f868476..d07f868476 100644
--- a/src/qml/doc/snippets/qml/listmodel/listmodel-simple.qml
+++ b/src/qmlmodels/doc/snippets/qml/listmodel/listmodel-simple.qml
diff --git a/src/qml/doc/snippets/qml/listmodel/listmodel.qml b/src/qmlmodels/doc/snippets/qml/listmodel/listmodel.qml
index c2a69d6e8f..c2a69d6e8f 100644
--- a/src/qml/doc/snippets/qml/listmodel/listmodel.qml
+++ b/src/qmlmodels/doc/snippets/qml/listmodel/listmodel.qml
diff --git a/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-complex.qml b/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-complex.qml
new file mode 100644
index 0000000000..104a2209d7
--- /dev/null
+++ b/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-complex.qml
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//![file]
+import QtQuick 2.12
+import QtQuick.Window 2.12
+import Qt.labs.qmlmodels 1.0
+
+Window {
+ width: 400
+ height: 400
+ visible: true
+
+ TableView {
+ anchors.fill: parent
+ columnSpacing: 1
+ rowSpacing: 1
+ boundsBehavior: Flickable.StopAtBounds
+
+ model: TableModel {
+ TableModelColumn {
+ display: function(modelIndex) { return rows[modelIndex.row][0].checked }
+ setDisplay: function(modelIndex, cellData) { rows[modelIndex.row][0].checked = cellData }
+ }
+ TableModelColumn {
+ display: function(modelIndex) { return rows[modelIndex.row][1].amount }
+ setDisplay: function(modelIndex, cellData) { rows[modelIndex.row][1].amount = cellData }
+ }
+ TableModelColumn {
+ display: function(modelIndex) { return rows[modelIndex.row][2].fruitType }
+ setDisplay: function(modelIndex, cellData) { rows[modelIndex.row][2].fruitType = cellData }
+ }
+ TableModelColumn {
+ display: function(modelIndex) { return rows[modelIndex.row][3].fruitName }
+ setDisplay: function(modelIndex, cellData) { rows[modelIndex.row][3].fruitName = cellData }
+ }
+ TableModelColumn {
+ display: function(modelIndex) { return rows[modelIndex.row][4].fruitPrice }
+ setDisplay: function(modelIndex, cellData) { rows[modelIndex.row][4].fruitPrice = cellData }
+ }
+
+ // Each row is one type of fruit that can be ordered
+//![rows]
+ rows: [
+ [
+ // Each object (line) is one cell/column.
+ { checked: false, checkable: true },
+ { amount: 1 },
+ { fruitType: "Apple" },
+ { fruitName: "Granny Smith" },
+ { fruitPrice: 1.50 }
+ ],
+ [
+ { checked: true, checkable: true },
+ { amount: 4 },
+ { fruitType: "Orange" },
+ { fruitName: "Navel" },
+ { fruitPrice: 2.50 }
+ ],
+ [
+ { checked: false, checkable: false },
+ { amount: 1 },
+ { fruitType: "Banana" },
+ { fruitName: "Cavendish" },
+ { fruitPrice: 3.50 }
+ ]
+ ]
+//![rows]
+ }
+//![delegate]
+ delegate: TextInput {
+ text: model.display
+ padding: 12
+ selectByMouse: true
+
+ onAccepted: model.display = text
+
+ Rectangle {
+ anchors.fill: parent
+ color: "#efefef"
+ z: -1
+ }
+ }
+//![delegate]
+ }
+}
+//![file]
diff --git a/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.qml b/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.qml
new file mode 100644
index 0000000000..d3f6176c70
--- /dev/null
+++ b/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.qml
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//![file]
+import QtQuick 2.12
+import QtQuick.Controls 2.5
+import Qt.labs.qmlmodels 1.0
+
+ApplicationWindow {
+ width: 400
+ height: 400
+ visible: true
+
+ TableView {
+ anchors.fill: parent
+ columnSpacing: 1
+ rowSpacing: 1
+ boundsBehavior: Flickable.StopAtBounds
+
+ model: TableModel {
+ TableModelColumn { display: "checked" }
+ TableModelColumn { display: "amount" }
+ TableModelColumn { display: "fruitType" }
+ TableModelColumn { display: "fruitName" }
+ TableModelColumn { display: "fruitPrice" }
+
+ // Each row is one type of fruit that can be ordered
+//![rows]
+ rows: [
+ {
+ // Each property is one cell/column.
+ checked: false,
+ amount: 1,
+ fruitType: "Apple",
+ fruitName: "Granny Smith",
+ fruitPrice: 1.50
+ },
+ {
+ checked: true,
+ amount: 4,
+ fruitType: "Orange",
+ fruitName: "Navel",
+ fruitPrice: 2.50
+ },
+ {
+ checked: false,
+ amount: 1,
+ fruitType: "Banana",
+ fruitName: "Cavendish",
+ fruitPrice: 3.50
+ }
+ ]
+//![rows]
+ }
+//![delegate]
+ delegate: DelegateChooser {
+ DelegateChoice {
+ column: 0
+ delegate: CheckBox {
+ checked: model.display
+ onToggled: model.display = checked
+ }
+ }
+ DelegateChoice {
+ column: 1
+ delegate: SpinBox {
+ value: model.display
+ onValueModified: model.display = value
+ }
+ }
+ DelegateChoice {
+ delegate: TextField {
+ text: model.display
+ selectByMouse: true
+ implicitWidth: 140
+ onAccepted: model.display = text
+ }
+ }
+ }
+//![delegate]
+ }
+}
+//![file]
diff --git a/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.qml b/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.qml
new file mode 100644
index 0000000000..f51c1818c3
--- /dev/null
+++ b/src/qmlmodels/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.qml
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//![file]
+import QtQuick 2.12
+import QtQuick.Window 2.12
+import Qt.labs.qmlmodels 1.0
+
+Window {
+ width: 400
+ height: 400
+ visible: true
+
+ TableView {
+ anchors.fill: parent
+ columnSpacing: 1
+ rowSpacing: 1
+ boundsBehavior: Flickable.StopAtBounds
+
+ model: TableModel {
+ TableModelColumn { display: "checked" }
+ TableModelColumn { display: "amount" }
+ TableModelColumn { display: "fruitType" }
+ TableModelColumn { display: "fruitName" }
+ TableModelColumn { display: "fruitPrice" }
+
+ // Each row is one type of fruit that can be ordered
+//![rows]
+ rows: [
+ {
+ // Each property is one cell/column.
+ checked: false,
+ amount: 1,
+ fruitType: "Apple",
+ fruitName: "Granny Smith",
+ fruitPrice: 1.50
+ },
+ {
+ checked: true,
+ amount: 4,
+ fruitType: "Orange",
+ fruitName: "Navel",
+ fruitPrice: 2.50
+ },
+ {
+ checked: false,
+ amount: 1,
+ fruitType: "Banana",
+ fruitName: "Cavendish",
+ fruitPrice: 3.50
+ }
+ ]
+//![rows]
+ }
+//![delegate]
+ delegate: TextInput {
+ text: model.display
+ padding: 12
+ selectByMouse: true
+
+ onAccepted: model.display = text
+
+ Rectangle {
+ anchors.fill: parent
+ color: "#efefef"
+ z: -1
+ }
+ }
+//![delegate]
+ }
+}
+//![file]
diff --git a/src/qmlmodels/qmlmodels.pro b/src/qmlmodels/qmlmodels.pro
new file mode 100644
index 0000000000..1d733f5bdb
--- /dev/null
+++ b/src/qmlmodels/qmlmodels.pro
@@ -0,0 +1,71 @@
+TARGET = QtQmlModels
+QT = core-private qml-private
+
+QMAKE_DOCS = $$PWD/doc/qtqmlmodels.qdocconf
+
+DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES QT_NO_FOREACH
+
+HEADERS += \
+ $$PWD/qqmlchangeset_p.h \
+ $$PWD/qqmlmodelsmodule_p.h \
+ $$PWD/qtqmlmodelsglobal_p.h \
+ $$PWD/qtqmlmodelsglobal.h
+
+SOURCES += \
+ $$PWD/qqmlchangeset.cpp \
+ $$PWD/qqmlmodelsmodule.cpp
+
+qtConfig(qml-object-model) {
+ SOURCES += \
+ $$PWD/qqmlinstantiator.cpp \
+ $$PWD/qqmlobjectmodel.cpp
+
+ HEADERS += \
+ $$PWD/qqmlinstantiator_p.h \
+ $$PWD/qqmlinstantiator_p_p.h \
+ $$PWD/qqmlobjectmodel_p.h
+}
+
+qtConfig(qml-table-model) {
+ SOURCES += \
+ $$PWD/qqmltableinstancemodel.cpp \
+ $$PWD/qqmltablemodel.cpp \
+ $$PWD/qqmltablemodelcolumn.cpp
+
+ HEADERS += \
+ $$PWD/qqmltableinstancemodel_p.h \
+ $$PWD/qqmltablemodel_p.h \
+ $$PWD/qqmltablemodelcolumn_p.h
+}
+
+qtConfig(qml-list-model) {
+ SOURCES += \
+ $$PWD/qqmllistmodel.cpp \
+ $$PWD/qqmllistmodelworkeragent.cpp
+
+ HEADERS += \
+ $$PWD/qqmllistmodel_p.h \
+ $$PWD/qqmllistmodel_p_p.h \
+ $$PWD/qqmllistmodelworkeragent_p.h
+}
+
+qtConfig(qml-delegate-model) {
+ SOURCES += \
+ $$PWD/qqmladaptormodel.cpp \
+ $$PWD/qqmldelegatemodel.cpp \
+ $$PWD/qqmldelegatecomponent.cpp \
+ $$PWD/qqmllistaccessor.cpp \
+ $$PWD/qqmllistcompositor.cpp \
+ $$PWD/qquickpackage.cpp
+
+ HEADERS += \
+ $$PWD/qqmladaptormodel_p.h \
+ $$PWD/qqmldelegatemodel_p.h \
+ $$PWD/qqmldelegatemodel_p_p.h \
+ $$PWD/qqmldelegatecomponent_p.h \
+ $$PWD/qqmllistaccessor_p.h \
+ $$PWD/qqmllistcompositor_p.h \
+ $$PWD/qquickpackage_p.h
+}
+
+load(qt_module)
diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qmlmodels/qqmladaptormodel.cpp
index 1ff866eb74..48ff4e7d5b 100644
--- a/src/qml/util/qqmladaptormodel.cpp
+++ b/src/qmlmodels/qqmladaptormodel.cpp
@@ -42,14 +42,13 @@
#include <private/qqmldelegatemodel_p_p.h>
#include <private/qmetaobjectbuilder_p.h>
#include <private/qqmlproperty_p.h>
-#include <private/qv8engine_p.h>
#include <private/qv4value_p.h>
#include <private/qv4functionobject_p.h>
QT_BEGIN_NAMESPACE
-class QQmlAdaptorModelEngineData : public QV8Engine::Deletable
+class QQmlAdaptorModelEngineData : public QV4::ExecutionEngine::Deletable
{
public:
QQmlAdaptorModelEngineData(QV4::ExecutionEngine *v4);
diff --git a/src/qml/util/qqmladaptormodel_p.h b/src/qmlmodels/qqmladaptormodel_p.h
index 8c18466ab5..a4549127af 100644
--- a/src/qml/util/qqmladaptormodel_p.h
+++ b/src/qmlmodels/qqmladaptormodel_p.h
@@ -53,10 +53,12 @@
#include <QtCore/qabstractitemmodel.h>
-#include "private/qqmllistaccessor_p.h"
#include <private/qqmlglobal_p.h>
+#include <private/qqmllistaccessor_p.h>
+#include <private/qtqmlmodelsglobal_p.h>
#include <private/qqmlguard_p.h>
#include <private/qqmlnullablevalue_p.h>
+#include <private/qqmlpropertycache_p.h>
QT_REQUIRE_CONFIG(qml_delegate_model);
@@ -68,7 +70,7 @@ class QQmlDelegateModel;
class QQmlDelegateModelItem;
class QQmlDelegateModelItemMetaType;
-class Q_QML_PRIVATE_EXPORT QQmlAdaptorModel : public QQmlStrongJSQObjectReference<QObject>
+class Q_QMLMODELS_PRIVATE_EXPORT QQmlAdaptorModel : public QQmlStrongJSQObjectReference<QObject>
{
public:
class Accessors
diff --git a/src/qml/util/qqmlchangeset.cpp b/src/qmlmodels/qqmlchangeset.cpp
index ba876b42e2..ba876b42e2 100644
--- a/src/qml/util/qqmlchangeset.cpp
+++ b/src/qmlmodels/qqmlchangeset.cpp
diff --git a/src/qml/util/qqmlchangeset_p.h b/src/qmlmodels/qqmlchangeset_p.h
index 8347a3ff19..5b44d2958c 100644
--- a/src/qml/util/qqmlchangeset_p.h
+++ b/src/qmlmodels/qqmlchangeset_p.h
@@ -53,11 +53,11 @@
#include <QtCore/qdebug.h>
#include <QtCore/qvector.h>
-#include <QtQml/private/qtqmlglobal_p.h>
+#include <QtQmlModels/private/qtqmlmodelsglobal_p.h>
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlChangeSet
+class Q_QMLMODELS_PRIVATE_EXPORT QQmlChangeSet
{
public:
struct MoveKey
@@ -153,8 +153,8 @@ inline uint qHash(const QQmlChangeSet::MoveKey &key) { return qHash(qMakePair(ke
inline bool operator ==(const QQmlChangeSet::MoveKey &l, const QQmlChangeSet::MoveKey &r) {
return l.moveId == r.moveId && l.offset == r.offset; }
-Q_QML_PRIVATE_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet::Change &change);
-Q_QML_PRIVATE_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet &change);
+Q_QMLMODELS_PRIVATE_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet::Change &change);
+Q_QMLMODELS_PRIVATE_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet &change);
QT_END_NAMESPACE
diff --git a/src/qml/types/qqmldelegatecomponent.cpp b/src/qmlmodels/qqmldelegatecomponent.cpp
index 676a524872..cc3b38ec93 100644
--- a/src/qml/types/qqmldelegatecomponent.cpp
+++ b/src/qmlmodels/qqmldelegatecomponent.cpp
@@ -38,7 +38,7 @@
****************************************************************************/
#include "qqmldelegatecomponent_p.h"
-#include <QtQml/private/qqmladaptormodel_p.h>
+#include <QtQmlModels/private/qqmladaptormodel_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/qml/types/qqmldelegatecomponent_p.h b/src/qmlmodels/qqmldelegatecomponent_p.h
index c925ed9a60..1d20f0327b 100644
--- a/src/qml/types/qqmldelegatecomponent_p.h
+++ b/src/qmlmodels/qqmldelegatecomponent_p.h
@@ -51,7 +51,7 @@
// We mean it.
//
-#include <private/qtqmlglobal_p.h>
+#include <private/qtqmlmodelsglobal_p.h>
#include <qqmlcomponent.h>
QT_REQUIRE_CONFIG(qml_delegate_model);
@@ -61,7 +61,7 @@ QT_BEGIN_NAMESPACE
// TODO: consider making QQmlAbstractDelegateComponent public API
class QQmlAbstractDelegateComponentPrivate;
class QQmlAdaptorModel;
-class Q_QML_PRIVATE_EXPORT QQmlAbstractDelegateComponent : public QQmlComponent
+class Q_QMLMODELS_PRIVATE_EXPORT QQmlAbstractDelegateComponent : public QQmlComponent
{
Q_OBJECT
public:
@@ -81,7 +81,7 @@ private:
Q_DISABLE_COPY(QQmlAbstractDelegateComponent)
};
-class Q_QML_PRIVATE_EXPORT QQmlDelegateChoice : public QObject
+class Q_QMLMODELS_PRIVATE_EXPORT QQmlDelegateChoice : public QObject
{
Q_OBJECT
Q_PROPERTY(QVariant roleValue READ roleValue WRITE setRoleValue NOTIFY roleValueChanged)
@@ -120,7 +120,7 @@ private:
QQmlComponent *m_delegate = nullptr;
};
-class Q_QML_PRIVATE_EXPORT QQmlDelegateChooser : public QQmlAbstractDelegateComponent
+class Q_QMLMODELS_PRIVATE_EXPORT QQmlDelegateChooser : public QQmlAbstractDelegateComponent
{
Q_OBJECT
Q_PROPERTY(QString role READ role WRITE setRole NOTIFY roleChanged)
diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp
index f58bab7c09..65c99d9bc0 100644
--- a/src/qml/types/qqmldelegatemodel.cpp
+++ b/src/qmlmodels/qqmldelegatemodel.cpp
@@ -52,7 +52,7 @@
#include <private/qv4value_p.h>
#include <private/qv4functionobject_p.h>
-#include <qv4objectiterator_p.h>
+#include <private/qv4objectiterator_p.h>
QT_BEGIN_NAMESPACE
@@ -123,7 +123,7 @@ DEFINE_OBJECT_VTABLE(QV4::DelegateModelGroupFunction);
-class QQmlDelegateModelEngineData : public QV8Engine::Deletable
+class QQmlDelegateModelEngineData : public QV4::ExecutionEngine::Deletable
{
public:
QQmlDelegateModelEngineData(QV4::ExecutionEngine *v4);
@@ -454,7 +454,8 @@ void QQmlDelegateModel::setDelegate(QQmlComponent *delegate)
}
if (d->m_delegate == delegate)
return;
- bool wasValid = d->m_delegate != nullptr;
+ if (d->m_complete)
+ _q_itemsRemoved(0, d->m_count);
d->m_delegate.setObject(delegate, this);
d->m_delegateValidated = false;
if (d->m_delegateChooser)
@@ -470,7 +471,11 @@ void QQmlDelegateModel::setDelegate(QQmlComponent *delegate)
[d](){ d->delegateChanged(); });
}
}
- d->delegateChanged(d->m_delegate, wasValid);
+ if (d->m_complete) {
+ _q_itemsInserted(0, d->adaptorModelCount());
+ d->requestMoreIfNecessary();
+ }
+ emit delegateChanged();
}
/*!
@@ -1127,7 +1132,7 @@ QQmlIncubator::Status QQmlDelegateModel::incubationStatus(int index)
return QQmlIncubator::Ready;
}
-QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index, const QString &name)
+QVariant QQmlDelegateModelPrivate::variantValue(QQmlListCompositor::Group group, int index, const QString &name)
{
Compositor::iterator it = m_compositor.find(group, index);
if (QQmlAdaptorModel *model = it.list<QQmlAdaptorModel>()) {
@@ -1139,20 +1144,20 @@ QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index
while (dot > 0) {
QObject *obj = qvariant_cast<QObject*>(value);
if (!obj)
- return QString();
- int from = dot+1;
+ return QVariant();
+ const int from = dot + 1;
dot = name.indexOf(QLatin1Char('.'), from);
value = obj->property(name.midRef(from, dot - from).toUtf8());
}
- return value.toString();
+ return value;
}
- return QString();
+ return QVariant();
}
-QString QQmlDelegateModel::stringValue(int index, const QString &name)
+QVariant QQmlDelegateModel::variantValue(int index, const QString &role)
{
Q_D(QQmlDelegateModel);
- return d->stringValue(d->m_compositorGroup, index, name);
+ return d->variantValue(d->m_compositorGroup, index, role);
}
int QQmlDelegateModel::indexOf(QObject *item, QObject *) const
@@ -2434,17 +2439,15 @@ void QQmlDelegateModelGroupPrivate::setModel(QQmlDelegateModel *m, Compositor::G
bool QQmlDelegateModelGroupPrivate::isChangedConnected()
{
Q_Q(QQmlDelegateModelGroup);
- IS_SIGNAL_CONNECTED(q, QQmlDelegateModelGroup, changed, (const QQmlV4Handle &,const QQmlV4Handle &));
+ IS_SIGNAL_CONNECTED(q, QQmlDelegateModelGroup, changed, (const QJSValue &,const QJSValue &));
}
void QQmlDelegateModelGroupPrivate::emitChanges(QV4::ExecutionEngine *v4)
{
Q_Q(QQmlDelegateModelGroup);
if (isChangedConnected() && !changeSet.isEmpty()) {
- QV4::Scope scope(v4);
- QV4::ScopedValue removed(scope, engineData(scope.engine)->array(v4, changeSet.removes()));
- QV4::ScopedValue inserted(scope, engineData(scope.engine)->array(v4, changeSet.inserts()));
- emit q->changed(QQmlV4Handle(removed), QQmlV4Handle(inserted));
+ emit q->changed(QJSValue(v4, engineData(v4)->array(v4, changeSet.removes())),
+ QJSValue(v4, engineData(v4)->array(v4, changeSet.inserts())));
}
if (changeSet.difference() != 0)
emit q->countChanged();
@@ -2622,18 +2625,18 @@ void QQmlDelegateModelGroup::setDefaultInclude(bool include)
\endlist
*/
-QQmlV4Handle QQmlDelegateModelGroup::get(int index)
+QJSValue QQmlDelegateModelGroup::get(int index)
{
Q_D(QQmlDelegateModelGroup);
if (!d->model)
- return QQmlV4Handle(QV4::Encode::undefined());
+ return QJSValue();
QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
if (!model->m_context || !model->m_context->isValid()) {
- return QQmlV4Handle(QV4::Encode::undefined());
+ return QJSValue();
} else if (index < 0 || index >= model->m_compositor.count(d->group)) {
qmlWarning(this) << tr("get: index out of range");
- return QQmlV4Handle(QV4::Encode::undefined());
+ return QJSValue();
}
Compositor::iterator it = model->m_compositor.find(d->group, index);
@@ -2645,7 +2648,7 @@ QQmlV4Handle QQmlDelegateModelGroup::get(int index)
cacheItem = model->m_adaptorModel.createItem(
model->m_cacheMetaType, it.modelIndex());
if (!cacheItem)
- return QQmlV4Handle(QV4::Encode::undefined());
+ return QJSValue();
cacheItem->groups = it->flags;
model->m_cache.insert(it.cacheIndex, cacheItem);
@@ -2661,7 +2664,7 @@ QQmlV4Handle QQmlDelegateModelGroup::get(int index)
QV4::ScopedObject p(scope, model->m_cacheMetaType->modelItemProto.value());
o->setPrototypeOf(p);
- return QQmlV4Handle(o);
+ return QJSValue(v4, o->asReturnedValue());
}
bool QQmlDelegateModelGroupPrivate::parseIndex(const QV4::Value &value, int *index, Compositor::Group *group) const
@@ -3353,9 +3356,9 @@ QQmlInstanceModel::ReleaseFlags QQmlPartsModel::release(QObject *item)
return flags;
}
-QString QQmlPartsModel::stringValue(int index, const QString &role)
+QVariant QQmlPartsModel::variantValue(int index, const QString &role)
{
- return QQmlDelegateModelPrivate::get(m_model)->stringValue(m_compositorGroup, index, role);
+ return QQmlDelegateModelPrivate::get(m_model)->variantValue(m_compositorGroup, index, role);
}
void QQmlPartsModel::setWatchedRoles(const QList<QByteArray> &roles)
diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qmlmodels/qqmldelegatemodel_p.h
index 0ad8939732..9e846ccc3e 100644
--- a/src/qml/types/qqmldelegatemodel_p.h
+++ b/src/qmlmodels/qqmldelegatemodel_p.h
@@ -51,7 +51,7 @@
// We mean it.
//
-#include <private/qtqmlglobal_p.h>
+#include <private/qtqmlmodelsglobal_p.h>
#include <private/qqmllistcompositor_p.h>
#include <private/qqmlobjectmodel_p.h>
#include <private/qqmlincubator_p.h>
@@ -59,9 +59,6 @@
#include <QtCore/qabstractitemmodel.h>
#include <QtCore/qstringlist.h>
-#include <private/qv8engine_p.h>
-#include <private/qqmlglobal_p.h>
-
QT_REQUIRE_CONFIG(qml_delegate_model);
QT_BEGIN_NAMESPACE
@@ -74,13 +71,13 @@ class QQmlDelegateModelAttached;
class QQmlDelegateModelPrivate;
-class Q_QML_PRIVATE_EXPORT QQmlDelegateModel : public QQmlInstanceModel, public QQmlParserStatus
+class Q_QMLMODELS_PRIVATE_EXPORT QQmlDelegateModel : public QQmlInstanceModel, public QQmlParserStatus
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQmlDelegateModel)
Q_PROPERTY(QVariant model READ model WRITE setModel)
- Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate)
+ Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup)
Q_PROPERTY(QQmlDelegateModelGroup *items READ items CONSTANT) //TODO : worth renaming?
Q_PROPERTY(QQmlDelegateModelGroup *persistedItems READ persistedItems CONSTANT)
@@ -114,7 +111,7 @@ public:
QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override;
ReleaseFlags release(QObject *object) override;
void cancel(int index) override;
- QString stringValue(int index, const QString &role) override;
+ QVariant variantValue(int index, const QString &role) override;
void setWatchedRoles(const QList<QByteArray> &roles) override;
QQmlIncubator::Status incubationStatus(int index) override;
@@ -139,6 +136,7 @@ Q_SIGNALS:
void filterGroupChanged();
void defaultGroupsChanged();
void rootIndexChanged();
+ void delegateChanged();
private Q_SLOTS:
void _q_itemsChanged(int index, int count, const QVector<int> &roles);
@@ -160,7 +158,7 @@ private:
};
class QQmlDelegateModelGroupPrivate;
-class Q_QML_PRIVATE_EXPORT QQmlDelegateModelGroup : public QObject
+class Q_QMLMODELS_PRIVATE_EXPORT QQmlDelegateModelGroup : public QObject
{
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged)
@@ -179,7 +177,7 @@ public:
bool defaultInclude() const;
void setDefaultInclude(bool include);
- Q_INVOKABLE QQmlV4Handle get(int index);
+ Q_INVOKABLE QJSValue get(int index);
public Q_SLOTS:
void insert(QQmlV4Function *);
@@ -195,7 +193,7 @@ Q_SIGNALS:
void countChanged();
void nameChanged();
void defaultIncludeChanged();
- void changed(const QQmlV4Handle &removed, const QQmlV4Handle &inserted);
+ void changed(const QJSValue &removed, const QJSValue &inserted);
private:
Q_DECLARE_PRIVATE(QQmlDelegateModelGroup)
};
diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qmlmodels/qqmldelegatemodel_p_p.h
index 0028849828..92362b8876 100644
--- a/src/qml/types/qqmldelegatemodel_p_p.h
+++ b/src/qmlmodels/qqmldelegatemodel_p_p.h
@@ -69,7 +69,7 @@ typedef QQmlListCompositor Compositor;
class QQmlDelegateModelAttachedMetaObject;
class QQmlAbstractDelegateComponent;
-class Q_QML_PRIVATE_EXPORT QQmlDelegateModelItemMetaType : public QQmlRefCount
+class Q_QMLMODELS_PRIVATE_EXPORT QQmlDelegateModelItemMetaType : public QQmlRefCount
{
public:
QQmlDelegateModelItemMetaType(QV4::ExecutionEngine *engine, QQmlDelegateModel *model, const QStringList &groupNames);
@@ -276,7 +276,7 @@ public:
void requestMoreIfNecessary();
QObject *object(Compositor::Group group, int index, QQmlIncubator::IncubationMode incubationMode);
QQmlDelegateModel::ReleaseFlags release(QObject *object);
- QString stringValue(Compositor::Group group, int index, const QString &name);
+ QVariant variantValue(Compositor::Group group, int index, const QString &name);
void emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package);
void emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package);
void emitCreatedItem(QQDMIncubationTask *incubationTask, QObject *item) {
@@ -378,7 +378,7 @@ public:
bool isValid() const override;
QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override;
ReleaseFlags release(QObject *item) override;
- QString stringValue(int index, const QString &role) override;
+ QVariant variantValue(int index, const QString &role) override;
QList<QByteArray> watchedRoles() const { return m_watchedRoles; }
void setWatchedRoles(const QList<QByteArray> &roles) override;
QQmlIncubator::Status incubationStatus(int index) override;
diff --git a/src/qml/types/qqmlinstantiator.cpp b/src/qmlmodels/qqmlinstantiator.cpp
index a23ec0f2b4..f9d5762f6e 100644
--- a/src/qml/types/qqmlinstantiator.cpp
+++ b/src/qmlmodels/qqmlinstantiator.cpp
@@ -43,9 +43,9 @@
#include <QtQml/QQmlComponent>
#include <QtQml/QQmlInfo>
#include <QtQml/QQmlError>
-#include <QtQml/private/qqmlobjectmodel_p.h>
+#include <QtQmlModels/private/qqmlobjectmodel_p.h>
#if QT_CONFIG(qml_delegate_model)
-#include <QtQml/private/qqmldelegatemodel_p.h>
+#include <QtQmlModels/private/qqmldelegatemodel_p.h>
#endif
QT_BEGIN_NAMESPACE
@@ -220,7 +220,8 @@ void QQmlInstantiatorPrivate::makeModel()
/*!
\qmltype Instantiator
\instantiates QQmlInstantiator
- \inqmlmodule QtQml
+ \inqmlmodule QtQml.Models
+ \ingroup qtquick-models
\brief Dynamically creates objects.
A Instantiator can be used to control the dynamic creation of objects, or to dynamically
@@ -231,6 +232,8 @@ void QQmlInstantiatorPrivate::makeModel()
can also be destroyed dynamically through other means, and the Instantiator will not recreate
them unless the properties of the Instantiator change.
+ \note Instantiator is part of QtQml.Models since version 2.14 and part of QtQml since
+ version 2.1. Importing Instantiator via QtQml is deprecated since Qt 5.14.
*/
QQmlInstantiator::QQmlInstantiator(QObject *parent)
: QObject(*(new QQmlInstantiatorPrivate), parent)
diff --git a/src/qml/types/qqmlinstantiator_p.h b/src/qmlmodels/qqmlinstantiator_p.h
index ca371adc23..87accc304f 100644
--- a/src/qml/types/qqmlinstantiator_p.h
+++ b/src/qmlmodels/qqmlinstantiator_p.h
@@ -53,12 +53,14 @@
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlparserstatus.h>
-#include <QtQml/private/qtqmlglobal_p.h>
+#include <QtQmlModels/private/qtqmlmodelsglobal_p.h>
+
+QT_REQUIRE_CONFIG(qml_object_model);
QT_BEGIN_NAMESPACE
class QQmlInstantiatorPrivate;
-class Q_QML_PRIVATE_EXPORT QQmlInstantiator : public QObject, public QQmlParserStatus
+class Q_QMLMODELS_PRIVATE_EXPORT QQmlInstantiator : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
diff --git a/src/qml/types/qqmlinstantiator_p_p.h b/src/qmlmodels/qqmlinstantiator_p_p.h
index 4c76d5c689..33cc2613e5 100644
--- a/src/qml/types/qqmlinstantiator_p_p.h
+++ b/src/qmlmodels/qqmlinstantiator_p_p.h
@@ -57,9 +57,11 @@
#include <private/qqmlchangeset_p.h>
#include <private/qqmlobjectmodel_p.h>
+QT_REQUIRE_CONFIG(qml_object_model);
+
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlInstantiatorPrivate : public QObjectPrivate
+class Q_QMLMODELS_PRIVATE_EXPORT QQmlInstantiatorPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QQmlInstantiator)
diff --git a/src/qml/types/qqmlitemmodels.qdoc b/src/qmlmodels/qqmlitemmodels.qdoc
index f6e1b0b1b9..2e12dbf656 100644
--- a/src/qml/types/qqmlitemmodels.qdoc
+++ b/src/qmlmodels/qqmlitemmodels.qdoc
@@ -62,7 +62,7 @@
\section1 QModelIndexList Type
- \l QModelIndexList is exposed in QML as a JavaScript array. Conversions are
+ QModelIndexList is exposed in QML as a JavaScript array. Conversions are
automatically made from and to C++. In fact, any JavaScript array can be
converted back to QModelIndexList, with non-QModelIndex objects replaced by
invalid \l{QModelIndex}es.
diff --git a/src/qml/types/qqmlitemselectionmodel.qdoc b/src/qmlmodels/qqmlitemselectionmodel.qdoc
index 43da4f7a55..43da4f7a55 100644
--- a/src/qml/types/qqmlitemselectionmodel.qdoc
+++ b/src/qmlmodels/qqmlitemselectionmodel.qdoc
diff --git a/src/qml/util/qqmllistaccessor.cpp b/src/qmlmodels/qqmllistaccessor.cpp
index 46a11e2bc2..46a11e2bc2 100644
--- a/src/qml/util/qqmllistaccessor.cpp
+++ b/src/qmlmodels/qqmllistaccessor.cpp
diff --git a/src/qml/util/qqmllistaccessor_p.h b/src/qmlmodels/qqmllistaccessor_p.h
index bcd079adef..bcd079adef 100644
--- a/src/qml/util/qqmllistaccessor_p.h
+++ b/src/qmlmodels/qqmllistaccessor_p.h
diff --git a/src/qml/util/qqmllistcompositor.cpp b/src/qmlmodels/qqmllistcompositor.cpp
index 921e86f355..921e86f355 100644
--- a/src/qml/util/qqmllistcompositor.cpp
+++ b/src/qmlmodels/qqmllistcompositor.cpp
diff --git a/src/qml/util/qqmllistcompositor_p.h b/src/qmlmodels/qqmllistcompositor_p.h
index 172040559c..172040559c 100644
--- a/src/qml/util/qqmllistcompositor_p.h
+++ b/src/qmlmodels/qqmllistcompositor_p.h
diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qmlmodels/qqmllistmodel.cpp
index 565e60b3c1..e0a66e7170 100644
--- a/src/qml/types/qqmllistmodel.cpp
+++ b/src/qmlmodels/qqmllistmodel.cpp
@@ -53,6 +53,7 @@
#include <private/qv4objectiterator_p.h>
#include <private/qv4alloca_p.h>
#include <private/qv4lookup_p.h>
+#include <private/qv4qmlcontext_p.h>
#include <qqmlcontext.h>
#include <qqmlinfo.h>
@@ -314,7 +315,7 @@ QString StringOrTranslation::toString(const QQmlListModel *owner) const
}
if (!owner)
return QString();
- return d.asT2()->valueAsString(owner->m_compilationUnit.data());
+ return owner->m_compilationUnit->bindingValueAsString(d.asT2());
}
QString StringOrTranslation::asString() const
@@ -633,7 +634,7 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles)
mo->updateValues(*roles);
}
-void ListModel::set(int elementIndex, QV4::Object *object)
+void ListModel::set(int elementIndex, QV4::Object *object, ListModel::SetElement reason)
{
if (!object)
return;
@@ -683,7 +684,7 @@ void ListModel::set(int elementIndex, QV4::Object *object)
} else if (QV4::DateObject *date = propertyValue->as<QV4::DateObject>()) {
const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime);
if (r.type == ListLayout::Role::DateTime) {
- QDateTime dt = date->toQDateTime();;
+ QDateTime dt = date->toQDateTime();
e->setDateTimePropertyFast(r, dt);
}
} else if (QV4::Object *o = propertyValue->as<QV4::Object>()) {
@@ -698,9 +699,16 @@ void ListModel::set(int elementIndex, QV4::Object *object)
e->setVariantMapFast(role, o);
}
} else if (propertyValue->isNullOrUndefined()) {
- const ListLayout::Role *r = m_layout->getExistingRole(propertyName);
- if (r)
- e->clearProperty(*r);
+ if (reason == SetElement::WasJustInserted) {
+ QQmlError err;
+ auto memberName = propertyName->toString(m_modelCache->engine())->toQString();
+ err.setDescription(QString::fromLatin1("%1 is %2. Adding an object with a %2 member does not create a role for it.").arg(memberName, propertyValue->isNull() ? QLatin1String("null") : QLatin1String("undefined")));
+ qmlWarning(nullptr, err);
+ } else {
+ const ListLayout::Role *r = m_layout->getExistingRole(propertyName);
+ if (r)
+ e->clearProperty(*r);
+ }
}
}
}
@@ -724,13 +732,13 @@ QVector<std::function<void()>> ListModel::remove(int index, int count)
void ListModel::insert(int elementIndex, QV4::Object *object)
{
insertElement(elementIndex);
- set(elementIndex, object);
+ set(elementIndex, object, SetElement::WasJustInserted);
}
int ListModel::append(QV4::Object *object)
{
int elementIndex = appendElement();
- set(elementIndex, object);
+ set(elementIndex, object, SetElement::WasJustInserted);
return elementIndex;
}
@@ -1640,8 +1648,18 @@ PropertyKey ModelObjectOwnPropertyKeyIterator::next(const Object *o, Property *p
if (attrs)
*attrs = QV4::Attr_Data;
if (pd) {
+
QVariant value = that->d()->m_model->data(that->d()->elementIndex(), role.index);
- pd->value = v4->fromVariant(value);
+ if (auto recursiveListModel = qvariant_cast<QQmlListModel*>(value)) {
+ auto size = recursiveListModel->count();
+ auto array = ScopedArrayObject{scope, v4->newArrayObject(size)};
+ for (auto i = 0; i < size; i++) {
+ array->arrayPut(i, QJSValuePrivate::convertedToValue(v4, recursiveListModel->get(i)));
+ }
+ pd->value = array;
+ } else {
+ pd->value = v4->fromVariant(value);
+ }
}
return roleName->toPropertyKey();
}
@@ -2545,7 +2563,7 @@ void QQmlListModel::append(QQmlV4Function *args)
\sa append()
*/
-QQmlV4Handle QQmlListModel::get(int index) const
+QJSValue QQmlListModel::get(int index) const
{
QV4::Scope scope(engine());
QV4::ScopedValue result(scope, QV4::Value::undefinedValue());
@@ -2568,7 +2586,7 @@ QQmlV4Handle QQmlListModel::get(int index) const
}
}
- return QQmlV4Handle(result);
+ return QJSValue(engine(), result->asReturnedValue());
}
/*!
@@ -2587,10 +2605,10 @@ QQmlV4Handle QQmlListModel::get(int index) const
\sa append()
*/
-void QQmlListModel::set(int index, const QQmlV4Handle &handle)
+void QQmlListModel::set(int index, const QJSValue &value)
{
QV4::Scope scope(engine());
- QV4::ScopedObject object(scope, handle);
+ QV4::ScopedObject object(scope, QJSValuePrivate::getValue(&value));
if (!object) {
qmlWarning(this) << tr("set: value is not an object");
@@ -2676,7 +2694,7 @@ void QQmlListModel::sync()
qmlWarning(this) << "List sync() can only be called from a WorkerScript";
}
-bool QQmlListModelParser::verifyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
+bool QQmlListModelParser::verifyProperty(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
{
if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
const quint32 targetObjectIndex = binding->value.objectIndex;
@@ -2707,7 +2725,7 @@ bool QQmlListModelParser::verifyProperty(const QQmlRefPointer<QV4::CompiledData:
return false;
}
} else if (binding->type == QV4::CompiledData::Binding::Type_Script) {
- QString scriptStr = binding->valueAsScriptString(compilationUnit.data());
+ QString scriptStr = compilationUnit->bindingValueAsScriptString(binding);
if (!binding->isFunctionExpression() && !definesEmptyList(scriptStr)) {
QByteArray script = scriptStr.toUtf8();
bool ok;
@@ -2722,7 +2740,9 @@ bool QQmlListModelParser::verifyProperty(const QQmlRefPointer<QV4::CompiledData:
return true;
}
-bool QQmlListModelParser::applyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex)
+bool QQmlListModelParser::applyProperty(
+ const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex)
{
const QString elementName = compilationUnit->stringAt(binding->propertyNameIndex);
@@ -2759,15 +2779,15 @@ bool QQmlListModelParser::applyProperty(const QQmlRefPointer<QV4::CompiledData::
if (binding->isTranslationBinding()) {
value = QVariant::fromValue<const QV4::CompiledData::Binding*>(binding);
} else if (binding->evaluatesToString()) {
- value = binding->valueAsString(compilationUnit.data());
+ value = compilationUnit->bindingValueAsString(binding);
} else if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- value = binding->valueAsNumber(compilationUnit->constants);
+ value = compilationUnit->bindingValueAsNumber(binding);
} else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) {
value = binding->valueAsBoolean();
} else if (binding->type == QV4::CompiledData::Binding::Type_Null) {
value = QVariant::fromValue(nullptr);
} else if (binding->type == QV4::CompiledData::Binding::Type_Script) {
- QString scriptStr = binding->valueAsScriptString(compilationUnit.data());
+ QString scriptStr = compilationUnit->bindingValueAsScriptString(binding);
if (definesEmptyList(scriptStr)) {
const ListLayout::Role &role = model->getOrCreateListRole(elementName);
ListModel *emptyModel = new ListModel(role.subLayout, nullptr);
@@ -2802,7 +2822,7 @@ bool QQmlListModelParser::applyProperty(const QQmlRefPointer<QV4::CompiledData::
return roleSet;
}
-void QQmlListModelParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
+void QQmlListModelParser::verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
{
listElementTypeName = QString(); // unknown
@@ -2817,7 +2837,7 @@ void QQmlListModelParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData:
}
}
-void QQmlListModelParser::applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
+void QQmlListModelParser::applyBindings(QObject *obj, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
{
QQmlListModel *rv = static_cast<QQmlListModel *>(obj);
diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qmlmodels/qqmllistmodel_p.h
index 95b797c898..10d67c1c6f 100644
--- a/src/qml/types/qqmllistmodel_p.h
+++ b/src/qmlmodels/qqmllistmodel_p.h
@@ -51,7 +51,7 @@
// We mean it.
//
-#include <qqml.h>
+#include <private/qtqmlmodelsglobal_p.h>
#include <private/qqmlcustomparser_p.h>
#include <QtCore/QObject>
@@ -77,11 +77,12 @@ namespace QV4 {
struct ModelObject;
}
-class Q_QML_PRIVATE_EXPORT QQmlListModel : public QAbstractListModel
+class Q_QMLMODELS_PRIVATE_EXPORT QQmlListModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged)
Q_PROPERTY(bool dynamicRoles READ dynamicRoles WRITE setDynamicRoles)
+ Q_PROPERTY(QObject *agent READ agent CONSTANT REVISION(14))
public:
QQmlListModel(QObject *parent=nullptr);
@@ -100,8 +101,8 @@ public:
Q_INVOKABLE void remove(QQmlV4Function *args);
Q_INVOKABLE void append(QQmlV4Function *args);
Q_INVOKABLE void insert(QQmlV4Function *args);
- Q_INVOKABLE QQmlV4Handle get(int index) const;
- Q_INVOKABLE void set(int index, const QQmlV4Handle &);
+ Q_INVOKABLE QJSValue get(int index) const;
+ Q_INVOKABLE void set(int index, const QJSValue &value);
Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value);
Q_INVOKABLE void move(int from, int to, int count);
Q_INVOKABLE void sync();
@@ -134,9 +135,9 @@ private:
inline bool canMove(int from, int to, int n) const { return !(from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0); }
- QQmlListModelWorkerAgent *m_agent;
+ mutable QQmlListModelWorkerAgent *m_agent;
mutable QV4::ExecutionEngine *m_engine;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> m_compilationUnit;
bool m_mainThread;
bool m_primary;
@@ -187,13 +188,13 @@ public:
QQmlListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {}
- void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
- void applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
+ void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
+ void applyBindings(QObject *obj, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
private:
- bool verifyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding);
+ bool verifyProperty(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding);
// returns true if a role was set
- bool applyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex);
+ bool applyProperty(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex);
static bool definesEmptyList(const QString &);
diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qmlmodels/qqmllistmodel_p_p.h
index 2876c71de6..2ad5158050 100644
--- a/src/qml/types/qqmllistmodel_p_p.h
+++ b/src/qmlmodels/qqmllistmodel_p_p.h
@@ -52,6 +52,7 @@
//
#include "qqmllistmodel_p.h"
+#include <private/qtqmlmodelsglobal_p.h>
#include <private/qqmlengine_p.h>
#include <private/qqmlopenmetaobject_p.h>
#include <private/qv4qobjectwrapper_p.h>
@@ -380,8 +381,10 @@ public:
return elements.count();
}
+ enum class SetElement {WasJustInserted, IsCurrentlyUpdated};
+
void set(int elementIndex, QV4::Object *object, QVector<int> *roles);
- void set(int elementIndex, QV4::Object *object);
+ void set(int elementIndex, QV4::Object *object, SetElement reason = SetElement::IsCurrentlyUpdated);
int append(QV4::Object *object);
void insert(int elementIndex, QV4::Object *object);
diff --git a/src/qml/types/qqmllistmodelworkeragent.cpp b/src/qmlmodels/qqmllistmodelworkeragent.cpp
index fe3eaa3198..7e92810f78 100644
--- a/src/qml/types/qqmllistmodelworkeragent.cpp
+++ b/src/qmlmodels/qqmllistmodelworkeragent.cpp
@@ -66,9 +66,17 @@ QQmlListModelWorkerAgent::~QQmlListModelWorkerAgent()
mutex.unlock();
}
+QV4::ExecutionEngine *QQmlListModelWorkerAgent::engine() const
+{
+ return m_copy->m_engine;
+}
+
void QQmlListModelWorkerAgent::setEngine(QV4::ExecutionEngine *eng)
{
- m_copy->m_engine = eng;
+ if (eng != m_copy->m_engine) {
+ m_copy->m_engine = eng;
+ emit engineChanged(eng);
+ }
}
void QQmlListModelWorkerAgent::addref()
@@ -114,12 +122,12 @@ void QQmlListModelWorkerAgent::insert(QQmlV4Function *args)
m_copy->insert(args);
}
-QQmlV4Handle QQmlListModelWorkerAgent::get(int index) const
+QJSValue QQmlListModelWorkerAgent::get(int index) const
{
return m_copy->get(index);
}
-void QQmlListModelWorkerAgent::set(int index, const QQmlV4Handle &value)
+void QQmlListModelWorkerAgent::set(int index, const QJSValue &value)
{
m_copy->set(index, value);
}
diff --git a/src/qml/types/qqmllistmodelworkeragent_p.h b/src/qmlmodels/qqmllistmodelworkeragent_p.h
index ae2d4b11e0..1ef27cea3f 100644
--- a/src/qml/types/qqmllistmodelworkeragent_p.h
+++ b/src/qmlmodels/qqmllistmodelworkeragent_p.h
@@ -51,13 +51,13 @@
// We mean it.
//
-#include <qqml.h>
+#include <qtqmlmodelsglobal_p.h>
#include <QEvent>
#include <QMutex>
#include <QWaitCondition>
-#include <private/qv8engine_p.h>
+#include <private/qv4engine_p.h>
QT_REQUIRE_CONFIG(qml_list_model);
@@ -70,14 +70,17 @@ class QQmlListModelWorkerAgent : public QObject
{
Q_OBJECT
Q_PROPERTY(int count READ count)
+ Q_PROPERTY(QV4::ExecutionEngine *engine READ engine WRITE setEngine NOTIFY engineChanged)
public:
QQmlListModelWorkerAgent(QQmlListModel *);
~QQmlListModelWorkerAgent();
+
+ QV4::ExecutionEngine *engine() const;
void setEngine(QV4::ExecutionEngine *eng);
- void addref();
- void release();
+ Q_INVOKABLE void addref();
+ Q_INVOKABLE void release();
int count() const;
@@ -85,30 +88,17 @@ public:
Q_INVOKABLE void remove(QQmlV4Function *args);
Q_INVOKABLE void append(QQmlV4Function *args);
Q_INVOKABLE void insert(QQmlV4Function *args);
- Q_INVOKABLE QQmlV4Handle get(int index) const;
- Q_INVOKABLE void set(int index, const QQmlV4Handle &);
+ Q_INVOKABLE QJSValue get(int index) const;
+ Q_INVOKABLE void set(int index, const QJSValue &value);
Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value);
Q_INVOKABLE void move(int from, int to, int count);
Q_INVOKABLE void sync();
- struct VariantRef
- {
- VariantRef() : a(nullptr) {}
- VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); }
- VariantRef(QQmlListModelWorkerAgent *_a) : a(_a) { if (a) a->addref(); }
- ~VariantRef() { if (a) a->release(); }
-
- VariantRef &operator=(const VariantRef &o) {
- if (o.a) o.a->addref();
- if (a) a->release();
- a = o.a;
- return *this;
- }
-
- QQmlListModelWorkerAgent *a;
- };
-
void modelDestroyed();
+
+signals:
+ void engineChanged(QV4::ExecutionEngine *engine);
+
protected:
bool event(QEvent *) override;
@@ -134,7 +124,5 @@ private:
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QQmlListModelWorkerAgent::VariantRef)
-
#endif // QQUICKLISTMODELWORKERAGENT_P_H
diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qmlmodels/qqmlmodelsmodule.cpp
index bb72771cd9..d569d8e23c 100644
--- a/src/qml/types/qqmlmodelsmodule.cpp
+++ b/src/qmlmodels/qqmlmodelsmodule.cpp
@@ -38,18 +38,65 @@
****************************************************************************/
#include "qqmlmodelsmodule_p.h"
+#include <private/qtqmlmodelsglobal_p.h>
+
+#if QT_CONFIG(itemmodel)
#include <QtCore/qitemselectionmodel.h>
+#endif
#if QT_CONFIG(qml_list_model)
#include <private/qqmllistmodel_p.h>
#endif
#if QT_CONFIG(qml_delegate_model)
#include <private/qqmldelegatemodel_p.h>
#include <private/qqmldelegatecomponent_p.h>
+#include <private/qquickpackage_p.h>
#endif
+#if QT_CONFIG(qml_object_model)
#include <private/qqmlobjectmodel_p.h>
+#include <private/qqmlinstantiator_p.h>
+#endif
+#if QT_CONFIG(qml_table_model)
+#include <private/qqmltablemodel_p.h>
+#include <private/qqmltablemodelcolumn_p.h>
+#endif
QT_BEGIN_NAMESPACE
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+
+void QQmlModelsModule::registerQmlTypes()
+{
+ // Don't add anything here. These are only for backwards compatibility.
+#if QT_CONFIG(qml_object_model)
+ qmlRegisterType<QQmlInstantiator>("QtQml", 2, 1, "Instantiator"); // Only available in >= 2.1
+ qmlRegisterAnonymousType<QQmlInstanceModel>("QtQml", 2);
+#endif
+}
+
+void QQmlModelsModule::registerQuickTypes()
+{
+ // Don't add anything here. These are only for backwards compatibility.
+
+ const char uri[] = "QtQuick";
+
+#if QT_CONFIG(qml_object_model)
+ qmlRegisterType<QQmlInstantiator>(uri, 2, 1, "Instantiator");
+ qmlRegisterAnonymousType<QQmlInstanceModel>(uri, 2);
+ qmlRegisterType<QQmlObjectModel>(uri, 2, 0, "VisualItemModel");
+#endif
+#if QT_CONFIG(qml_list_model)
+ qmlRegisterType<QQmlListElement>(uri, 2, 0, "ListElement");
+ qmlRegisterCustomType<QQmlListModel>(uri, 2, 0, "ListModel", new QQmlListModelParser);
+#endif
+#if QT_CONFIG(qml_delegate_model)
+ qmlRegisterType<QQmlDelegateModel>(uri, 2, 0, "VisualDataModel");
+ qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 0, "VisualDataGroup");
+ qmlRegisterType<QQuickPackage>(uri, 2, 0, "Package");
+#endif
+}
+
+#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+
void QQmlModelsModule::defineModule()
{
const char uri[] = "QtQml.Models";
@@ -61,11 +108,17 @@ void QQmlModelsModule::defineModule()
#if QT_CONFIG(qml_delegate_model)
qmlRegisterType<QQmlDelegateModel>(uri, 2, 1, "DelegateModel");
qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 1, "DelegateModelGroup");
+ qmlRegisterType<QQuickPackage>(uri, 2, 14, "Package");
#endif
+#if QT_CONFIG(qml_object_model)
qmlRegisterType<QQmlObjectModel>(uri, 2, 1, "ObjectModel");
qmlRegisterType<QQmlObjectModel,3>(uri, 2, 3, "ObjectModel");
-
+ qmlRegisterType<QQmlInstantiator>(uri, 2, 14, "Instantiator");
+ qmlRegisterAnonymousType<QQmlInstanceModel>(uri, 2);
+#endif
+#if QT_CONFIG(itemmodel)
qmlRegisterType<QItemSelectionModel>(uri, 2, 2, "ItemSelectionModel");
+#endif
}
void QQmlModelsModule::defineLabsModule()
@@ -77,6 +130,10 @@ void QQmlModelsModule::defineLabsModule()
qmlRegisterType<QQmlDelegateChooser>(uri, 1, 0, "DelegateChooser");
qmlRegisterType<QQmlDelegateChoice>(uri, 1, 0, "DelegateChoice");
#endif
+#if QT_CONFIG(qml_table_model)
+ qmlRegisterType<QQmlTableModel>(uri, 1, 0, "TableModel");
+ qmlRegisterType<QQmlTableModelColumn>(uri, 1, 0, "TableModelColumn");
+#endif
}
QT_END_NAMESPACE
diff --git a/src/qml/types/qqmlmodelsmodule_p.h b/src/qmlmodels/qqmlmodelsmodule_p.h
index 939ecc1500..7e02578db9 100644
--- a/src/qml/types/qqmlmodelsmodule_p.h
+++ b/src/qmlmodels/qqmlmodelsmodule_p.h
@@ -51,13 +51,18 @@
// We mean it.
//
-#include <private/qtqmlglobal_p.h>
+#include <private/qtqmlmodelsglobal_p.h>
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlModelsModule
+class Q_QMLMODELS_PRIVATE_EXPORT QQmlModelsModule
{
public:
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ static void registerQmlTypes();
+ static void registerQuickTypes();
+#endif
+
static void defineModule();
static void defineLabsModule();
};
diff --git a/src/qml/types/qqmlobjectmodel.cpp b/src/qmlmodels/qqmlobjectmodel.cpp
index 525920c6e0..7409178616 100644
--- a/src/qml/types/qqmlobjectmodel.cpp
+++ b/src/qmlmodels/qqmlobjectmodel.cpp
@@ -273,12 +273,12 @@ QQmlInstanceModel::ReleaseFlags QQmlObjectModel::release(QObject *item)
return nullptr;
}
-QString QQmlObjectModel::stringValue(int index, const QString &name)
+QVariant QQmlObjectModel::variantValue(int index, const QString &role)
{
Q_D(QQmlObjectModel);
if (index < 0 || index >= d->children.count())
return QString();
- return QQmlEngine::contextForObject(d->children.at(index).item)->contextProperty(name).toString();
+ return QQmlEngine::contextForObject(d->children.at(index).item)->contextProperty(role);
}
QQmlIncubator::Status QQmlObjectModel::incubationStatus(int)
diff --git a/src/qml/types/qqmlobjectmodel_p.h b/src/qmlmodels/qqmlobjectmodel_p.h
index 4ac4f1c65b..78a5615ae2 100644
--- a/src/qml/types/qqmlobjectmodel_p.h
+++ b/src/qmlmodels/qqmlobjectmodel_p.h
@@ -51,18 +51,20 @@
// We mean it.
//
-#include <private/qtqmlglobal_p.h>
+#include <private/qtqmlmodelsglobal_p.h>
#include <private/qqmlincubator_p.h>
#include <QtQml/qqml.h>
#include <QtCore/qobject.h>
+QT_REQUIRE_CONFIG(qml_object_model);
+
QT_BEGIN_NAMESPACE
class QObject;
class QQmlChangeSet;
class QAbstractItemModel;
-class Q_QML_PRIVATE_EXPORT QQmlInstanceModel : public QObject
+class Q_QMLMODELS_PRIVATE_EXPORT QQmlInstanceModel : public QObject
{
Q_OBJECT
@@ -79,7 +81,8 @@ public:
virtual QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) = 0;
virtual ReleaseFlags release(QObject *object) = 0;
virtual void cancel(int) {}
- virtual QString stringValue(int, const QString &) = 0;
+ QString stringValue(int index, const QString &role) { return variantValue(index, role).toString(); }
+ virtual QVariant variantValue(int, const QString &) = 0;
virtual void setWatchedRoles(const QList<QByteArray> &roles) = 0;
virtual QQmlIncubator::Status incubationStatus(int index) = 0;
@@ -103,7 +106,7 @@ private:
class QQmlObjectModelAttached;
class QQmlObjectModelPrivate;
-class Q_QML_PRIVATE_EXPORT QQmlObjectModel : public QQmlInstanceModel
+class Q_QMLMODELS_PRIVATE_EXPORT QQmlObjectModel : public QQmlInstanceModel
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQmlObjectModel)
@@ -119,7 +122,7 @@ public:
bool isValid() const override;
QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override;
ReleaseFlags release(QObject *object) override;
- QString stringValue(int index, const QString &role) override;
+ QVariant variantValue(int index, const QString &role) override;
void setWatchedRoles(const QList<QByteArray> &) override {}
QQmlIncubator::Status incubationStatus(int index) override;
diff --git a/src/qml/types/qqmltableinstancemodel.cpp b/src/qmlmodels/qqmltableinstancemodel.cpp
index 2170e2daec..b244a007e5 100644
--- a/src/qml/types/qqmltableinstancemodel.cpp
+++ b/src/qmlmodels/qqmltableinstancemodel.cpp
@@ -43,7 +43,7 @@
#include <QtCore/QTimer>
#include <QtQml/private/qqmlincubator_p.h>
-#include <QtQml/private/qqmlchangeset_p.h>
+#include <QtQmlModels/private/qqmlchangeset_p.h>
#include <QtQml/private/qqmlcomponent_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/qml/types/qqmltableinstancemodel_p.h b/src/qmlmodels/qqmltableinstancemodel_p.h
index 3dd5c4e4ce..ce5a37bc98 100644
--- a/src/qml/types/qqmltableinstancemodel_p.h
+++ b/src/qmlmodels/qqmltableinstancemodel_p.h
@@ -51,8 +51,10 @@
// We mean it.
//
-#include <QtQml/private/qqmldelegatemodel_p.h>
-#include <QtQml/private/qqmldelegatemodel_p_p.h>
+#include <QtQmlModels/private/qqmldelegatemodel_p.h>
+#include <QtQmlModels/private/qqmldelegatemodel_p_p.h>
+
+QT_REQUIRE_CONFIG(qml_table_model);
QT_BEGIN_NAMESPACE
@@ -79,7 +81,7 @@ public:
QQmlTableInstanceModel *tableInstanceModel = nullptr;
};
-class Q_QML_PRIVATE_EXPORT QQmlTableInstanceModel : public QQmlInstanceModel
+class Q_QMLMODELS_PRIVATE_EXPORT QQmlTableInstanceModel : public QQmlInstanceModel
{
Q_OBJECT
@@ -122,7 +124,7 @@ public:
QQmlIncubator::Status incubationStatus(int index) override;
- QString stringValue(int, const QString &) override { Q_UNREACHABLE(); return QString(); }
+ QVariant variantValue(int, const QString &) override { Q_UNREACHABLE(); return QVariant(); }
void setWatchedRoles(const QList<QByteArray> &) override { Q_UNREACHABLE(); }
int indexOf(QObject *, QObject *) const override { Q_UNREACHABLE(); return 0; }
diff --git a/src/qmlmodels/qqmltablemodel.cpp b/src/qmlmodels/qqmltablemodel.cpp
new file mode 100644
index 0000000000..f190ad86b1
--- /dev/null
+++ b/src/qmlmodels/qqmltablemodel.cpp
@@ -0,0 +1,1059 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmltablemodel_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtQml/qqmlinfo.h>
+#include <QtQml/qqmlengine.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcTableModel, "qt.qml.tablemodel")
+
+/*!
+ \qmltype TableModel
+ \instantiates QQmlTableModel
+ \inqmlmodule Qt.labs.qmlmodels
+ \brief Encapsulates a simple table model.
+ \since 5.14
+
+ The TableModel type stores JavaScript/JSON objects as data for a table
+ model that can be used with \l TableView. It is intended to support
+ very simple models without requiring the creation of a custom
+ QAbstractTableModel subclass in C++.
+
+ \snippet qml/tablemodel/fruit-example-simpledelegate.qml file
+
+ The model's initial row data is set with either the \l rows property or by
+ calling \l appendRow(). Each column in the model is specified by declaring
+ a \l TableModelColumn instance, where the order of each instance determines
+ its column index. Once the model's \l Component::completed() signal has been
+ emitted, the columns and roles will have been established and are then
+ fixed for the lifetime of the model.
+
+ To access a specific row, the \l getRow() function can be used.
+ It's also possible to access the model's JavaScript data
+ directly via the \l rows property, but it is not possible to
+ modify the model data this way.
+
+ To add new rows, use \l appendRow() and \l insertRow(). To modify
+ existing rows, use \l setRow(), \l moveRow(), \l removeRow(), and
+ \l clear().
+
+ It is also possible to modify the model's data via the delegate,
+ as shown in the example above:
+
+ \snippet qml/tablemodel/fruit-example-simpledelegate.qml delegate
+
+ If the type of the data at the modified role does not match the type of the
+ data that is set, it will be automatically converted via
+ \l {QVariant::canConvert()}{QVariant}.
+
+ \section1 Supported Row Data Structures
+
+ TableModel is designed to work with JavaScript/JSON data, where each row
+ is a simple key-pair object:
+
+ \code
+ {
+ // Each property is one cell/column.
+ checked: false,
+ amount: 1,
+ fruitType: "Apple",
+ fruitName: "Granny Smith",
+ fruitPrice: 1.50
+ },
+ // ...
+ \endcode
+
+ As model manipulation in Qt is done via row and column indices,
+ and because object keys are unordered, each column must be specified via
+ TableModelColumn. This allows mapping Qt's built-in roles to any property
+ in each row object.
+
+ Complex row structures are supported, but with limited functionality.
+ As TableModel has no way of knowing how each row is structured,
+ it cannot manipulate it. As a consequence of this, the copy of the
+ model data that TableModel has stored in \l rows is not kept in sync
+ with the source data that was set in QML. For these reasons, TableModel
+ relies on the user to handle simple data manipulation.
+
+ For example, suppose you wanted to have several roles per column. One way
+ of doing this is to use a data source where each row is an array and each
+ cell is an object. To use this data source with TableModel, define a
+ getter and setter:
+
+ \code
+ TableModel {
+ TableModelColumn {
+ display: function(modelIndex) { return rows[modelIndex.row][0].checked }
+ setDisplay: function(modelIndex, cellData) { rows[modelIndex.row][0].checked = cellData }
+ }
+ // ...
+
+ rows: [
+ [
+ { checked: false, checkable: true },
+ { amount: 1 },
+ { fruitType: "Apple" },
+ { fruitName: "Granny Smith" },
+ { fruitPrice: 1.50 }
+ ]
+ // ...
+ ]
+ }
+ \endcode
+
+ The row above is one example of a complex row.
+
+ \note Row manipulation functions such as \l appendRow(), \l removeRow(),
+ etc. are not supported when using complex rows.
+
+ \section1 Using DelegateChooser with TableModel
+
+ For most real world use cases, it is recommended to use DelegateChooser
+ as the delegate of a TableView that uses TableModel. This allows you to
+ use specific roles in the relevant delegates. For example, the snippet
+ above can be rewritten to use DelegateChooser like so:
+
+ \snippet qml/tablemodel/fruit-example-delegatechooser.qml file
+
+ The most specific delegates are declared first: the columns at index \c 0
+ and \c 1 have \c bool and \c integer data types, so they use a
+ \l [QtQuickControls2]{CheckBox} and \l [QtQuickControls2]{SpinBox},
+ respectively. The remaining columns can simply use a
+ \l [QtQuickControls2]{TextField}, and so that delegate is declared
+ last as a fallback.
+
+ \sa TableModelColumn, TableView, QAbstractTableModel
+*/
+
+QQmlTableModel::QQmlTableModel(QObject *parent)
+ : QAbstractTableModel(parent)
+{
+}
+
+QQmlTableModel::~QQmlTableModel()
+{
+}
+
+/*!
+ \qmlproperty object TableModel::rows
+
+ This property holds the model data in the form of an array of rows:
+
+ \snippet qml/tablemodel/fruit-example-simpledelegate.qml rows
+
+ \sa getRow(), setRow(), moveRow(), appendRow(), insertRow(), clear(), rowCount, columnCount
+*/
+QVariant QQmlTableModel::rows() const
+{
+ return mRows;
+}
+
+void QQmlTableModel::setRows(const QVariant &rows)
+{
+ if (rows.userType() != qMetaTypeId<QJSValue>()) {
+ qmlWarning(this) << "setRows(): \"rows\" must be an array; actual type is " << rows.typeName();
+ return;
+ }
+
+ const QJSValue rowsAsJSValue = rows.value<QJSValue>();
+ const QVariantList rowsAsVariantList = rowsAsJSValue.toVariant().toList();
+ if (rowsAsVariantList == mRows) {
+ // No change.
+ return;
+ }
+
+ if (!componentCompleted) {
+ // Store the rows until we can call doSetRows() after component completion.
+ mRows = rowsAsVariantList;
+ return;
+ }
+
+ doSetRows(rowsAsVariantList);
+}
+
+void QQmlTableModel::doSetRows(const QVariantList &rowsAsVariantList)
+{
+ Q_ASSERT(componentCompleted);
+
+ // By now, all TableModelColumns should have been set.
+ if (mColumns.isEmpty()) {
+ qmlWarning(this) << "No TableModelColumns were set; model will be empty";
+ return;
+ }
+
+ const bool firstTimeValidRowsHaveBeenSet = mColumnMetadata.isEmpty();
+ if (!firstTimeValidRowsHaveBeenSet) {
+ // This is not the first time rows have been set; validate each one.
+ for (int rowIndex = 0; rowIndex < rowsAsVariantList.size(); ++rowIndex) {
+ // validateNewRow() expects a QVariant wrapping a QJSValue, so to
+ // simplify the code, just create one here.
+ const QVariant row = QVariant::fromValue(rowsAsVariantList.at(rowIndex));
+ if (!validateNewRow("setRows()", row, rowIndex, SetRowsOperation))
+ return;
+ }
+ }
+
+ const int oldRowCount = mRowCount;
+ const int oldColumnCount = mColumnCount;
+
+ beginResetModel();
+
+ // We don't clear the column or role data, because a TableModel should not be reused in that way.
+ // Once it has valid data, its columns and roles are fixed.
+ mRows = rowsAsVariantList;
+ mRowCount = mRows.size();
+
+ // Gather metadata the first time rows is set.
+ if (firstTimeValidRowsHaveBeenSet && !mRows.isEmpty())
+ fetchColumnMetadata();
+
+ endResetModel();
+
+ emit rowsChanged();
+
+ if (mRowCount != oldRowCount)
+ emit rowCountChanged();
+ if (mColumnCount != oldColumnCount)
+ emit columnCountChanged();
+}
+
+QQmlTableModel::ColumnRoleMetadata QQmlTableModel::fetchColumnRoleData(const QString &roleNameKey,
+ QQmlTableModelColumn *tableModelColumn, int columnIndex) const
+{
+ const QVariant firstRow = mRows.first();
+ ColumnRoleMetadata roleData;
+
+ QJSValue columnRoleGetter = tableModelColumn->getterAtRole(roleNameKey);
+ if (columnRoleGetter.isUndefined()) {
+ // This role is not defined, which is fine; just skip it.
+ return roleData;
+ }
+
+ if (columnRoleGetter.isString()) {
+ // The role is set as a string, so we assume the row is a simple object.
+ if (firstRow.type() != QVariant::Map) {
+ qmlWarning(this).quote() << "expected row for role "
+ << roleNameKey << " of TableModelColumn at index "
+ << columnIndex << " to be a simple object, but it's "
+ << firstRow.typeName() << " instead: " << firstRow;
+ return roleData;
+ }
+ const QVariantMap firstRowAsMap = firstRow.toMap();
+ const QString rolePropertyName = columnRoleGetter.toString();
+ const QVariant roleProperty = firstRowAsMap.value(rolePropertyName);
+
+ roleData.isStringRole = true;
+ roleData.name = rolePropertyName;
+ roleData.type = roleProperty.type();
+ roleData.typeName = QString::fromLatin1(roleProperty.typeName());
+ } else if (columnRoleGetter.isCallable()) {
+ // The role is provided via a function, which means the row is complex and
+ // the user needs to provide the data for it.
+ const auto modelIndex = index(0, columnIndex);
+ const auto args = QJSValueList() << qmlEngine(this)->toScriptValue(modelIndex);
+ const QVariant cellData = columnRoleGetter.call(args).toVariant();
+
+ // We don't know the property name since it's provided through the function.
+ // roleData.name = ???
+ roleData.isStringRole = false;
+ roleData.type = cellData.type();
+ roleData.typeName = QString::fromLatin1(cellData.typeName());
+ } else {
+ // Invalid role.
+ qmlWarning(this) << "TableModelColumn role for column at index "
+ << columnIndex << " must be either a string or a function; actual type is: "
+ << columnRoleGetter.toString();
+ }
+
+ return roleData;
+}
+
+void QQmlTableModel::fetchColumnMetadata()
+{
+ qCDebug(lcTableModel) << "gathering metadata for" << mColumnCount << "columns from first row:";
+
+ static const auto supportedRoleNames = QQmlTableModelColumn::supportedRoleNames();
+
+ // Since we support different data structures at the row level, we require that there
+ // is a TableModelColumn for each column.
+ // Collect and cache metadata for each column. This makes data lookup faster.
+ for (int columnIndex = 0; columnIndex < mColumns.size(); ++columnIndex) {
+ QQmlTableModelColumn *column = mColumns.at(columnIndex);
+ qCDebug(lcTableModel).nospace() << "- column " << columnIndex << ":";
+
+ ColumnMetadata metaData;
+ const auto builtInRoleKeys = supportedRoleNames.keys();
+ for (const int builtInRoleKey : builtInRoleKeys) {
+ const QString builtInRoleName = supportedRoleNames.value(builtInRoleKey);
+ ColumnRoleMetadata roleData = fetchColumnRoleData(builtInRoleName, column, columnIndex);
+ if (roleData.type == QVariant::Invalid) {
+ // This built-in role was not specified in this column.
+ continue;
+ }
+
+ qCDebug(lcTableModel).nospace() << " - added metadata for built-in role "
+ << builtInRoleName << " at column index " << columnIndex
+ << ": name=" << roleData.name << " typeName=" << roleData.typeName
+ << " type=" << roleData.type;
+
+ // This column now supports this specific built-in role.
+ metaData.roles.insert(builtInRoleName, roleData);
+ // Add it if it doesn't already exist.
+ mRoleNames[builtInRoleKey] = builtInRoleName.toLatin1();
+ }
+ mColumnMetadata.insert(columnIndex, metaData);
+ }
+}
+
+/*!
+ \qmlmethod TableModel::appendRow(object row)
+
+ Adds a new row to the end of the model, with the
+ values (cells) in \a row.
+
+ \code
+ model.appendRow({
+ checkable: true,
+ amount: 1,
+ fruitType: "Pear",
+ fruitName: "Williams",
+ fruitPrice: 1.50,
+ })
+ \endcode
+
+ \sa insertRow(), setRow(), removeRow()
+*/
+void QQmlTableModel::appendRow(const QVariant &row)
+{
+ if (!validateNewRow("appendRow()", row, -1, AppendOperation))
+ return;
+
+ doInsert(mRowCount, row);
+}
+
+/*!
+ \qmlmethod TableModel::clear()
+
+ Removes all rows from the model.
+
+ \sa removeRow()
+*/
+void QQmlTableModel::clear()
+{
+ QQmlEngine *engine = qmlEngine(this);
+ Q_ASSERT(engine);
+ setRows(QVariant::fromValue(engine->newArray()));
+}
+
+/*!
+ \qmlmethod object TableModel::getRow(int rowIndex)
+
+ Returns the row at \a rowIndex in the model.
+
+ Note that this equivalent to accessing the row directly
+ through the \l rows property:
+
+ \code
+ Component.onCompleted: {
+ // These two lines are equivalent.
+ console.log(model.getRow(0).display);
+ console.log(model.rows[0].fruitName);
+ }
+ \endcode
+
+ \note the returned object cannot be used to modify the contents of the
+ model; use setRow() instead.
+
+ \sa setRow(), appendRow(), insertRow(), removeRow(), moveRow()
+*/
+QVariant QQmlTableModel::getRow(int rowIndex)
+{
+ if (!validateRowIndex("getRow()", "rowIndex", rowIndex))
+ return QVariant();
+
+ return mRows.at(rowIndex);
+}
+
+/*!
+ \qmlmethod TableModel::insertRow(int rowIndex, object row)
+
+ Adds a new row to the list model at position \a rowIndex, with the
+ values (cells) in \a row.
+
+ \code
+ model.insertRow(2, {
+ checkable: true, checked: false,
+ amount: 1,
+ fruitType: "Pear",
+ fruitName: "Williams",
+ fruitPrice: 1.50,
+ })
+ \endcode
+
+ The \a rowIndex must be to an existing item in the list, or one past
+ the end of the list (equivalent to \l appendRow()).
+
+ \sa appendRow(), setRow(), removeRow(), rowCount
+*/
+void QQmlTableModel::insertRow(int rowIndex, const QVariant &row)
+{
+ if (!validateNewRow("insertRow()", row, rowIndex))
+ return;
+
+ doInsert(rowIndex, row);
+}
+
+void QQmlTableModel::doInsert(int rowIndex, const QVariant &row)
+{
+ beginInsertRows(QModelIndex(), rowIndex, rowIndex);
+
+ // Adding rowAsVariant.toList() will add each invidual variant in the list,
+ // which is definitely not what we want.
+ const QVariant rowAsVariant = row.value<QJSValue>().toVariant();
+ mRows.insert(rowIndex, rowAsVariant);
+ ++mRowCount;
+
+ qCDebug(lcTableModel).nospace() << "inserted the following row to the model at index "
+ << rowIndex << ":\n" << rowAsVariant.toMap();
+
+ // Gather metadata the first time a row is added.
+ if (mColumnMetadata.isEmpty())
+ fetchColumnMetadata();
+
+ endInsertRows();
+ emit rowCountChanged();
+}
+
+void QQmlTableModel::classBegin()
+{
+}
+
+void QQmlTableModel::componentComplete()
+{
+ componentCompleted = true;
+
+ mColumnCount = mColumns.size();
+ if (mColumnCount > 0)
+ emit columnCountChanged();
+
+ doSetRows(mRows);
+}
+
+/*!
+ \qmlmethod TableModel::moveRow(int fromRowIndex, int toRowIndex, int rows)
+
+ Moves \a rows from the index at \a fromRowIndex to the index at
+ \a toRowIndex.
+
+ The from and to ranges must exist; for example, to move the first 3 items
+ to the end of the list:
+
+ \code
+ model.moveRow(0, model.rowCount - 3, 3)
+ \endcode
+
+ \sa appendRow(), insertRow(), removeRow(), rowCount
+*/
+void QQmlTableModel::moveRow(int fromRowIndex, int toRowIndex, int rows)
+{
+ if (fromRowIndex == toRowIndex) {
+ qmlWarning(this) << "moveRow(): \"fromRowIndex\" cannot be equal to \"toRowIndex\"";
+ return;
+ }
+
+ if (rows <= 0) {
+ qmlWarning(this) << "moveRow(): \"rows\" is less than or equal to 0";
+ return;
+ }
+
+ if (!validateRowIndex("moveRow()", "fromRowIndex", fromRowIndex))
+ return;
+
+ if (!validateRowIndex("moveRow()", "toRowIndex", toRowIndex))
+ return;
+
+ if (fromRowIndex + rows > mRowCount) {
+ qmlWarning(this) << "moveRow(): \"fromRowIndex\" (" << fromRowIndex
+ << ") + \"rows\" (" << rows << ") = " << (fromRowIndex + rows)
+ << ", which is greater than rowCount() of " << mRowCount;
+ return;
+ }
+
+ if (toRowIndex + rows > mRowCount) {
+ qmlWarning(this) << "moveRow(): \"toRowIndex\" (" << toRowIndex
+ << ") + \"rows\" (" << rows << ") = " << (toRowIndex + rows)
+ << ", which is greater than rowCount() of " << mRowCount;
+ return;
+ }
+
+ qCDebug(lcTableModel).nospace() << "moving " << rows
+ << " row(s) from index " << fromRowIndex
+ << " to index " << toRowIndex;
+
+ // Based on the same call in QQmlListModel::moveRow().
+ beginMoveRows(QModelIndex(), fromRowIndex, fromRowIndex + rows - 1, QModelIndex(),
+ toRowIndex > fromRowIndex ? toRowIndex + rows : toRowIndex);
+
+ // Based on ListModel::moveRow().
+ if (fromRowIndex > toRowIndex) {
+ // Only move forwards - flip if moving backwards.
+ const int from = fromRowIndex;
+ const int to = toRowIndex;
+ fromRowIndex = to;
+ toRowIndex = to + rows;
+ rows = from - to;
+ }
+
+ QVector<QVariant> store;
+ store.reserve(rows);
+ for (int i = 0; i < (toRowIndex - fromRowIndex); ++i)
+ store.append(mRows.at(fromRowIndex + rows + i));
+ for (int i = 0; i < rows; ++i)
+ store.append(mRows.at(fromRowIndex + i));
+ for (int i = 0; i < store.size(); ++i)
+ mRows[fromRowIndex + i] = store[i];
+
+ qCDebug(lcTableModel).nospace() << "after moving, rows are:\n" << mRows;
+
+ endMoveRows();
+}
+
+/*!
+ \qmlmethod TableModel::removeRow(int rowIndex, int rows = 1)
+
+ Removes the row at \a rowIndex from the model.
+
+ \sa clear(), rowCount
+*/
+void QQmlTableModel::removeRow(int rowIndex, int rows)
+{
+ if (!validateRowIndex("removeRow()", "rowIndex", rowIndex))
+ return;
+
+ if (rows <= 0) {
+ qmlWarning(this) << "removeRow(): \"rows\" is less than or equal to zero";
+ return;
+ }
+
+ if (rowIndex + rows - 1 >= mRowCount) {
+ qmlWarning(this) << "removeRow(): \"rows\" " << rows
+ << " exceeds available rowCount() of " << mRowCount
+ << " when removing from \"rowIndex\" " << rowIndex;
+ return;
+ }
+
+ beginRemoveRows(QModelIndex(), rowIndex, rowIndex + rows - 1);
+
+ auto firstIterator = mRows.begin() + rowIndex;
+ // The "last" argument to erase() is exclusive, so we go one past the last item.
+ auto lastIterator = firstIterator + rows;
+ mRows.erase(firstIterator, lastIterator);
+ mRowCount -= rows;
+
+ endRemoveRows();
+ emit rowCountChanged();
+
+ qCDebug(lcTableModel).nospace() << "removed " << rows
+ << " items from the model, starting at index " << rowIndex;
+}
+
+/*!
+ \qmlmethod TableModel::setRow(int rowIndex, object row)
+
+ Changes the row at \a rowIndex in the model with \a row.
+
+ All columns/cells must be present in \c row, and in the correct order.
+
+ \code
+ model.setRow(0, {
+ checkable: true,
+ amount: 1,
+ fruitType: "Pear",
+ fruitName: "Williams",
+ fruitPrice: 1.50,
+ })
+ \endcode
+
+ If \a rowIndex is equal to \c rowCount(), then a new row is appended to the
+ model. Otherwise, \a rowIndex must point to an existing row in the model.
+
+ \sa appendRow(), insertRow(), rowCount
+*/
+void QQmlTableModel::setRow(int rowIndex, const QVariant &row)
+{
+ if (!validateNewRow("setRow()", row, rowIndex))
+ return;
+
+ if (rowIndex != mRowCount) {
+ // Setting an existing row.
+ mRows[rowIndex] = row;
+
+ // For now we just assume the whole row changed, as it's simpler.
+ const QModelIndex topLeftModelIndex(createIndex(rowIndex, 0));
+ const QModelIndex bottomRightModelIndex(createIndex(rowIndex, mColumnCount - 1));
+ emit dataChanged(topLeftModelIndex, bottomRightModelIndex);
+ } else {
+ // Appending a row.
+ doInsert(rowIndex, row);
+ }
+}
+
+QQmlListProperty<QQmlTableModelColumn> QQmlTableModel::columns()
+{
+ return QQmlListProperty<QQmlTableModelColumn>(this, nullptr,
+ &QQmlTableModel::columns_append,
+ &QQmlTableModel::columns_count,
+ &QQmlTableModel::columns_at,
+ &QQmlTableModel::columns_clear);
+}
+
+void QQmlTableModel::columns_append(QQmlListProperty<QQmlTableModelColumn> *property,
+ QQmlTableModelColumn *value)
+{
+ QQmlTableModel *model = static_cast<QQmlTableModel*>(property->object);
+ QQmlTableModelColumn *column = qobject_cast<QQmlTableModelColumn*>(value);
+ if (column)
+ model->mColumns.append(column);
+}
+
+int QQmlTableModel::columns_count(QQmlListProperty<QQmlTableModelColumn> *property)
+{
+ const QQmlTableModel *model = static_cast<QQmlTableModel*>(property->object);
+ return model->mColumns.count();
+}
+
+QQmlTableModelColumn *QQmlTableModel::columns_at(QQmlListProperty<QQmlTableModelColumn> *property, int index)
+{
+ const QQmlTableModel *model = static_cast<QQmlTableModel*>(property->object);
+ return model->mColumns.at(index);
+}
+
+void QQmlTableModel::columns_clear(QQmlListProperty<QQmlTableModelColumn> *property)
+{
+ QQmlTableModel *model = static_cast<QQmlTableModel*>(property->object);
+ return model->mColumns.clear();
+}
+
+/*!
+ \qmlmethod QModelIndex TableModel::index(int row, int column)
+
+ Returns a \l QModelIndex object referencing the given \a row and \a column,
+ which can be passed to the data() function to get the data from that cell,
+ or to setData() to edit the contents of that cell.
+
+ \code
+ import QtQml 2.14
+ import Qt.labs.qmlmodels 1.0
+
+ TableModel {
+ id: model
+
+ TableModelColumn { display: "fruitType" }
+ TableModelColumn { display: "fruitPrice" }
+
+ rows: [
+ { fruitType: "Apple", fruitPrice: 1.50 },
+ { fruitType: "Orange", fruitPrice: 2.50 }
+ ]
+
+ Component.onCompleted: {
+ for (var r = 0; r < model.rowCount; ++r) {
+ console.log("An " + model.data(model.index(r, 0)).display +
+ " costs " + model.data(model.index(r, 1)).display.toFixed(2))
+ }
+ }
+ }
+ \endcode
+
+ \sa {QModelIndex and related Classes in QML}, data()
+*/
+// Note: we don't document the parent argument, because you never need it, because
+// cells in a TableModel don't have parents. But it is there because this function is an override.
+QModelIndex QQmlTableModel::index(int row, int column, const QModelIndex &parent) const
+{
+ return row >= 0 && row < rowCount() && column >= 0 && column < columnCount() && !parent.isValid()
+ ? createIndex(row, column)
+ : QModelIndex();
+}
+
+/*!
+ \qmlproperty int TableModel::rowCount
+ \readonly
+
+ This read-only property holds the number of rows in the model.
+
+ This value changes whenever rows are added or removed from the model.
+*/
+int QQmlTableModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ return mRowCount;
+}
+
+/*!
+ \qmlproperty int TableModel::columnCount
+ \readonly
+
+ This read-only property holds the number of columns in the model.
+
+ The number of columns is fixed for the lifetime of the model
+ after the \l rows property is set or \l appendRow() is called for the first
+ time.
+*/
+int QQmlTableModel::columnCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ return mColumnCount;
+}
+
+/*!
+ \qmlmethod variant TableModel::data(QModelIndex index, string role)
+
+ Returns the data from the table cell at the given \a index belonging to the
+ given \a role.
+
+ \sa index()
+*/
+QVariant QQmlTableModel::data(const QModelIndex &index, const QString &role) const
+{
+ const int iRole = mRoleNames.key(role.toUtf8(), -1);
+ if (iRole >= 0)
+ return data(index, iRole);
+ return QVariant();
+}
+
+QVariant QQmlTableModel::data(const QModelIndex &index, int role) const
+{
+ const int row = index.row();
+ if (row < 0 || row >= rowCount())
+ return QVariant();
+
+ const int column = index.column();
+ if (column < 0 || column >= columnCount())
+ return QVariant();
+
+ const ColumnMetadata columnMetadata = mColumnMetadata.at(index.column());
+ const QString roleName = QString::fromUtf8(mRoleNames.value(role));
+ if (!columnMetadata.roles.contains(roleName)) {
+ qmlWarning(this) << "setData(): no role named " << roleName
+ << " at column index " << column << ". The available roles for that column are: "
+ << columnMetadata.roles.keys();
+ return QVariant();
+ }
+
+ const ColumnRoleMetadata roleData = columnMetadata.roles.value(roleName);
+ if (roleData.isStringRole) {
+ // We know the data structure, so we can get the data for the user.
+ const QVariantMap rowData = mRows.at(row).toMap();
+ const QString propertyName = columnMetadata.roles.value(roleName).name;
+ const QVariant value = rowData.value(propertyName);
+ return value;
+ }
+
+ // We don't know the data structure, so the user has to modify their data themselves.
+ // First, find the getter for this column and role.
+ QJSValue getter = mColumns.at(column)->getterAtRole(roleName);
+
+ // Then, call it and return what it returned.
+ const auto args = QJSValueList() << qmlEngine(this)->toScriptValue(index);
+ return getter.call(args).toVariant();
+}
+
+/*!
+ \qmlmethod bool TableModel::setData(QModelIndex index, string role, variant value)
+
+ Inserts or updates the data field named by \a role in the table cell at the
+ given \a index with \a value. Returns true if sucessful, false if not.
+
+ \sa index()
+*/
+bool QQmlTableModel::setData(const QModelIndex &index, const QString &role, const QVariant &value)
+{
+ const int intRole = mRoleNames.key(role.toUtf8(), -1);
+ if (intRole >= 0)
+ return setData(index, value, intRole);
+ return false;
+}
+
+bool QQmlTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ const int row = index.row();
+ if (row < 0 || row >= rowCount())
+ return false;
+
+ const int column = index.column();
+ if (column < 0 || column >= columnCount())
+ return false;
+
+ const QString roleName = QString::fromUtf8(mRoleNames.value(role));
+
+ qCDebug(lcTableModel).nospace() << "setData() called with index "
+ << index << ", value " << value << " and role " << roleName;
+
+ // Verify that the role exists for this column.
+ const ColumnMetadata columnMetadata = mColumnMetadata.at(index.column());
+ if (!columnMetadata.roles.contains(roleName)) {
+ qmlWarning(this) << "setData(): no role named \"" << roleName
+ << "\" at column index " << column << ". The available roles for that column are: "
+ << columnMetadata.roles.keys();
+ return false;
+ }
+
+ // Verify that the type of the value is what we expect.
+ // If the value set is not of the expected type, we can try to convert it automatically.
+ const ColumnRoleMetadata roleData = columnMetadata.roles.value(roleName);
+ QVariant effectiveValue = value;
+ if (value.type() != roleData.type) {
+ if (!value.canConvert(int(roleData.type))) {
+ qmlWarning(this).nospace() << "setData(): the value " << value
+ << " set at row " << row << " column " << column << " with role " << roleName
+ << " cannot be converted to " << roleData.typeName;
+ return false;
+ }
+
+ if (!effectiveValue.convert(int(roleData.type))) {
+ qmlWarning(this).nospace() << "setData(): failed converting value " << value
+ << " set at row " << row << " column " << column << " with role " << roleName
+ << " to " << roleData.typeName;
+ return false;
+ }
+ }
+
+ if (roleData.isStringRole) {
+ // We know the data structure, so we can set it for the user.
+ QVariantMap modifiedRow = mRows.at(row).toMap();
+ modifiedRow[roleData.name] = value;
+
+ mRows[row] = modifiedRow;
+ } else {
+ // We don't know the data structure, so the user has to modify their data themselves.
+ auto engine = qmlEngine(this);
+ auto args = QJSValueList()
+ // arg 0: modelIndex.
+ << engine->toScriptValue(index)
+ // arg 1: cellData.
+ << engine->toScriptValue(value);
+ // Do the actual setting.
+ QJSValue setter = mColumns.at(column)->setterAtRole(roleName);
+ setter.call(args);
+
+ /*
+ The chain of events so far:
+
+ - User did e.g.: model.edit = textInput.text
+ - setData() is called
+ - setData() calls the setter
+ (remember that we need to emit the dataChanged() signal,
+ which is why the user can't just set the data directly in the delegate)
+
+ Now the user's setter function has modified *their* copy of the
+ data, but *our* copy of the data is old. Imagine the getters and setters looked like this:
+
+ display: function(modelIndex) { return rows[modelIndex.row][1].amount }
+ setDisplay: function(modelIndex, cellData) { rows[modelIndex.row][1].amount = cellData }
+
+ We don't know the structure of the user's data, so we can't just do
+ what we do above for the isStringRole case:
+
+ modifiedRow[column][roleName] = value
+
+ This means that, besides getting the implicit row count when rows is initially set,
+ our copy of the data is unused when it comes to complex columns.
+
+ Another point to note is that we can't pass rowData in to the getter as a convenience,
+ because we would be passing in *our* copy of the row, which is not up-to-date.
+ Since the user already has access to the data, it's not a big deal for them to do:
+
+ display: function(modelIndex) { return rows[modelIndex.row][1].amount }
+
+ instead of:
+
+ display: function(modelIndex, rowData) { return rowData[1].amount }
+ */
+ }
+
+ QVector<int> rolesChanged;
+ rolesChanged.append(role);
+ emit dataChanged(index, index, rolesChanged);
+
+ return true;
+}
+
+QHash<int, QByteArray> QQmlTableModel::roleNames() const
+{
+ return mRoleNames;
+}
+
+QQmlTableModel::ColumnRoleMetadata::ColumnRoleMetadata()
+{
+}
+
+QQmlTableModel::ColumnRoleMetadata::ColumnRoleMetadata(
+ bool isStringRole, const QString &name, QVariant::Type type, const QString &typeName) :
+ isStringRole(isStringRole),
+ name(name),
+ type(type),
+ typeName(typeName)
+{
+}
+
+bool QQmlTableModel::ColumnRoleMetadata::isValid() const
+{
+ return !name.isEmpty();
+}
+
+bool QQmlTableModel::validateRowType(const char *functionName, const QVariant &row) const
+{
+ if (!row.canConvert<QJSValue>()) {
+ qmlWarning(this) << functionName << ": expected \"row\" argument to be a QJSValue,"
+ << " but got " << row.typeName() << " instead:\n" << row;
+ return false;
+ }
+
+ const QJSValue rowAsJSValue = row.value<QJSValue>();
+ if (!rowAsJSValue.isObject() && !rowAsJSValue.isArray()) {
+ qmlWarning(this) << functionName << ": expected \"row\" argument "
+ << "to be an object or array, but got:\n" << rowAsJSValue.toString();
+ return false;
+ }
+
+ return true;
+}
+
+bool QQmlTableModel::validateNewRow(const char *functionName, const QVariant &row,
+ int rowIndex, NewRowOperationFlag operation) const
+{
+ if (mColumnMetadata.isEmpty()) {
+ // There is no column metadata, so we have nothing to validate the row against.
+ // Rows have to be added before we can gather metadata from them, so just this
+ // once we'll return true to allow the rows to be added.
+ return true;
+ }
+
+ // Don't require each row to be a QJSValue when setting all rows,
+ // as they won't be; they'll be QVariantMap.
+ if (operation != SetRowsOperation && !validateRowType(functionName, row))
+ return false;
+
+ if (operation == OtherOperation) {
+ // Inserting/setting.
+ if (rowIndex < 0) {
+ qmlWarning(this) << functionName << ": \"rowIndex\" cannot be negative";
+ return false;
+ }
+
+ if (rowIndex > mRowCount) {
+ qmlWarning(this) << functionName << ": \"rowIndex\" " << rowIndex
+ << " is greater than rowCount() of " << mRowCount;
+ return false;
+ }
+ }
+
+ const QVariant rowAsVariant = operation == SetRowsOperation
+ ? row : row.value<QJSValue>().toVariant();
+ if (rowAsVariant.type() != QVariant::Map) {
+ qmlWarning(this) << functionName << ": row manipulation functions "
+ << "do not support complex rows (row index: " << rowIndex << ")";
+ return false;
+ }
+
+ const QVariantMap rowAsMap = rowAsVariant.toMap();
+ const int columnCount = rowAsMap.size();
+ if (columnCount < mColumnCount) {
+ qmlWarning(this) << functionName << ": expected " << mColumnCount
+ << " columns, but only got " << columnCount;
+ return false;
+ }
+
+ // We can't validate complex structures, but we can make sure that
+ // each simple string-based role in each column is correct.
+ for (int columnIndex = 0; columnIndex < mColumns.size(); ++columnIndex) {
+ QQmlTableModelColumn *column = mColumns.at(columnIndex);
+ const QHash<QString, QJSValue> getters = column->getters();
+ const auto roleNames = getters.keys();
+ const ColumnMetadata columnMetadata = mColumnMetadata.at(columnIndex);
+ for (const QString &roleName : roleNames) {
+ const ColumnRoleMetadata roleData = columnMetadata.roles.value(roleName);
+ if (!roleData.isStringRole)
+ continue;
+
+ if (!rowAsMap.contains(roleData.name)) {
+ qmlWarning(this).quote() << functionName << ": expected a property named "
+ << roleData.name << " in row at index " << rowIndex << ", but couldn't find one";
+ return false;
+ }
+
+ const QVariant rolePropertyValue = rowAsMap.value(roleData.name);
+ if (rolePropertyValue.type() != roleData.type) {
+ qmlWarning(this).quote() << functionName << ": expected the property named "
+ << roleData.name << " to be of type " << roleData.typeName
+ << ", but got " << QString::fromLatin1(rolePropertyValue.typeName()) << " instead";
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool QQmlTableModel::validateRowIndex(const char *functionName, const char *argumentName, int rowIndex) const
+{
+ if (rowIndex < 0) {
+ qmlWarning(this) << functionName << ": \"" << argumentName << "\" cannot be negative";
+ return false;
+ }
+
+ if (rowIndex >= mRowCount) {
+ qmlWarning(this) << functionName << ": \"" << argumentName
+ << "\" " << rowIndex << " is greater than or equal to rowCount() of " << mRowCount;
+ return false;
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlmodels/qqmltablemodel_p.h b/src/qmlmodels/qqmltablemodel_p.h
new file mode 100644
index 0000000000..b3c0cc2848
--- /dev/null
+++ b/src/qmlmodels/qqmltablemodel_p.h
@@ -0,0 +1,172 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLTABLEMODEL_P_H
+#define QQMLTABLEMODEL_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>
+#include <QtCore/QAbstractTableModel>
+#include <QtQml/qqml.h>
+#include <QtQmlModels/private/qtqmlmodelsglobal_p.h>
+#include <QtQmlModels/private/qqmltablemodelcolumn_p.h>
+#include <QtQml/QJSValue>
+#include <QtQml/QQmlListProperty>
+
+QT_REQUIRE_CONFIG(qml_table_model);
+
+QT_BEGIN_NAMESPACE
+
+class Q_QMLMODELS_PRIVATE_EXPORT QQmlTableModel : public QAbstractTableModel, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_PROPERTY(int columnCount READ columnCount NOTIFY columnCountChanged FINAL)
+ Q_PROPERTY(int rowCount READ rowCount NOTIFY rowCountChanged FINAL)
+ Q_PROPERTY(QVariant rows READ rows WRITE setRows NOTIFY rowsChanged FINAL)
+ Q_PROPERTY(QQmlListProperty<QQmlTableModelColumn> columns READ columns CONSTANT FINAL)
+ Q_INTERFACES(QQmlParserStatus)
+ Q_CLASSINFO("DefaultProperty", "columns")
+
+public:
+ QQmlTableModel(QObject *parent = nullptr);
+ ~QQmlTableModel() override;
+
+ QVariant rows() const;
+ void setRows(const QVariant &rows);
+
+ Q_INVOKABLE void appendRow(const QVariant &row);
+ Q_INVOKABLE void clear();
+ Q_INVOKABLE QVariant getRow(int rowIndex);
+ Q_INVOKABLE void insertRow(int rowIndex, const QVariant &row);
+ Q_INVOKABLE void moveRow(int fromRowIndex, int toRowIndex, int rows = 1);
+ Q_INVOKABLE void removeRow(int rowIndex, int rows = 1);
+ Q_INVOKABLE void setRow(int rowIndex, const QVariant &row);
+
+ QQmlListProperty<QQmlTableModelColumn> columns();
+
+ static void columns_append(QQmlListProperty<QQmlTableModelColumn> *property, QQmlTableModelColumn *value);
+ static int columns_count(QQmlListProperty<QQmlTableModelColumn> *property);
+ static QQmlTableModelColumn *columns_at(QQmlListProperty<QQmlTableModelColumn> *property, int index);
+ static void columns_clear(QQmlListProperty<QQmlTableModelColumn> *property);
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+ Q_INVOKABLE QVariant data(const QModelIndex &index, const QString &role) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ Q_INVOKABLE bool setData(const QModelIndex &index, const QString &role, const QVariant &value);
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole) override;
+ QHash<int, QByteArray> roleNames() const override;
+
+Q_SIGNALS:
+ void columnCountChanged();
+ void rowCountChanged();
+ void rowsChanged();
+
+private:
+ class ColumnRoleMetadata
+ {
+ public:
+ ColumnRoleMetadata();
+ ColumnRoleMetadata(bool isStringRole, const QString &name, QVariant::Type type, const QString &typeName);
+
+ bool isValid() const;
+
+ // If this is false, it's a function role.
+ bool isStringRole = false;
+ QString name;
+ QVariant::Type type = QVariant::Invalid;
+ QString typeName;
+ };
+
+ struct ColumnMetadata
+ {
+ // Key = role name that will be made visible to the delegate
+ // Value = metadata about that role, including actual name in the model data, type, etc.
+ QHash<QString, ColumnRoleMetadata> roles;
+ };
+
+ enum NewRowOperationFlag {
+ OtherOperation, // insert(), set(), etc.
+ SetRowsOperation,
+ AppendOperation
+ };
+
+ void doSetRows(const QVariantList &rowsAsVariantList);
+ ColumnRoleMetadata fetchColumnRoleData(const QString &roleNameKey,
+ QQmlTableModelColumn *tableModelColumn, int columnIndex) const;
+ void fetchColumnMetadata();
+
+ bool validateRowType(const char *functionName, const QVariant &row) const;
+ bool validateNewRow(const char *functionName, const QVariant &row,
+ int rowIndex, NewRowOperationFlag operation = OtherOperation) const;
+ bool validateRowIndex(const char *functionName, const char *argumentName, int rowIndex) const;
+
+ void doInsert(int rowIndex, const QVariant &row);
+
+ void classBegin() override;
+ void componentComplete() override;
+
+ bool componentCompleted = false;
+ QVariantList mRows;
+ QList<QQmlTableModelColumn *> mColumns;
+ int mRowCount = 0;
+ int mColumnCount = 0;
+ // Each entry contains information about the properties of the column at that index.
+ QVector<ColumnMetadata> mColumnMetadata;
+ // key = property index (0 to number of properties across all columns)
+ // value = role name
+ QHash<int, QByteArray> mRoleNames;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQmlTableModel)
+
+#endif // QQMLTABLEMODEL_P_H
diff --git a/src/qmlmodels/qqmltablemodelcolumn.cpp b/src/qmlmodels/qqmltablemodelcolumn.cpp
new file mode 100644
index 0000000000..93da0642de
--- /dev/null
+++ b/src/qmlmodels/qqmltablemodelcolumn.cpp
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmltablemodelcolumn_p.h"
+
+#include <QtQml/qqmlinfo.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype TableModelColumn
+ \instantiates QQmlTableModelColumn
+ \inqmlmodule Qt.labs.qmlmodels
+ \brief Represents a column in a model.
+ \since 5.14
+
+ \section1 Supported Roles
+
+ TableModelColumn supports all of \l {Qt::ItemDataRole}{Qt's roles},
+ with the exception of \c Qt::InitialSortOrderRole.
+
+ \sa TableModel, TableView
+*/
+
+static const QString displayRoleName = QStringLiteral("display");
+static const QString decorationRoleName = QStringLiteral("decoration");
+static const QString editRoleName = QStringLiteral("edit");
+static const QString toolTipRoleName = QStringLiteral("toolTip");
+static const QString statusTipRoleName = QStringLiteral("statusTip");
+static const QString whatsThisRoleName = QStringLiteral("whatsThis");
+
+static const QString fontRoleName = QStringLiteral("font");
+static const QString textAlignmentRoleName = QStringLiteral("textAlignment");
+static const QString backgroundRoleName = QStringLiteral("background");
+static const QString foregroundRoleName = QStringLiteral("foreground");
+static const QString checkStateRoleName = QStringLiteral("checkState");
+
+static const QString accessibleTextRoleName = QStringLiteral("accessibleText");
+static const QString accessibleDescriptionRoleName = QStringLiteral("accessibleDescription");
+
+static const QString sizeHintRoleName = QStringLiteral("sizeHint");
+
+
+QQmlTableModelColumn::QQmlTableModelColumn(QObject *parent)
+ : QObject(parent)
+{
+}
+
+QQmlTableModelColumn::~QQmlTableModelColumn()
+{
+}
+
+#define DEFINE_ROLE_PROPERTIES(getterGetterName, getterSetterName, getterSignal, setterGetterName, setterSetterName, setterSignal, roleName) \
+QJSValue QQmlTableModelColumn::getterGetterName() const \
+{ \
+ return mGetters.value(roleName); \
+} \
+\
+void QQmlTableModelColumn::getterSetterName(const QJSValue &stringOrFunction) \
+{ \
+ if (!stringOrFunction.isString() && !stringOrFunction.isCallable()) { \
+ qmlWarning(this).quote() << "getter for " << roleName << " must be a function"; \
+ return; \
+ } \
+ if (stringOrFunction.strictlyEquals(decoration())) \
+ return; \
+\
+ mGetters[roleName] = stringOrFunction; \
+ emit decorationChanged(); \
+} \
+\
+QJSValue QQmlTableModelColumn::setterGetterName() const \
+{ \
+ return mSetters.value(roleName); \
+} \
+\
+void QQmlTableModelColumn::setterSetterName(const QJSValue &function) \
+{ \
+ if (!function.isCallable()) { \
+ qmlWarning(this).quote() << "setter for " << roleName << " must be a function"; \
+ return; \
+ } \
+\
+ if (function.strictlyEquals(getSetDisplay())) \
+ return; \
+\
+ mSetters[roleName] = function; \
+ emit setDisplayChanged(); \
+}
+
+DEFINE_ROLE_PROPERTIES(display, setDisplay, displayChanged,
+ getSetDisplay, setSetDisplay, setDisplayChanged, displayRoleName)
+DEFINE_ROLE_PROPERTIES(decoration, setDecoration, decorationChanged,
+ getSetDecoration, setSetDecoration, setDecorationChanged, decorationRoleName)
+DEFINE_ROLE_PROPERTIES(edit, setEdit, editChanged,
+ getSetEdit, setSetEdit, setEditChanged, editRoleName)
+DEFINE_ROLE_PROPERTIES(toolTip, setToolTip, toolTipChanged,
+ getSetToolTip, setSetToolTip, setToolTipChanged, toolTipRoleName)
+DEFINE_ROLE_PROPERTIES(statusTip, setStatusTip, statusTipChanged,
+ getSetStatusTip, setSetStatusTip, setStatusTipChanged, statusTipRoleName)
+DEFINE_ROLE_PROPERTIES(whatsThis, setWhatsThis, whatsThisChanged,
+ getSetWhatsThis, setSetWhatsThis, setWhatsThisChanged, whatsThisRoleName)
+
+DEFINE_ROLE_PROPERTIES(font, setFont, fontChanged,
+ getSetFont, setSetFont, setFontChanged, fontRoleName)
+DEFINE_ROLE_PROPERTIES(textAlignment, setTextAlignment, textAlignmentChanged,
+ getSetTextAlignment, setSetTextAlignment, setTextAlignmentChanged, textAlignmentRoleName)
+DEFINE_ROLE_PROPERTIES(background, setBackground, backgroundChanged,
+ getSetBackground, setSetBackground, setBackgroundChanged, backgroundRoleName)
+DEFINE_ROLE_PROPERTIES(foreground, setForeground, foregroundChanged,
+ getSetForeground, setSetForeground, setForegroundChanged, foregroundRoleName)
+DEFINE_ROLE_PROPERTIES(checkState, setCheckState, checkStateChanged,
+ getSetCheckState, setSetCheckState, setCheckStateChanged, checkStateRoleName)
+
+DEFINE_ROLE_PROPERTIES(accessibleText, setAccessibleText, accessibleTextChanged,
+ getSetAccessibleText, setSetAccessibleText, setAccessibleTextChanged, accessibleTextRoleName)
+DEFINE_ROLE_PROPERTIES(accessibleDescription, setAccessibleDescription, accessibleDescriptionChanged,
+ getSetAccessibleDescription, setSetAccessibleDescription, setAccessibleDescriptionChanged, accessibleDescriptionRoleName)
+
+DEFINE_ROLE_PROPERTIES(sizeHint, setSizeHint, sizeHintChanged,
+ getSetSizeHint, setSetSizeHint, setSizeHintChanged, sizeHintRoleName)
+
+QJSValue QQmlTableModelColumn::getterAtRole(const QString &roleName)
+{
+ auto it = mGetters.find(roleName);
+ if (it == mGetters.end())
+ return QJSValue();
+ return *it;
+}
+
+QJSValue QQmlTableModelColumn::setterAtRole(const QString &roleName)
+{
+ auto it = mSetters.find(roleName);
+ if (it == mSetters.end())
+ return QJSValue();
+ return *it;
+}
+
+const QHash<QString, QJSValue> QQmlTableModelColumn::getters() const
+{
+ return mGetters;
+}
+
+const QHash<int, QString> QQmlTableModelColumn::supportedRoleNames()
+{
+ QHash<int, QString> names;
+ names[Qt::DisplayRole] = QLatin1String("display");
+ names[Qt::DecorationRole] = QLatin1String("decoration");
+ names[Qt::EditRole] = QLatin1String("edit");
+ names[Qt::ToolTipRole] = QLatin1String("toolTip");
+ names[Qt::StatusTipRole] = QLatin1String("statusTip");
+ names[Qt::WhatsThisRole] = QLatin1String("whatsThis");
+ names[Qt::FontRole] = QLatin1String("font");
+ names[Qt::TextAlignmentRole] = QLatin1String("textAlignment");
+ names[Qt::BackgroundRole] = QLatin1String("background");
+ names[Qt::ForegroundRole] = QLatin1String("foreground");
+ names[Qt::CheckStateRole] = QLatin1String("checkState");
+ names[Qt::AccessibleTextRole] = QLatin1String("accessibleText");
+ names[Qt::AccessibleDescriptionRole] = QLatin1String("accessibleDescription");
+ names[Qt::SizeHintRole] = QLatin1String("sizeHint");
+ return names;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlmodels/qqmltablemodelcolumn_p.h b/src/qmlmodels/qqmltablemodelcolumn_p.h
new file mode 100644
index 0000000000..0b6478ce38
--- /dev/null
+++ b/src/qmlmodels/qqmltablemodelcolumn_p.h
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLTABLEMODELCOLUMN_P_H
+#define QQMLTABLEMODELCOLUMN_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>
+#include <QtQml/qqml.h>
+#include <QtQmlModels/private/qtqmlmodelsglobal_p.h>
+#include <QtQml/qjsvalue.h>
+
+QT_REQUIRE_CONFIG(qml_table_model);
+
+QT_BEGIN_NAMESPACE
+
+class Q_QMLMODELS_PRIVATE_EXPORT QQmlTableModelColumn : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QJSValue display READ display WRITE setDisplay NOTIFY displayChanged FINAL)
+ Q_PROPERTY(QJSValue setDisplay READ getSetDisplay WRITE setSetDisplay NOTIFY setDisplayChanged)
+ Q_PROPERTY(QJSValue decoration READ decoration WRITE setDecoration NOTIFY decorationChanged FINAL)
+ Q_PROPERTY(QJSValue setDecoration READ getSetDecoration WRITE setSetDecoration NOTIFY setDecorationChanged FINAL)
+ Q_PROPERTY(QJSValue edit READ edit WRITE setEdit NOTIFY editChanged FINAL)
+ Q_PROPERTY(QJSValue setEdit READ getSetEdit WRITE setSetEdit NOTIFY setEditChanged FINAL)
+ Q_PROPERTY(QJSValue toolTip READ toolTip WRITE setToolTip NOTIFY toolTipChanged FINAL)
+ Q_PROPERTY(QJSValue setToolTip READ getSetToolTip WRITE setSetToolTip NOTIFY setToolTipChanged FINAL)
+ Q_PROPERTY(QJSValue statusTip READ statusTip WRITE setStatusTip NOTIFY statusTipChanged FINAL)
+ Q_PROPERTY(QJSValue setStatusTip READ getSetStatusTip WRITE setSetStatusTip NOTIFY setStatusTipChanged FINAL)
+ Q_PROPERTY(QJSValue whatsThis READ whatsThis WRITE setWhatsThis NOTIFY whatsThisChanged FINAL)
+ Q_PROPERTY(QJSValue setWhatsThis READ getSetWhatsThis WRITE setSetWhatsThis NOTIFY setWhatsThisChanged FINAL)
+
+ Q_PROPERTY(QJSValue font READ font WRITE setFont NOTIFY fontChanged FINAL)
+ Q_PROPERTY(QJSValue setFont READ getSetFont WRITE setSetFont NOTIFY setFontChanged FINAL)
+ Q_PROPERTY(QJSValue textAlignment READ textAlignment WRITE setTextAlignment NOTIFY textAlignmentChanged FINAL)
+ Q_PROPERTY(QJSValue setTextAlignment READ getSetTextAlignment WRITE setSetTextAlignment NOTIFY setTextAlignmentChanged FINAL)
+ Q_PROPERTY(QJSValue background READ background WRITE setBackground NOTIFY backgroundChanged FINAL)
+ Q_PROPERTY(QJSValue setBackground READ getSetBackground WRITE setSetBackground NOTIFY setBackgroundChanged FINAL)
+ Q_PROPERTY(QJSValue foreground READ foreground WRITE setForeground NOTIFY foregroundChanged FINAL)
+ Q_PROPERTY(QJSValue setForeground READ getSetForeground WRITE setSetForeground NOTIFY setForegroundChanged FINAL)
+ Q_PROPERTY(QJSValue checkState READ checkState WRITE setCheckState NOTIFY checkStateChanged FINAL)
+ Q_PROPERTY(QJSValue setCheckState READ getSetCheckState WRITE setSetCheckState NOTIFY setCheckStateChanged FINAL)
+
+ Q_PROPERTY(QJSValue accessibleText READ accessibleText WRITE setAccessibleText NOTIFY accessibleTextChanged FINAL)
+ Q_PROPERTY(QJSValue setAccessibleText READ getSetAccessibleText WRITE setSetAccessibleText NOTIFY setAccessibleTextChanged FINAL)
+ Q_PROPERTY(QJSValue accessibleDescription READ accessibleDescription
+ WRITE setAccessibleDescription NOTIFY accessibleDescriptionChanged FINAL)
+ Q_PROPERTY(QJSValue setAccessibleDescription READ getSetAccessibleDescription
+ WRITE setSetAccessibleDescription NOTIFY setAccessibleDescriptionChanged FINAL)
+
+ Q_PROPERTY(QJSValue sizeHint READ sizeHint WRITE setSizeHint NOTIFY sizeHintChanged FINAL)
+ Q_PROPERTY(QJSValue setSizeHint READ getSetSizeHint WRITE setSetSizeHint NOTIFY setSizeHintChanged FINAL)
+
+public:
+ QQmlTableModelColumn(QObject *parent = nullptr);
+ ~QQmlTableModelColumn() override;
+
+ QJSValue display() const;
+ void setDisplay(const QJSValue &stringOrFunction);
+ QJSValue getSetDisplay() const;
+ void setSetDisplay(const QJSValue &function);
+
+ QJSValue decoration() const;
+ void setDecoration(const QJSValue &stringOrFunction);
+ QJSValue getSetDecoration() const;
+ void setSetDecoration(const QJSValue &function);
+
+ QJSValue edit() const;
+ void setEdit(const QJSValue &stringOrFunction);
+ QJSValue getSetEdit() const;
+ void setSetEdit(const QJSValue &function);
+
+ QJSValue toolTip() const;
+ void setToolTip(const QJSValue &stringOrFunction);
+ QJSValue getSetToolTip() const;
+ void setSetToolTip(const QJSValue &function);
+
+ QJSValue statusTip() const;
+ void setStatusTip(const QJSValue &stringOrFunction);
+ QJSValue getSetStatusTip() const;
+ void setSetStatusTip(const QJSValue &function);
+
+ QJSValue whatsThis() const;
+ void setWhatsThis(const QJSValue &stringOrFunction);
+ QJSValue getSetWhatsThis() const;
+ void setSetWhatsThis(const QJSValue &function);
+
+ QJSValue font() const;
+ void setFont(const QJSValue &stringOrFunction);
+ QJSValue getSetFont() const;
+ void setSetFont(const QJSValue &function);
+
+ QJSValue textAlignment() const;
+ void setTextAlignment(const QJSValue &stringOrFunction);
+ QJSValue getSetTextAlignment() const;
+ void setSetTextAlignment(const QJSValue &function);
+
+ QJSValue background() const;
+ void setBackground(const QJSValue &stringOrFunction);
+ QJSValue getSetBackground() const;
+ void setSetBackground(const QJSValue &function);
+
+ QJSValue foreground() const;
+ void setForeground(const QJSValue &stringOrFunction);
+ QJSValue getSetForeground() const;
+ void setSetForeground(const QJSValue &function);
+
+ QJSValue checkState() const;
+ void setCheckState(const QJSValue &stringOrFunction);
+ QJSValue getSetCheckState() const;
+ void setSetCheckState(const QJSValue &function);
+
+ QJSValue accessibleText() const;
+ void setAccessibleText(const QJSValue &stringOrFunction);
+ QJSValue getSetAccessibleText() const;
+ void setSetAccessibleText(const QJSValue &function);
+
+ QJSValue accessibleDescription() const;
+ void setAccessibleDescription(const QJSValue &stringOrFunction);
+ QJSValue getSetAccessibleDescription() const;
+ void setSetAccessibleDescription(const QJSValue &function);
+
+ QJSValue sizeHint() const;
+ void setSizeHint(const QJSValue &stringOrFunction);
+ QJSValue getSetSizeHint() const;
+ void setSetSizeHint(const QJSValue &function);
+
+ QJSValue getterAtRole(const QString &roleName);
+ QJSValue setterAtRole(const QString &roleName);
+
+ const QHash<QString, QJSValue> getters() const;
+
+ static const QHash<int, QString> supportedRoleNames();
+
+Q_SIGNALS:
+ void indexChanged();
+ void displayChanged();
+ void setDisplayChanged();
+ void decorationChanged();
+ void setDecorationChanged();
+ void editChanged();
+ void setEditChanged();
+ void toolTipChanged();
+ void setToolTipChanged();
+ void statusTipChanged();
+ void setStatusTipChanged();
+ void whatsThisChanged();
+ void setWhatsThisChanged();
+
+ void fontChanged();
+ void setFontChanged();
+ void textAlignmentChanged();
+ void setTextAlignmentChanged();
+ void backgroundChanged();
+ void setBackgroundChanged();
+ void foregroundChanged();
+ void setForegroundChanged();
+ void checkStateChanged();
+ void setCheckStateChanged();
+
+ void accessibleTextChanged();
+ void setAccessibleTextChanged();
+ void accessibleDescriptionChanged();
+ void setAccessibleDescriptionChanged();
+ void sizeHintChanged();
+ void setSizeHintChanged();
+
+private:
+ int mIndex = -1;
+
+ // We store these in hashes because QQuickTableModel needs string-based lookup in certain situations.
+ QHash<QString, QJSValue> mGetters;
+ QHash<QString, QJSValue> mSetters;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQmlTableModelColumn)
+
+#endif // QQMLTABLEMODELCOLUMN_P_H
diff --git a/src/qml/types/qquickpackage.cpp b/src/qmlmodels/qquickpackage.cpp
index 03539d8737..567381e5ab 100644
--- a/src/qml/types/qquickpackage.cpp
+++ b/src/qmlmodels/qquickpackage.cpp
@@ -47,8 +47,8 @@ QT_BEGIN_NAMESPACE
/*!
\qmltype Package
\instantiates QQuickPackage
- \inqmlmodule QtQuick
- \ingroup qtquick-views
+ \inqmlmodule QtQml.Models
+ \ingroup qtquick-models
\brief Specifies a collection of named items.
The Package type is used in conjunction with
@@ -71,6 +71,9 @@ QT_BEGIN_NAMESPACE
\snippet package/view.qml 0
+ \note Package is part of QtQml.Models since version 2.14 and part of QtQuick since version 2.0.
+ Importing Package via QtQuick is deprecated since Qt 5.14.
+
\sa {Qt Quick Examples - Views}, {Qt Quick Demo - Photo Viewer}, {Qt QML}
*/
diff --git a/src/qml/types/qquickpackage_p.h b/src/qmlmodels/qquickpackage_p.h
index 122c7fcb30..97f7818fee 100644
--- a/src/qml/types/qquickpackage_p.h
+++ b/src/qmlmodels/qquickpackage_p.h
@@ -52,6 +52,9 @@
//
#include <qqml.h>
+#include <QtQmlModels/private/qtqmlmodelsglobal_p.h>
+
+QT_REQUIRE_CONFIG(qml_delegate_model);
QT_BEGIN_NAMESPACE
diff --git a/src/qmlmodels/qtqmlmodelsglobal.h b/src/qmlmodels/qtqmlmodelsglobal.h
new file mode 100644
index 0000000000..6e6cf299b2
--- /dev/null
+++ b/src/qmlmodels/qtqmlmodelsglobal.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTQMLMODELSGLOBAL_H
+#define QTQMLMODELSGLOBAL_H
+
+#include <QtQml/qtqmlglobal.h>
+#include <QtQmlModels/qtqmlmodels-config.h>
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_STATIC)
+# if defined(QT_BUILD_QMLMODELS_LIB)
+# define Q_QMLMODELS_EXPORT Q_DECL_EXPORT
+# else
+# define Q_QMLMODELS_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define Q_QMLMODELS_EXPORT
+#endif
+
+QT_END_NAMESPACE
+#endif // QTQMLMODELSGLOBAL_H
diff --git a/src/qmlmodels/qtqmlmodelsglobal_p.h b/src/qmlmodels/qtqmlmodelsglobal_p.h
new file mode 100644
index 0000000000..145112c9c1
--- /dev/null
+++ b/src/qmlmodels/qtqmlmodelsglobal_p.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTQMLMODELSGLOBAL_P_H
+#define QTQMLMODELSGLOBAL_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 <QtQml/private/qtqmlglobal_p.h>
+#include <QtQmlModels/qtqmlmodelsglobal.h>
+#include <QtQmlModels/private/qtqmlmodels-config_p.h>
+
+#define Q_QMLMODELS_PRIVATE_EXPORT Q_QMLMODELS_EXPORT
+#define Q_QMLMODELS_AUTOTEST_EXPORT Q_AUTOTEST_EXPORT
+
+#endif // QTQMLMODELSGLOBAL_P_H
diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp
index 9cddf61543..470b3c0f7a 100644
--- a/src/qmltest/quicktest.cpp
+++ b/src/qmltest/quicktest.cpp
@@ -70,6 +70,7 @@
#include <QtQml/QQmlFileSelector>
#include <private/qqmlcomponent_p.h>
+#include <private/qv4executablecompilationunit_p.h>
#ifdef QT_QMLTEST_WITH_WIDGETS
#include <QtWidgets/QApplication>
@@ -290,7 +291,8 @@ public:
m_errors += component.errors();
if (component.isReady()) {
- QQmlRefPointer<CompilationUnit> rootCompilationUnit = QQmlComponentPrivate::get(&component)->compilationUnit;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> rootCompilationUnit
+ = QQmlComponentPrivate::get(&component)->compilationUnit;
TestCaseEnumerationResult result = enumerateTestCases(rootCompilationUnit.data());
m_testCases = result.testCases + result.finalizedPartialTestCases();
m_errors += result.errors;
@@ -330,12 +332,13 @@ private:
}
};
- TestCaseEnumerationResult enumerateTestCases(CompilationUnit *compilationUnit, const Object *object = nullptr)
+ TestCaseEnumerationResult enumerateTestCases(QV4::ExecutableCompilationUnit *compilationUnit,
+ const Object *object = nullptr)
{
QQmlType testCaseType;
for (quint32 i = 0, count = compilationUnit->importCount(); i < count; ++i) {
const Import *import = compilationUnit->importAt(i);
- if (compilationUnit->stringAt(import->uriIndex) != QLatin1Literal("QtTest"))
+ if (compilationUnit->stringAt(import->uriIndex) != QLatin1String("QtTest"))
continue;
QString testCaseTypeName(QStringLiteral("TestCase"));
@@ -353,7 +356,9 @@ private:
if (!object) // Start at root of compilation unit if not enumerating a specific child
object = compilationUnit->objectAt(0);
- if (CompilationUnit *superTypeUnit = compilationUnit->resolvedTypes.value(object->inheritedTypeNameIndex)->compilationUnit.data()) {
+ if (QV4::ExecutableCompilationUnit *superTypeUnit
+ = compilationUnit->resolvedTypes.value(object->inheritedTypeNameIndex)
+ ->compilationUnit.data()) {
// We have a non-C++ super type, which could indicate we're a subtype of a TestCase
if (testCaseType.isValid() && superTypeUnit->url() == testCaseType.sourceUrl())
result.isTestCase = true;
@@ -363,7 +368,7 @@ private:
if (result.isTestCase) {
// Look for override of name in this type
for (auto binding = object->bindingsBegin(); binding != object->bindingsEnd(); ++binding) {
- if (compilationUnit->stringAt(binding->propertyNameIndex) == QLatin1Literal("name")) {
+ if (compilationUnit->stringAt(binding->propertyNameIndex) == QLatin1String("name")) {
if (binding->type == QV4::CompiledData::Binding::Type_String) {
result.testCaseName = compilationUnit->stringAt(binding->stringIndex);
} else {
@@ -382,10 +387,10 @@ private:
auto functionsEnd = compilationUnit->objectFunctionsEnd(object);
for (auto function = compilationUnit->objectFunctionsBegin(object); function != functionsEnd; ++function) {
QString functionName = compilationUnit->stringAt(function->nameIndex);
- if (!(functionName.startsWith(QLatin1Literal("test_")) || functionName.startsWith(QLatin1Literal("benchmark_"))))
+ if (!(functionName.startsWith(QLatin1String("test_")) || functionName.startsWith(QLatin1String("benchmark_"))))
continue;
- if (functionName.endsWith(QLatin1Literal("_data")))
+ if (functionName.endsWith(QLatin1String("_data")))
continue;
result.testFunctions << functionName;
@@ -554,7 +559,7 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch
// Register the test object
qmlRegisterSingletonType<QTestRootObject>("Qt.test.qtestroot", 1, 0, "QTestRootObject", testRootObject);
- QSet<QString> commandLineTestFunctions = QTest::testFunctions.toSet();
+ QSet<QString> commandLineTestFunctions(QTest::testFunctions.cbegin(), QTest::testFunctions.cend());
const bool filteringTestFunctions = !commandLineTestFunctions.isEmpty();
// Scan through all of the "tst_*.qml" files and run each of them
@@ -596,7 +601,7 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch
continue;
}
- const QSet<QString> availableTestSet = availableTestFunctions.toSet();
+ const QSet<QString> availableTestSet(availableTestFunctions.cbegin(), availableTestFunctions.cend());
if (filteringTestFunctions && !availableTestSet.intersects(commandLineTestFunctions))
continue;
commandLineTestFunctions.subtract(availableTestSet);
diff --git a/src/qmltest/quicktestevent.cpp b/src/qmltest/quicktestevent.cpp
index 5b07220c29..1e949615c2 100644
--- a/src/qmltest/quicktestevent.cpp
+++ b/src/qmltest/quicktestevent.cpp
@@ -241,7 +241,8 @@ namespace QtQuickTest
QTEST_ASSERT(stateKey == 0 || stateKey & Qt::KeyboardModifierMask);
stateKey &= static_cast<unsigned int>(Qt::KeyboardModifierMask);
- QWheelEvent we(pos, window->mapToGlobal(pos), QPoint(0, 0), QPoint(xDelta, yDelta), 0, Qt::Vertical, buttons, stateKey);
+ QWheelEvent we(pos, window->mapToGlobal(pos), QPoint(0, 0), QPoint(xDelta, yDelta), buttons,
+ stateKey, Qt::NoScrollPhase, false);
QSpontaneKeyEvent::setSpontaneous(&we); // hmmmm
if (!qApp->notify(window, &we))
diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp
index 4dbec585c4..49552e1901 100644
--- a/src/qmltest/quicktestresult.cpp
+++ b/src/qmltest/quicktestresult.cpp
@@ -62,7 +62,9 @@
#include <QtCore/qdebug.h>
#include <QtCore/QUrl>
#include <QtCore/QDir>
+#if QT_CONFIG(regularexpression)
#include <QtCore/qregularexpression.h>
+#endif
#include <QtQuick/qquickwindow.h>
#include <QtGui/qvector3d.h>
#include <QtGui/qimagewriter.h>
@@ -640,12 +642,9 @@ void QuickTestResult::warn(const QString &message, const QUrl &location, int lin
void QuickTestResult::ignoreWarning(const QJSValue &message)
{
if (message.isRegExp()) {
- // ### we should probably handle QRegularExpression conversion engine-side
- QRegExp re = message.toVariant().toRegExp();
- QRegularExpression::PatternOptions opts = re.caseSensitivity() ==
- Qt::CaseInsensitive ? QRegularExpression::CaseInsensitiveOption : QRegularExpression::NoPatternOption;
- QRegularExpression re2(re.pattern(), opts);
- QTestLog::ignoreMessage(QtWarningMsg, re2);
+#if QT_CONFIG(regularexpression)
+ QTestLog::ignoreMessage(QtWarningMsg, message.toVariant().toRegularExpression());
+#endif
} else {
QTestLog::ignoreMessage(QtWarningMsg, message.toString().toLatin1());
}
diff --git a/src/qmltest/quicktestresult_p.h b/src/qmltest/quicktestresult_p.h
index 3643826e5d..0d229ad713 100644
--- a/src/qmltest/quicktestresult_p.h
+++ b/src/qmltest/quicktestresult_p.h
@@ -57,7 +57,6 @@
#include <QtCore/qstringlist.h>
#include <QtCore/qscopedpointer.h>
#include <QtQuick/qquickitem.h>
-#include <QtQml/private/qv8engine_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/qmlworkerscript/doc/qtqmlworkerscript.qdocconf b/src/qmlworkerscript/doc/qtqmlworkerscript.qdocconf
new file mode 100644
index 0000000000..bb883cf39f
--- /dev/null
+++ b/src/qmlworkerscript/doc/qtqmlworkerscript.qdocconf
@@ -0,0 +1,37 @@
+include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+include($QT_INSTALL_DOCS/config/exampleurl-qtdeclarative.qdocconf)
+
+project = QtQmlWorkerScript
+description = Qt Qml WorkerScript Reference Documentation
+version = $QT_VERSION
+moduleheader = QtQmlWorkerScript
+qhp.projects = QtQmlWorkerScript
+
+qhp.QtQmlWorkerScript.file = qtqmlworkerscript.qhp
+qhp.QtQmlWorkerScript.namespace = org.qt-project.qtqmlworkerscript.$QT_VERSION_TAG
+qhp.QtQmlWorkerScript.virtualFolder = qtqmlworkerscript
+qhp.QtQmlWorkerScript.indexRoot =
+
+qhp.QtQmlWorkerScript.filterAttributes = qtqmlworkerscript $QT_VERSION qtrefdoc
+qhp.QtQmlWorkerScript.customFilters.Qt.name = QtQmlWorkerScript $QT_VERSION
+qhp.QtQmlWorkerScript.customFilters.Qt.filterAttributes = qtqmlworkerscript $QT_VERSION
+
+qhp.QtQmlWorkerScript.title = QML Types
+qhp.QtQmlWorkerScript.indexTitle = Qt QML WorkerScript QML Types
+qhp.QtQmlWorkerScript.selectors = qmlclass
+qhp.QtQmlWorkerScript.sortPages = true
+
+tagfile = qtqmlworkerscript.tags
+
+depends += qtcore qtqml qtdoc
+
+headerdirs += ..
+
+sourcedirs += .. \
+ ../../imports/workerscript
+
+exampledirs += ../../../examples/qml \
+ ../ \
+ snippets
+
+navigation.qmltypespage = "Qt Qml WorkerScript QML Types"
diff --git a/src/qmlworkerscript/qmlworkerscript.pro b/src/qmlworkerscript/qmlworkerscript.pro
new file mode 100644
index 0000000000..9f5e0e809a
--- /dev/null
+++ b/src/qmlworkerscript/qmlworkerscript.pro
@@ -0,0 +1,22 @@
+TARGET = QtQmlWorkerScript
+QT = core-private qml-private
+
+QMAKE_DOCS = $$PWD/doc/qtqmlworkerscript.qdocconf
+
+DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES QT_NO_FOREACH
+
+HEADERS += \
+ qqmlworkerscriptmodule_p.h \
+ qquickworkerscript_p.h \
+ qtqmlworkerscriptglobal.h \
+ qtqmlworkerscriptglobal_p.h \
+ qv4serialize_p.h
+
+SOURCES += \
+ qqmlworkerscriptmodule.cpp \
+ qquickworkerscript.cpp \
+ qv4serialize.cpp
+
+include(../3rdparty/masm/masm-defs.pri)
+
+load(qt_module)
diff --git a/src/qmlworkerscript/qqmlworkerscriptmodule.cpp b/src/qmlworkerscript/qqmlworkerscriptmodule.cpp
new file mode 100644
index 0000000000..98e82dbeba
--- /dev/null
+++ b/src/qmlworkerscript/qqmlworkerscriptmodule.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlworkerscriptmodule_p.h"
+#include "qquickworkerscript_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+
+void QQmlWorkerScriptModule::registerQuickTypes()
+{
+ // Don't add anything here. These are only for backwards compatibility.
+ const char uri[] = "QtQuick";
+ qmlRegisterType<QQuickWorkerScript>(uri, 2, 0, "WorkerScript");
+}
+
+#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+
+void QQmlWorkerScriptModule::defineModule()
+{
+ const char uri[] = "QtQml.WorkerScript";
+ qmlRegisterType<QQuickWorkerScript>(uri, 2, 0, "WorkerScript");
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlworkerscript/qqmlworkerscriptmodule_p.h b/src/qmlworkerscript/qqmlworkerscriptmodule_p.h
new file mode 100644
index 0000000000..a2efb304c1
--- /dev/null
+++ b/src/qmlworkerscript/qqmlworkerscriptmodule_p.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLWORKERSCRIPTMODULE_P_H
+#define QQMLWORKERSCRIPTMODULE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtqmlworkerscriptglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QMLWORKERSCRIPT_PRIVATE_EXPORT QQmlWorkerScriptModule
+{
+public:
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ static void registerQuickTypes();
+#endif
+ static void defineModule();
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLWORKERSCRIPTMODULE_P_H
diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qmlworkerscript/qquickworkerscript.cpp
index edb112276c..8b236697b9 100644
--- a/src/qml/types/qquickworkerscript.cpp
+++ b/src/qmlworkerscript/qquickworkerscript.cpp
@@ -37,12 +37,8 @@
**
****************************************************************************/
-#include "qtqmlglobal_p.h"
+#include "qtqmlworkerscriptglobal_p.h"
#include "qquickworkerscript_p.h"
-#if QT_CONFIG(qml_list_model)
-#include "qqmllistmodel_p.h"
-#include "qqmllistmodelworkeragent_p.h"
-#endif
#include <private/qqmlengine_p.h>
#include <private/qqmlexpression_p.h>
@@ -61,7 +57,6 @@
#include "qqmlnetworkaccessmanagerfactory.h"
#endif
-#include <private/qv8engine_p.h>
#include <private/qv4serialize_p.h>
#include <private/qv4value_p.h>
@@ -129,6 +124,15 @@ private:
QQmlError m_error;
};
+struct WorkerScript : public QV4::ExecutionEngine {
+ WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent);
+
+ QQuickWorkerScriptEnginePrivate *p = nullptr;
+ QUrl source;
+ QQuickWorkerScript *owner = nullptr;
+ int id = -1;
+};
+
class QQuickWorkerScriptEnginePrivate : public QObject
{
Q_OBJECT
@@ -144,23 +148,6 @@ public:
QMutex m_lock;
QWaitCondition m_wait;
- struct WorkerScript : public QV8Engine {
- WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent);
- ~WorkerScript() override;
-
-#if QT_CONFIG(qml_network)
- QNetworkAccessManager *networkAccessManager() override;
-#endif
-
- QQuickWorkerScriptEnginePrivate *p = nullptr;
- QUrl source;
- QQuickWorkerScript *owner = nullptr;
-#if QT_CONFIG(qml_network)
- QScopedPointer<QNetworkAccessManager> accessManager;
-#endif
- int id = -1;
- };
-
QHash<int, WorkerScript *> workers;
QV4::ReturnedValue getWorker(WorkerScript *);
@@ -189,7 +176,7 @@ QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4
const QV4::Value *, const QV4::Value *argv, int argc)
{
QV4::Scope scope(b);
- WorkerScript *script = static_cast<WorkerScript *>(scope.engine->v8Engine);
+ WorkerScript *script = static_cast<WorkerScript *>(scope.engine);
QV4::ScopedValue v(scope, argc > 0 ? argv[0] : QV4::Value::undefinedValue());
QByteArray data = QV4::Serialize::serialize(v, scope.engine);
@@ -234,21 +221,20 @@ void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &d
if (!script)
return;
- QV4::ExecutionEngine *v4 = QV8Engine::getV4(script);
- QV4::Scope scope(v4);
+ QV4::Scope scope(script);
QV4::ScopedString v(scope);
- QV4::ScopedObject worker(scope, v4->globalObject->get((v = v4->newString(QStringLiteral("WorkerScript")))));
+ QV4::ScopedObject worker(scope, script->globalObject->get((v = script->newString(QStringLiteral("WorkerScript")))));
QV4::ScopedFunctionObject onmessage(scope);
if (worker)
- onmessage = worker->get((v = v4->newString(QStringLiteral("onMessage"))));
+ onmessage = worker->get((v = script->newString(QStringLiteral("onMessage"))));
if (!onmessage)
return;
- QV4::ScopedValue value(scope, QV4::Serialize::deserialize(data, v4));
+ QV4::ScopedValue value(scope, QV4::Serialize::deserialize(data, script));
QV4::JSCallData jsCallData(scope, 1);
- *jsCallData->thisObject = v4->global();
+ *jsCallData->thisObject = script->global();
jsCallData->args[0] = value;
onmessage->call(jsCallData);
if (scope.hasException()) {
@@ -268,35 +254,33 @@ void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url)
if (!script)
return;
- QV4::ExecutionEngine *v4 = QV8Engine::getV4(script);
-
script->source = url;
if (fileName.endsWith(QLatin1String(".mjs"))) {
- auto moduleUnit = v4->loadModule(url);
+ auto moduleUnit = script->loadModule(url);
if (moduleUnit) {
- if (moduleUnit->instantiate(v4))
+ if (moduleUnit->instantiate(script))
moduleUnit->evaluate();
} else {
- v4->throwError(QStringLiteral("Could not load module file"));
+ script->throwError(QStringLiteral("Could not load module file"));
}
} else {
QString error;
- QV4::Scope scope(v4);
+ QV4::Scope scope(script);
QScopedPointer<QV4::Script> program;
- program.reset(QV4::Script::createFromFileOrCache(v4, /*qmlContext*/nullptr, fileName, url, &error));
+ program.reset(QV4::Script::createFromFileOrCache(script, /*qmlContext*/nullptr, fileName, url, &error));
if (program.isNull()) {
if (!error.isEmpty())
qWarning().nospace() << error;
return;
}
- if (!v4->hasException)
+ if (!script->hasException)
program->run();
}
- if (v4->hasException) {
- QQmlError error = v4->catchExceptionAsQmlError();
+ if (script->hasException) {
+ QQmlError error = script->catchExceptionAsQmlError();
reportScriptException(script, error);
}
}
@@ -390,48 +374,25 @@ QQuickWorkerScriptEngine::~QQuickWorkerScriptEngine()
yieldCurrentThread();
}
- d->deleteLater();
+ delete d;
}
-QQuickWorkerScriptEnginePrivate::WorkerScript::WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent)
- : QV8Engine(new QV4::ExecutionEngine)
- , p(parent)
+WorkerScript::WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent)
+ : p(parent)
, id(id)
{
- m_v4Engine->v8Engine = this;
-
initQmlGlobalObject();
- QV4::Scope scope(m_v4Engine);
+ QV4::Scope scope(this);
QV4::ScopedObject api(scope, scope.engine->newObject());
- QV4::ScopedString name(scope, m_v4Engine->newString(QStringLiteral("sendMessage")));
- QV4::ScopedValue sendMessage(scope, QV4::FunctionObject::createBuiltinFunction(m_v4Engine, name, method_sendMessage, 1));
+ QV4::ScopedString name(scope, newString(QStringLiteral("sendMessage")));
+ QV4::ScopedValue sendMessage(scope, QV4::FunctionObject::createBuiltinFunction(this, name, QQuickWorkerScriptEnginePrivate::method_sendMessage, 1));
api->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("sendMessage"))), sendMessage);
- m_v4Engine->globalObject->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("WorkerScript"))), api);
-}
-
-QQuickWorkerScriptEnginePrivate::WorkerScript::~WorkerScript()
-{
- delete m_v4Engine;
+ globalObject->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("WorkerScript"))), api);
}
-#if QT_CONFIG(qml_network)
-QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerScript::networkAccessManager()
-{
- if (!accessManager) {
- if (p->qmlengine && p->qmlengine->networkAccessManagerFactory()) {
- accessManager.reset(p->qmlengine->networkAccessManagerFactory()->create(p));
- } else {
- accessManager.reset(new QNetworkAccessManager(p));
- }
- }
- return accessManager.data();
-}
-#endif
-
int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner)
{
- typedef QQuickWorkerScriptEnginePrivate::WorkerScript WorkerScript;
WorkerScript *script = new WorkerScript(d->m_nextId++, d);
script->owner = owner;
@@ -445,8 +406,7 @@ int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner)
void QQuickWorkerScriptEngine::removeWorkerScript(int id)
{
- QQuickWorkerScriptEnginePrivate::WorkerScript* script = d->workers.value(id);
- if (script) {
+ if (WorkerScript *script = d->workers.value(id)) {
script->owner = nullptr;
QCoreApplication::postEvent(d, new WorkerRemoveEvent(id));
}
@@ -481,7 +441,7 @@ void QQuickWorkerScriptEngine::run()
\qmltype WorkerScript
\instantiates QQuickWorkerScript
\ingroup qtquick-threading
- \inqmlmodule QtQml
+ \inqmlmodule QtQml.WorkerScript
\brief Enables the use of threads in a Qt Quick application.
Use WorkerScript to run operations in a new thread.
@@ -622,7 +582,11 @@ QQuickWorkerScriptEngine *QQuickWorkerScript::engine()
return nullptr;
}
- m_engine = QQmlEnginePrivate::get(engine)->getWorkerScriptEngine();
+ QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
+ if (enginePrivate->workerScriptEngine == nullptr)
+ enginePrivate->workerScriptEngine = new QQuickWorkerScriptEngine(engine);
+ m_engine = qobject_cast<QQuickWorkerScriptEngine *>(enginePrivate->workerScriptEngine);
+ Q_ASSERT(m_engine);
m_scriptId = m_engine->registerWorkerScript(this);
if (m_source.isValid())
@@ -651,12 +615,10 @@ void QQuickWorkerScript::componentComplete()
bool QQuickWorkerScript::event(QEvent *event)
{
if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) {
- QQmlEngine *engine = qmlEngine(this);
- if (engine) {
+ if (QQmlEngine *engine = qmlEngine(this)) {
+ QV4::ExecutionEngine *v4 = engine->handle();
WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event);
- QV4::Scope scope(engine->handle());
- QV4::ScopedValue value(scope, QV4::Serialize::deserialize(workerEvent->data(), scope.engine));
- emit message(QQmlV4Handle(value));
+ emit message(QJSValue(v4, QV4::Serialize::deserialize(workerEvent->data(), v4)));
}
return true;
} else if (event->type() == (QEvent::Type)WorkerErrorEvent::WorkerError) {
diff --git a/src/qml/types/qquickworkerscript_p.h b/src/qmlworkerscript/qquickworkerscript_p.h
index 1a8d2ab076..87cf2e9754 100644
--- a/src/qml/types/qquickworkerscript_p.h
+++ b/src/qmlworkerscript/qquickworkerscript_p.h
@@ -83,7 +83,6 @@ private:
};
class QQmlV4Function;
-class QQmlV4Handle;
class Q_AUTOTEST_EXPORT QQuickWorkerScript : public QObject, public QQmlParserStatus
{
Q_OBJECT
@@ -102,7 +101,7 @@ public Q_SLOTS:
Q_SIGNALS:
void sourceChanged();
- void message(const QQmlV4Handle &messageObject);
+ void message(const QJSValue &messageObject);
protected:
void classBegin() override;
diff --git a/src/qmlworkerscript/qtqmlworkerscriptglobal.h b/src/qmlworkerscript/qtqmlworkerscriptglobal.h
new file mode 100644
index 0000000000..be3ea4e12a
--- /dev/null
+++ b/src/qmlworkerscript/qtqmlworkerscriptglobal.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTQMLWORKERSCRIPTGLOBAL_H
+#define QTQMLWORKERSCRIPTGLOBAL_H
+
+#include <QtQml/qtqmlglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_STATIC)
+# if defined(QT_BUILD_QMLWORKERSCRIPT_LIB)
+# define Q_QMLWORKERSCRIPT_EXPORT Q_DECL_EXPORT
+# else
+# define Q_QMLWORKERSCRIPT_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define Q_QMLWORKERSCRIPT_EXPORT
+#endif
+
+QT_END_NAMESPACE
+#endif // QTQMLWORKERSCRIPTGLOBAL_H
diff --git a/src/qmlworkerscript/qtqmlworkerscriptglobal_p.h b/src/qmlworkerscript/qtqmlworkerscriptglobal_p.h
new file mode 100644
index 0000000000..34236cd79e
--- /dev/null
+++ b/src/qmlworkerscript/qtqmlworkerscriptglobal_p.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTQMLWORKERSCRIPTGLOBAL_P_H
+#define QTQMLWORKERSCRIPTGLOBAL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQml/private/qtqmlglobal_p.h>
+#include <QtQmlWorkerScript/qtqmlworkerscriptglobal.h>
+
+#define Q_QMLWORKERSCRIPT_PRIVATE_EXPORT Q_QMLWORKERSCRIPT_EXPORT
+#define Q_QMLWORKERSCRIPT_AUTOTEST_EXPORT Q_AUTOTEST_EXPORT
+
+#endif // QTQMLWORKERSCRIPTGLOBAL_P_H
diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qmlworkerscript/qv4serialize.cpp
index 50871a4d87..a5e62d3e35 100644
--- a/src/qml/jsruntime/qv4serialize.cpp
+++ b/src/qmlworkerscript/qv4serialize.cpp
@@ -39,12 +39,6 @@
#include "qv4serialize_p.h"
-#include <private/qv8engine_p.h>
-#if QT_CONFIG(qml_list_model)
-#include <private/qqmllistmodel_p.h>
-#include <private/qqmllistmodelworkeragent_p.h>
-#endif
-
#include <private/qv4value_p.h>
#include <private/qv4dateobject_p.h>
#include <private/qv4regexpobject_p.h>
@@ -86,9 +80,7 @@ enum Type {
WorkerNumber,
WorkerDate,
WorkerRegexp,
-#if QT_CONFIG(qml_list_model)
WorkerListModel,
-#endif
#if QT_CONFIG(qml_sequence_object)
WorkerSequence
#endif
@@ -236,18 +228,15 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine
} else if (const QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) {
// XXX TODO: Generalize passing objects between the main thread and worker scripts so
// that others can trivially plug in their elements.
-#if QT_CONFIG(qml_list_model)
- QQmlListModel *lm = qobject_cast<QQmlListModel *>(qobjectWrapper->object());
- if (lm && lm->agent()) {
- QQmlListModelWorkerAgent *agent = lm->agent();
- agent->addref();
- push(data, valueheader(WorkerListModel));
- push(data, (void *)agent);
- return;
+ if (QObject *lm = qobjectWrapper->object()) {
+ if (QObject *agent = qvariant_cast<QObject *>(lm->property("agent"))) {
+ if (QMetaObject::invokeMethod(agent, "addref")) {
+ push(data, valueheader(WorkerListModel));
+ push(data, (void *)agent);
+ return;
+ }
+ }
}
-#else
- Q_UNUSED(qobjectWrapper);
-#endif
// No other QObject's are allowed to be sent
push(data, valueheader(WorkerUndefined));
} else if (const Object *o = v.as<Object>()) {
@@ -299,6 +288,41 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine
}
}
+struct VariantRef
+{
+ VariantRef() : obj(nullptr) {}
+ VariantRef(const VariantRef &r) : obj(r.obj) { addref(); }
+ VariantRef(QObject *a) : obj(a) { addref(); }
+ ~VariantRef() { release(); }
+
+ VariantRef &operator=(const VariantRef &o) {
+ o.addref();
+ release();
+ obj = o.obj;
+ return *this;
+ }
+
+ void addref() const
+ {
+ if (obj)
+ QMetaObject::invokeMethod(obj, "addref");
+ }
+
+ void release() const
+ {
+ if (obj)
+ QMetaObject::invokeMethod(obj, "release");
+
+ }
+
+ QObject *obj;
+};
+
+QT_END_NAMESPACE
+Q_DECLARE_METATYPE(VariantRef)
+Q_DECLARE_METATYPE(QV4::ExecutionEngine *)
+QT_BEGIN_NAMESPACE
+
ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
{
quint32 header = popUint32(data);
@@ -367,24 +391,21 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
data += ALIGN(length * sizeof(quint16));
return Encode(engine->newRegExpObject(pattern, flags));
}
-#if QT_CONFIG(qml_list_model)
case WorkerListModel:
{
- void *ptr = popPtr(data);
- QQmlListModelWorkerAgent *agent = (QQmlListModelWorkerAgent *)ptr;
+ QObject *agent = reinterpret_cast<QObject *>(popPtr(data));
QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(engine, agent));
// ### Find a better solution then the ugly property
- QQmlListModelWorkerAgent::VariantRef ref(agent);
- QVariant var = qVariantFromValue(ref);
+ VariantRef ref(agent);
+ QVariant var = QVariant::fromValue(ref);
QV4::ScopedValue v(scope, scope.engine->fromVariant(var));
QV4::ScopedString s(scope, engine->newString(QStringLiteral("__qml:hidden:ref")));
rv->as<Object>()->defineReadonlyProperty(s, v);
- agent->release();
- agent->setEngine(engine);
+ QMetaObject::invokeMethod(agent, "release");
+ agent->setProperty("engine", QVariant::fromValue(engine));
return rv->asReturnedValue();
}
-#endif
#if QT_CONFIG(qml_sequence_object)
case WorkerSequence:
{
@@ -424,4 +445,3 @@ ReturnedValue Serialize::deserialize(const QByteArray &data, ExecutionEngine *en
}
QT_END_NAMESPACE
-
diff --git a/src/qml/jsruntime/qv4serialize_p.h b/src/qmlworkerscript/qv4serialize_p.h
index c8700c3ca5..c8700c3ca5 100644
--- a/src/qml/jsruntime/qv4serialize_p.h
+++ b/src/qmlworkerscript/qv4serialize_p.h
diff --git a/src/quick/configure.json b/src/quick/configure.json
index 9ec3531ef4..0cb1e7470b 100644
--- a/src/quick/configure.json
+++ b/src/quick/configure.json
@@ -1,8 +1,10 @@
{
"module": "quick",
"depends": [
+ "core-private",
"qml-private",
- "gui-private"
+ "gui-private",
+ "qmlmodels-private"
],
"testDir": "../../config.tests",
@@ -112,6 +114,7 @@
"label": "TableView item",
"purpose": "Provides the TableView item.",
"section": "Qt Quick",
+ "condition": "features.qml-table-model",
"output": [
"privateFeature"
]
@@ -178,6 +181,18 @@
"output": [
"privateFeature"
]
+ },
+ "quick-draganddrop": {
+ "label": "Drag & Drop",
+ "purpose": "Drag and drop support for Qt Quick",
+ "section": "Qt Quick",
+ "condition": [
+ "features.draganddrop",
+ "features.regularexpression"
+ ],
+ "output": [
+ "publicFeature"
+ ]
}
},
diff --git a/src/quick/designer/qqmldesignermetaobject.cpp b/src/quick/designer/qqmldesignermetaobject.cpp
index 2efcdada8b..de7da7f9be 100644
--- a/src/quick/designer/qqmldesignermetaobject.cpp
+++ b/src/quick/designer/qqmldesignermetaobject.cpp
@@ -45,7 +45,6 @@
#include <QDebug>
#include <private/qqmlengine_p.h>
-#include <private/qqmlpropertycache_p.h>
QT_BEGIN_NAMESPACE
@@ -137,7 +136,7 @@ QQmlDesignerMetaObject::QQmlDesignerMetaObject(QObject *object, QQmlEngine *engi
//Assign cache to object
if (ddata && ddata->propertyCache) {
cache->setParent(ddata->propertyCache);
- cache->invalidate(engine, this);
+ cache->invalidate(this);
ddata->propertyCache->release();
ddata->propertyCache = cache.data();
ddata->propertyCache->addref();
@@ -162,7 +161,7 @@ void QQmlDesignerMetaObject::createNewDynamicProperty(const QString &name)
//Updating cache
QQmlPropertyCache *oldParent = cache->parent();
- QQmlEnginePrivate::get(m_context->engine())->cache(this)->invalidate(m_context->engine(), this);
+ QQmlEnginePrivate::get(m_context->engine())->cache(this)->invalidate(this);
cache->setParent(oldParent);
QQmlProperty property(myObject(), name, m_context);
diff --git a/src/quick/designer/qquickdesignercustomparserobject.cpp b/src/quick/designer/qquickdesignercustomparserobject.cpp
index 50a8b6a25b..841aae5bc3 100644
--- a/src/quick/designer/qquickdesignercustomparserobject.cpp
+++ b/src/quick/designer/qquickdesignercustomparserobject.cpp
@@ -46,12 +46,12 @@ QQuickDesignerCustomParserObject::QQuickDesignerCustomParserObject()
}
-void QQuickDesignerCustomParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &)
+void QQuickDesignerCustomParser::verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &)
{
/* Nothing to do we accept anything */
}
-void QQuickDesignerCustomParser::applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &)
+void QQuickDesignerCustomParser::applyBindings(QObject *, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &)
{
/* Nothing to do we accept anything */
}
diff --git a/src/quick/designer/qquickdesignercustomparserobject_p.h b/src/quick/designer/qquickdesignercustomparserobject_p.h
index b38417d102..4da00ea841 100644
--- a/src/quick/designer/qquickdesignercustomparserobject_p.h
+++ b/src/quick/designer/qquickdesignercustomparserobject_p.h
@@ -70,8 +70,8 @@ public:
QQuickDesignerCustomParser()
: QQmlCustomParser(AcceptsAttachedProperties | AcceptsSignalHandlers) {}
- void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) override;
- void applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
+ void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) override;
+ void applyBindings(QObject *obj, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
};
QT_END_NAMESPACE
diff --git a/src/quick/designer/qquickdesignerwindowmanager.cpp b/src/quick/designer/qquickdesignerwindowmanager.cpp
index 093559a572..9648a40a23 100644
--- a/src/quick/designer/qquickdesignerwindowmanager.cpp
+++ b/src/quick/designer/qquickdesignerwindowmanager.cpp
@@ -39,10 +39,10 @@
#include "qquickdesignerwindowmanager_p.h"
#include "private/qquickwindow_p.h"
+#include <QtQuick/QQuickWindow>
#if QT_CONFIG(opengl)
-# include <QtQuick/private/qsgdefaultrendercontext_p.h>
+#include <private/qsgdefaultrendercontext_p.h>
#endif
-#include <QtQuick/QQuickWindow>
QT_BEGIN_NAMESPACE
@@ -74,7 +74,12 @@ void QQuickDesignerWindowManager::makeOpenGLContext(QQuickWindow *window)
m_openGlContext->create();
if (!m_openGlContext->makeCurrent(window))
qWarning("QQuickWindow: makeCurrent() failed...");
- m_renderContext->initialize(m_openGlContext.data());
+ QSGDefaultRenderContext::InitParams params;
+ params.sampleCount = qMax(1, m_openGlContext->format().samples());
+ params.openGLContext = m_openGlContext.data();
+ params.initialSurfacePixelSize = window->size() * window->effectiveDevicePixelRatio();
+ params.maybeSurface = window;
+ m_renderContext->initialize(&params);
} else {
m_openGlContext->makeCurrent(window);
}
diff --git a/src/quick/designer/qquickdesignerwindowmanager_p.h b/src/quick/designer/qquickdesignerwindowmanager_p.h
index 5322b6c421..5e387ff5b9 100644
--- a/src/quick/designer/qquickdesignerwindowmanager_p.h
+++ b/src/quick/designer/qquickdesignerwindowmanager_p.h
@@ -55,18 +55,17 @@
#include <private/qsgrenderloop_p.h>
#include <private/qtquickglobal_p.h>
-#include <QtQuick/private/qsgcontext_p.h>
+#include <private/qsgcontext_p.h>
#if QT_CONFIG(opengl)
# include <QtGui/QOpenGLContext>
#endif
-
QT_BEGIN_NAMESPACE
class QQuickWindow;
class QSGContext;
-class QSGRenderContext;
+class QSGDefaultRenderContext;
class QAnimationDriver;
class QQuickDesignerWindowManager : public QSGRenderLoop
diff --git a/src/quick/doc/qtquick.qdocconf b/src/quick/doc/qtquick.qdocconf
index 56f1f20b3e..91458527dd 100644
--- a/src/quick/doc/qtquick.qdocconf
+++ b/src/quick/doc/qtquick.qdocconf
@@ -41,9 +41,11 @@ tagfile = ../../../doc/qtquick/qtquick.tags
depends += qtcore qtqml qtqmltest qtgui qtlinguist qtquickcontrols qtquickcontrols1 qtdoc qtquickdialogs qtsensors qtwidgets qmake qtmultimedia qtgraphicaleffects qtsql qtxmlpatterns
headerdirs += ..\
+ ../../quick \
../../quickwidgets
sourcedirs += .. \
+ ../../quick \
../../quickwidgets
exampledirs += ../../../examples/quick \
diff --git a/src/quick/doc/snippets/cmake-macros/examples.cmake b/src/quick/doc/snippets/cmake-macros/examples.cmake
new file mode 100644
index 0000000000..8ca6180f9b
--- /dev/null
+++ b/src/quick/doc/snippets/cmake-macros/examples.cmake
@@ -0,0 +1,6 @@
+#! [qt5_import_qml_plugins]
+find_package(Qt5 COMPONENTS Quick QmlImportScanner)
+add_executable(myapp main.cpp)
+target_link_libraries(myapp Qt5::Quick)
+qt5_import_qml_plugins(myapp)
+#! [qt5_import_plugins]
diff --git a/src/quick/doc/snippets/pointerHandlers/handlerFlick.qml b/src/quick/doc/snippets/pointerHandlers/handlerFlick.qml
new file mode 100644
index 0000000000..f74b075357
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/handlerFlick.qml
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+//![0]
+import QtQuick 2.14
+import Qt.labs.animation 1.0
+
+Item {
+ width: 320; height: 480
+ Flow {
+ id: content
+ width: parent.width
+ spacing: 2; padding: 2
+
+ WheelHandler {
+ orientation: Qt.Vertical
+ property: "y"
+ rotationScale: 15
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ onActiveChanged: if (!active) ybr.returnToBounds()
+ }
+
+ DragHandler {
+ xAxis.enabled: false
+ onActiveChanged: if (!active) ybr.returnToBounds()
+ }
+
+ BoundaryRule on y {
+ id: ybr
+ minimum: content.parent.height - content.height
+ maximum: 0
+ minimumOvershoot: 400; maximumOvershoot: 400
+ overshootFilter: BoundaryRule.Peak
+ }
+
+ Repeater {
+ model: 1000
+ Rectangle { color: "gray"; width: 10 + Math.random() * 100; height: 15 }
+ }
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/wheelHandler.qml b/src/quick/doc/snippets/pointerHandlers/wheelHandler.qml
new file mode 100644
index 0000000000..2c9913ac97
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/wheelHandler.qml
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+//![0]
+import QtQuick 2.14
+
+Rectangle {
+ width: 170; height: 120
+ color: "green"; antialiasing: true
+
+ WheelHandler {
+ property: "rotation"
+ onWheel: console.log("rotation", event.angleDelta.y,
+ "scaled", rotation, "@", point.position, "=>", parent.rotation)
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/qml/boundaryRule.qml b/src/quick/doc/snippets/qml/boundaryRule.qml
new file mode 100644
index 0000000000..c010f5de5e
--- /dev/null
+++ b/src/quick/doc/snippets/qml/boundaryRule.qml
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+//![0]
+import QtQuick 2.14
+import Qt.labs.animation 1.0
+
+Rectangle {
+ id: root
+ width: 170; height: 120
+ color: "green"
+
+ DragHandler {
+ id: dragHandler
+ yAxis.minimum: -1000
+ xAxis.minimum: -1000
+ onActiveChanged: if (!active) xbr.returnToBounds();
+ }
+
+ BoundaryRule on x {
+ id: xbr
+ minimum: -50
+ maximum: 100
+ minimumOvershoot: 40
+ maximumOvershoot: 40
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/qml/qml-extending-types/signals/Button.qml b/src/quick/doc/snippets/qml/qml-extending-types/signals/Button.qml
index b40f517234..96f7fc1c7c 100644
--- a/src/quick/doc/snippets/qml/qml-extending-types/signals/Button.qml
+++ b/src/quick/doc/snippets/qml/qml-extending-types/signals/Button.qml
@@ -55,7 +55,7 @@ Rectangle {
id: rect
width: 100; height: 100
- signal buttonClicked(int xPos, int yPos)
+ signal buttonClicked(xPos: int, yPos: int)
MouseArea {
anchors.fill: parent
diff --git a/src/quick/doc/snippets/qml/regularexpression.qml b/src/quick/doc/snippets/qml/regularexpression.qml
new file mode 100644
index 0000000000..f6f4a4171a
--- /dev/null
+++ b/src/quick/doc/snippets/qml/regularexpression.qml
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.14
+//![0]
+TextInput {
+ id: hexNumber
+ validator: RegularExpressionValidator { regularExpression: /[0-9A-F]+/ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/qml/tableview/tablemodel.cpp b/src/quick/doc/snippets/qml/tableview/cpp-tablemodel.cpp
index ea9f76f131..ea9f76f131 100644
--- a/src/quick/doc/snippets/qml/tableview/tablemodel.cpp
+++ b/src/quick/doc/snippets/qml/tableview/cpp-tablemodel.cpp
diff --git a/src/quick/doc/snippets/qml/tableview/tablemodel.qml b/src/quick/doc/snippets/qml/tableview/cpp-tablemodel.qml
index 8a8ec94958..8a8ec94958 100644
--- a/src/quick/doc/snippets/qml/tableview/tablemodel.qml
+++ b/src/quick/doc/snippets/qml/tableview/cpp-tablemodel.qml
diff --git a/src/quick/doc/snippets/qml/tableview/qml-tablemodel.qml b/src/quick/doc/snippets/qml/tableview/qml-tablemodel.qml
new file mode 100644
index 0000000000..a01a2a628a
--- /dev/null
+++ b/src/quick/doc/snippets/qml/tableview/qml-tablemodel.qml
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//![0]
+import QtQuick 2.14
+import Qt.labs.qmlmodels 1.0
+
+TableView {
+ anchors.fill: parent
+ columnSpacing: 1
+ rowSpacing: 1
+ clip: true
+
+ model: TableModel {
+ TableModelColumn { display: "name" }
+ TableModelColumn { display: "color" }
+
+ rows: [
+ {
+ "name": "cat",
+ "color": "black"
+ },
+ {
+ "name": "dog",
+ "color": "brown"
+ },
+ {
+ "name": "bird",
+ "color": "white"
+ }
+ ]
+ }
+
+ delegate: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 50
+ border.width: 1
+
+ Text {
+ text: display
+ anchors.centerIn: parent
+ }
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/qml/texthandling.qml b/src/quick/doc/snippets/qml/texthandling.qml
index 0fb5fc130f..6c086f8501 100644
--- a/src/quick/doc/snippets/qml/texthandling.qml
+++ b/src/quick/doc/snippets/qml/texthandling.qml
@@ -48,7 +48,7 @@
**
****************************************************************************/
//! [document]
-import QtQuick 2.0
+import QtQuick 2.14
//! [parent begin]
@@ -83,7 +83,7 @@ Column {
}
TextInput {
focus: true
- validator: RegExpValidator { regExp: /fruit basket/ }
+ validator: RegularExpressionValidator { regularExpression: /fruit basket/ }
}
}
//! [regexp validator]
diff --git a/src/quick/doc/src/cmake-macros.qdoc b/src/quick/doc/src/cmake-macros.qdoc
new file mode 100644
index 0000000000..b643a9e4e4
--- /dev/null
+++ b/src/quick/doc/src/cmake-macros.qdoc
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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$
+**
+****************************************************************************/
+
+/*!
+\page qtqml-cmake-qt5-import-qml-plugins.html
+\ingroup cmake-macros-qtqml
+
+\title qt5_import_qml_plugins
+
+\brief Scans \c{.qml} files and imports required QML static plugins
+
+\section1 Overview
+
+\badcode
+find_package(Qt5QmlImportScanner REQUIRED)
+qt5_import_qml_plugins(<TARGET>)
+\endcode
+
+\section1 Description
+
+Runs \c{qmlimportscanner} at configure time to find the static QML plugins
+used and links them to the given target.
+
+\note When used with a non-static Qt build, this function does nothing.
+
+This CMake command was introduced in Qt 5.14.
+
+\section1 Example
+
+\snippet cmake-macros/examples.cmake qt5_import_qml_plugins
+
+*/
diff --git a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc
index 8edc5cb0b6..e83aa39734 100644
--- a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc
+++ b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc
@@ -31,15 +31,35 @@
\section1 Scene Graph Adaptations in Qt Quick
-Originally, Qt Quick always relied on OpenGL (OpenGL ES 2.0 or OpenGL 2.0) to parse the scene graph
-and render the results to a render target. From Qt 5.8 onwards, Qt Quick also supports rendering in
-software and with Direct3D 12.
+Originally, Qt Quick always relied on OpenGL (OpenGL ES 2.0 or OpenGL 2.0) to
+parse the scene graph and render the results to a render target
+
+From Qt 5.8 onwards, Qt Quick also supports rendering in software, with OpenVG,
+and with Direct3D 12. This is realized by having additional scene graph
+adaptations, either in form of plugins (d3d12, openvg) or built-in to the Qt
+Quick library (software). The default adaptation continues to rely directly on
+OpenGL.
+
+From Qt 5.14 onwards, the default adaptation gains the option of rendering via
+a graphics abstraction layer, the Qt Rendering Hardware Interface (RHI),
+provided by the \l QtGui module. When enabled, no direct OpenGL calls are made.
+Rather, the scene graph renders by using the APIs provided by the abstraction
+layer, which is then translated into OpenGL, Vulkan, Metal, or Direct 3D calls.
+Shader handling is also unified by writing shader code once, compiling to
+\l{https://www.khronos.org/spir/}{SPIR-V}, and then translating to the language
+appropriate for the various graphics APIs.
\target Switching Between the Adaptation Used by the Application
\section1 Switch Between Adaptations in Your Application
-The default rendering backend is still OpenGL, but in Qt builds with OpenGL support disabled, the
-default is the software renderer. You can override this in one of two ways:
+Unlike \c software or \c d3d12, the RHI-based renderer is not an additional
+adaptation, and is always built-in. As of Qt 5.14 it can be enabled by setting
+the environment variable \c{QSG_RHI} to a non-zero value before starting the
+application, or via \l QQuickWindow::setScenegraphBackend() in combination with
+\l QSGRendererInterface::GraphicsApi. When none of this is done, OpenGL is used
+directly like in previous versions.
+
+Switching to a different adaptation can be achieved in two ways:
\list
\li Use an environment variable - Set the \c{QT_QUICK_BACKEND} or the legacy
@@ -51,7 +71,8 @@ default is the software renderer. You can override this in one of two ways:
The following backends are supported:
\list
- \li OpenGL - Request with the \c{""} string or the QSGRendererInterface::OpenGL enum value.
+ \li Default - Request with the \c{""} string or a QSGRendererInterface::GraphicsApi enum value
+ different than the ones listed below.
\li Software - Request with the \c{"software"} string or the QSGRendererInterface::Software
enum value.
\li Direct3D 12 - Request with the \c{"d3d12"} string or the QSGRendererInterface::Direct3D12
@@ -64,16 +85,25 @@ To find out which backend is in use, you can enable basic scene graph informatio
\c{QSG_INFO} environment variable or the \c{qt.scenegraph.general} logging category. This results
in some information being printed onto the debug output, during application startup.
-\note Typically, adaptations other than OpenGL come with a set of limitations as they are unlikely
- to provide a feature set that's 100% compatible with OpenGL. However, these adaptations may
- provide their own specific advantages in certain areas. For more information on the various
- adaptations, refer to the sections below.
+\note In Qt builds with OpenGL disabled, the default adaptation is \c software.
+This may change in future releases.
+
+\note Typically, adaptations other than the default one come with a set of
+limitations as they are unlikely to provide a feature set that's 100%
+compatible with OpenGL. However, these adaptations may provide their own
+specific advantages in certain areas. For more information on the various
+adaptations, refer to the sections below.
+
+\section1 Default Adaptation
-\section1 OpenGL ES 2.0 and OpenGL 2.0 Adaptation
+When using OpenGL directly, the default adaptation is capable of providing the
+full Qt Quick 2 feature set. For more details, see
+\l{qtquick-visualcanvas-scenegraph-renderer.html}{Default Adaptation}.
-The OpenGL adaptation is the default adaptation, which is capable of providing the full Qt Quick 2
-feature set. For more details, see
-\l{qtquick-visualcanvas-scenegraph-renderer.html}{OpenGL Adaptation}.
+When using OpenGL, Vulkan, Metal, or Direct 3D via the RHI, the default
+adaptation is capable of providing most features, including the full batching
+renderer described in \l{qtquick-visualcanvas-scenegraph-renderer.html}{Default
+Adaptation}, but some additional features may not be available as of Qt 5.14.
\section1 Software Adaptation
diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
index ee6c501c71..e09c430e43 100644
--- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
+++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
@@ -31,15 +31,13 @@
\section1 The Scene Graph in Qt Quick
-Qt Quick 2 makes use of a dedicated scene graph based and a series of
-adaptations of which the default uses OpenGL ES 2.0 or OpenGL 2.0 for
-its rendering. Using a scene graph for graphics rather than the
-traditional imperative painting systems (QPainter and
-similar), means the scene to be rendered can be retained between
-frames and the complete set of primitives to render is known before
-rendering starts. This opens up for a number of optimizations, such as
-batch rendering to minimize state changes and discarding obscured
-primitives.
+Qt Quick 2 makes use of a dedicated scene graph that is then traversed and
+rendered via a graphics API such as OpenGL ES, OpenGL, Vulkan, Metal, or Direct
+3D. Using a scene graph for graphics rather than the traditional imperative
+painting systems (QPainter and similar), means the scene to be rendered can be
+retained between frames and the complete set of primitives to render is known
+before rendering starts. This opens up for a number of optimizations, such as
+batch rendering to minimize state changes and discarding obscured primitives.
For example, say a user-interface contains a list of ten items
where each item has a background color, an icon and a text. Using the
@@ -63,9 +61,10 @@ independently of the state of the items. On many platforms, the scene
graph will even be rendered on a dedicated render thread while the GUI
thread is preparing the next frame's state.
-\note Much of the information listed on this page is specific to the
-default OpenGL adaptation of the Qt Quick Scene graph. For more information
-about the different scene graph adaptations see
+\note Much of the information listed on this page is specific to the built-in,
+default behavior of the Qt Quick Scene graph. When using an alternative scene
+graph adaptation, such as, the \c software adaptation, not all concepts may
+apply. For more information about the different scene graph adaptations see
\l{qtquick-visualcanvas-adaptations.html}{Scene Graph Adaptations}.
@@ -106,10 +105,10 @@ Custom nodes are added to the scene graph by subclassing
QQuickItem::updatePaintNode() and setting the
\l {QQuickItem::ItemHasContents} flag.
-\warning It is crucial that OpenGL operations and interaction with the
-scene graph happens exclusively on the render thread, primarily
-during the updatePaintNode() call. The rule of thumb is to only
-use classes with the "QSG" prefix inside the
+\warning It is crucial that native graphics (OpenGL, Vulkan, Metal, etc.)
+operations and interaction with the scene graph happens exclusively on the
+render thread, primarily during the updatePaintNode() call. The rule of thumb
+is to only use classes with the "QSG" prefix inside the
QQuickItem::updatePaintNode() function.
For more details, see the \l {Scene Graph - Custom Geometry}.
@@ -133,11 +132,11 @@ simplifies cleanup when the scene graph lives outside the GUI thread.
\section2 Materials
-The material describes how the interior of a geometry in a \l
-QSGGeometryNode is filled. It encapsulates an OpenGL shader program
-and provides ample flexibility in what can be achieved, though most of
-the Qt Quick items themselves only use very basic materials, such as
-solid color and texture fills.
+The material describes how the interior of a geometry in a \l QSGGeometryNode
+is filled. It encapsulates graphics shaders for the vertex and fragment stages
+of the graphics pipeline and provides ample flexibility in what can be
+achieved, though most of the Qt Quick items themselves only use very basic
+materials, such as solid color and texture fills.
For users who just want to apply custom shading to a QML Item type,
it is possible to do this directly in QML using the \l ShaderEffect
@@ -151,11 +150,11 @@ For more details, see the \l {Scene Graph - Simple Material}
\section2 Convenience Nodes
-The scene graph API is very low-level and focuses on performance
-rather than convenience. Writing custom geometries and materials from
-scratch, even the most basic ones, requires a non-trivial amount of
-code. For this reason, the API includes a few convenience classes to
-make the most common custom nodes readily available.
+The scene graph API is low-level and focuses on performance rather than
+convenience. Writing custom geometries and materials from scratch, even the
+most basic ones, requires a non-trivial amount of code. For this reason, the
+API includes a few convenience classes to make the most common custom nodes
+readily available.
\list
\li \l QSGSimpleRectNode - a QSGGeometryNode subclass which defines a
@@ -169,15 +168,16 @@ a rectangular geometry with a texture material.
\section1 Scene Graph and Rendering
-The rendering of the scene graph happens internally in the
-QQuickWindow class, and there is no public API to access it. There are,
-however, a few places in the rendering pipeline where the user can
-attach application code. This can be used to add custom scene graph
-content or render raw OpenGL content. The integration points are
-defined by the render loop.
+The rendering of the scene graph happens internally in the QQuickWindow class,
+and there is no public API to access it. There are, however, a few places in
+the rendering pipeline where the user can attach application code. This can be
+used to add custom scene graph content or to insert arbitrary rendering
+commands by directly calling the graphics API (OpenGL, Vulkan, Metal, etc.)
+that is in use by the scene graph. The integration points are defined by the
+render loop.
-For detailed description of how the scene graph renderer for OpenGL
-works, see \l {Qt Quick Scene Graph OpenGL Renderer}.
+For detailed description of how the scene graph renderer works, see \l {Qt
+Quick Scene Graph Default Renderer}.
There are three render loop variants available: \c basic, \c windows,
and \c threaded. Out of these, \c basic and \c windows are
@@ -189,14 +189,14 @@ satisfactory, or for testing purposes, the environment variable
verify which render loop is in use, enable the \c qt.scenegraph.general
\l {QLoggingCategory}{logging category}.
-\note The \c threaded and \c windows render loops rely on the OpenGL
-implementation for throttling by requesting a swap interval of 1. Some
-graphics drivers allow users to override this setting and turn it off,
-ignoring Qt's request. Without blocking in the swap buffers operation
-(or elsewhere), the render loop will run animations too fast and spin
-the CPU at 100%. If a system is known to be unable to provide
-vsync-based throttling, use the \c basic render loop instead by
-setting \c {QSG_RENDER_LOOP=basic} in the environment.
+\note The \c threaded and \c windows render loops rely on the graphics API
+implementation for throttling, for example, by requesting a swap interval of 1
+in case of OpenGL. Some graphics drivers allow users to override this setting
+and turn it off, ignoring Qt's request. Without blocking in the swap buffers
+operation (or elsewhere), the render loop will run animations too fast and spin
+the CPU at 100%. If a system is known to be unable to provide vsync-based
+throttling, use the \c basic render loop instead by setting \c
+{QSG_RENDER_LOOP=basic} in the environment.
\section2 Threaded Render Loop ("threaded")
@@ -207,8 +207,9 @@ waiting for a blocking swap buffer call. This offers significant
performance improvements, but imposes certain restrictions on where
and when interaction with the scene graph can happen.
-The following is a simple outline of how a frame gets
-composed with the threaded render loop.
+The following is a simple outline of how a frame gets rendered with the
+threaded render loop and OpenGL. The steps are the same with other graphics
+APIs as well, apart from the OpenGL context specifics.
\image sg-renderloop-threaded.png
@@ -219,8 +220,8 @@ to be called. This can be the result of for instance an animation or
user input. An event is posted to the render thread to initiate a new
frame.
-\li The render thread prepares to draw a new frame and makes the
-OpenGL context current and initiates a block on the GUI thread.
+\li The render thread prepares to draw a new frame and initiates a block on the
+GUI thread.
\li While the render thread is preparing the new frame, the GUI thread
calls QQuickItem::updatePolish() to do final touch-up of items before
@@ -243,23 +244,27 @@ time the QML items and the nodes in the scene graph interact.
\li The scene graph is rendered:
\list 1
- \li The QQuickWindow::beforeRendering() signal is
- emitted. Applications can make direct connections
- (using Qt::DirectConnection) to this signal to use custom OpenGL calls
- which will then stack visually beneath the QML scene.
+ \li The QQuickWindow::beforeRendering() signal is emitted. Applications can
+ make direct connections (using Qt::DirectConnection) to this signal to use
+ custom graphics API calls which will then stack visually beneath the QML
+ scene.
\li Items that have specified QSGNode::UsePreprocess, will have their
QSGNode::preprocess() function invoked.
- \li The renderer processes the nodes and calls OpenGL functions.
+ \li The renderer processes the nodes.
- \li The QQuickWindow::afterRendering() signal is
- emitted. Applications can make direct connections
- (using Qt::DirectConnection) to this signal to use custom OpenGL calls
- which will then stack visually over the QML scene.
+ \li The renderer generates states and records draw calls for the graphics
+ API in use.
- \li The rendered frame is swapped and QQuickWindow::frameSwapped()
- is emitted.
+ \li The QQuickWindow::afterRendering() signal is emitted. Applications can
+ make direct connections (using Qt::DirectConnection) to this signal to
+ issue custom graphics API calls which will then stack visually over the QML
+ scene.
+
+ \li The frame is now ready. The buffers are swapped (OpenGL), or a present
+ command is recorded and the command buffers are submitted to a graphics
+ queue (Vulkan, Metal). QQuickWindow::frameSwapped() is emitted.
\endlist
@@ -269,37 +274,38 @@ animations, process events, etc.
\endlist
The threaded renderer is currently used by default on Windows with
-opengl32.dll, Linux with non-Mesa based drivers, mobile
-platforms, and Embedded Linux with EGLFS but this is subject to
-change. It is possible to force use of the threaded renderer by
-setting \c {QSG_RENDER_LOOP=threaded} in the environment.
+opengl32.dll, Linux excluding Mesa llvmpipe, \macos with Metal, mobile
+platforms, and Embedded Linux with EGLFS, and with Vulkan regardless of the
+platform, but this is subject to change. It is always possible to force use of
+the threaded renderer by setting \c {QSG_RENDER_LOOP=threaded} in the
+environment.
\section2 Non-threaded Render Loops ("basic" and "windows")
-The non-threaded render loop is currently used by default on Windows
-with ANGLE or a non-default opengl32 implementation, \macos, and Linux with
-Mesa drivers. For the latter this is mostly a precautionary measure,
-as not all combinations of OpenGL drivers and windowing systems have
-been tested. At the same time implementations like ANGLE or Mesa
-llvmpipe are not able to function properly with threaded rendering at
-all so not using threaded rendering is essential for these.
+The non-threaded render loop is currently used by default on Windows with ANGLE
+or a non-default opengl32 implementation, \macos with OpenGL, and Linux with
+some drivers. For the latter this is mostly a precautionary measure, as not all
+combinations of OpenGL drivers and windowing systems have been tested. At the
+same time implementations like ANGLE or Mesa llvmpipe are not able to function
+properly with threaded rendering at all so not using threaded rendering is
+essential for these.
-On macOS, the threaded render loop is not supported when building
-with XCode 10 (10.14 SDK) or later, since this opts in to layer-backed
-views on macOS 10.14. You can build with Xcode 9 (10.13 SDK) to opt
-out of layer-backing, in which case the threaded render loop is
-available and used by default.
+On macOS and OpenGL, the threaded render loop is not supported when building
+with XCode 10 (10.14 SDK) or later, since this opts in to layer-backed views on
+macOS 10.14. You can build with Xcode 9 (10.13 SDK) to opt out of
+layer-backing, in which case the threaded render loop is available and used by
+default. There is no such restriction with Metal.
-By default \c windows is used for non-threaded rendering on Windows
-with ANGLE, while \c basic is used for all other platforms when
-non-threaded rendering is needed.
+By default \c windows is used for non-threaded rendering on Windows with ANGLE,
+while \c basic is used for all other platforms when non-threaded rendering is
+needed.
-Even when using the non-threaded render loop, you should write your
-code as if you are using the threaded renderer, as failing to do so
-will make the code non-portable.
+Even when using the non-threaded render loop, you should write your code as if
+you are using the threaded renderer, as failing to do so will make the code
+non-portable.
-The following is a simplified illustration of the frame rendering
-sequence in the non-threaded renderer.
+The following is a simplified illustration of the frame rendering sequence in
+the non-threaded renderer.
\image sg-renderloop-singlethreaded.png
@@ -314,32 +320,50 @@ time. It is possible to implement either a threaded or non-threaded
behavior similar to the ones shown above.
-\section2 Mixing Scene Graph and OpenGL
+\section2 Mixing Scene Graph and the native graphics API
-The scene graph offers two methods for integrating OpenGL content:
-by calling OpenGL commands directly and by creating a textured node
-in the scene graph.
+The scene graph offers two methods for integrating application-provided
+graphics commands: by issuing OpenGL, Vulkan, Metal, etc. commands directly,
+and by creating a textured node in the scene graph.
By connecting to the \l QQuickWindow::beforeRendering() and \l
-QQuickWindow::afterRendering() signals, applications can make OpenGL
-calls directly into the same context as the scene graph is rendering
-to. As the signal names indicate, the user can then render OpenGL
-content either under a Qt Quick scene or over it. The benefit of
-integrating in this manner is that no extra framebuffer nor memory is
-needed to perform the rendering. The downside is that Qt Quick decides
-when to call the signals and this is the only time the OpenGL
-application is allowed to draw.
+QQuickWindow::afterRendering() signals, applications can make OpenGL calls
+directly into the same context as the scene graph is rendering to. With APIs
+like Vulkan or Metal, applications can query native objects, such as, the scene
+graph's command buffer, via QSGRendererInterface, and record commands to it as
+they see fit. As the signal names indicate, the user can then render content
+either under a Qt Quick scene or over it. The benefit of integrating in this
+manner is that no extra framebuffer nor memory is needed to perform the
+rendering, and a possibly expensive texturing step is eliminated. The downside
+is that Qt Quick decides when to call the signals and this is the only time the
+OpenGL application is allowed to draw.
The \l {Scene Graph - OpenGL Under QML} example gives an example on
-how to use these signals.
+how to use these signals using OpenGL.
+
+The \l {Scene Graph - Direct3D 11 Under QML} example gives an example on
+how to use these signals using Direct3D.
+
+The \l {Scene Graph - Metal Under QML} example gives an example on
+how to use these signals using Metal.
+
+The \l {Scene Graph - Vulkan Under QML} example gives an example on
+how to use these signals using Vulkan.
-The other alternative is to create a QQuickFramebufferObject, render
-into it, and let it be displayed in the scene graph as a texture.
-The \l {Scene Graph - Rendering FBOs} example shows how this can be
-done. It is also possible to combine multiple rendering contexts and
-multiple threads to create content to be displayed in the scene graph.
-The \l {Scene Graph - Rendering FBOs in a thread} examples show how
-this can be done.
+The other alternative, only available for OpenGL currently, is to create a
+QQuickFramebufferObject, render into it, and let it be displayed in the scene
+graph as a texture. The \l {Scene Graph - Rendering FBOs} example shows how
+this can be done. It is also possible to combine multiple rendering contexts
+and multiple threads to create content to be displayed in the scene graph. The
+\l {Scene Graph - Rendering FBOs in a thread} examples show how this can be
+done.
+
+Graphics APIs other than OpenGL can also follow this approach, even though
+QQuickFramebufferObject does not currently support them. Creating and rendering
+to a texture directly with the underlying API, followed by wrapping and using
+this resource in a Qt Quick scene in a custom QQuickItem, is demonstrated in
+the \l {Scene Graph - Metal Texture Import} example. That example uses Metal,
+the concepts however apply to all other graphics APIs as well.
\warning When mixing OpenGL content with scene graph rendering, it is
important the application does not leave the OpenGL context in a state
@@ -347,8 +371,8 @@ with buffers bound, attributes enabled, special values in the z-buffer
or stencil-buffer or similar. Doing so can result in unpredictable
behavior.
-\warning The OpenGL rendering code must be thread aware, as the
-rendering might be happening outside the GUI thread.
+\warning The custom rendering code must be thread aware in the sense that it
+should not assume being executed on the GUI (main) thread of the application.
\section2 Custom Items using QPainter
@@ -386,6 +410,15 @@ addition to being helpful to Qt contributors.
\endlist
+The legacy \c{QSG_INFO} environment variable is also available. Setting it to a
+non-zero value enables the \c{qt.scenegraph.general} category.
+
+\note When encountering graphics problems, or when in doubt which render loop
+or graphics API is in use, always start the application with at least
+\c{qt.scenegraph.general} and \c{qt.rhi.*} enabled, or \c{QSG_INFO=1} set. This
+will then print some essential information onto the debug output during
+initialization.
+
\section1 Scene Graph Backend
In addition to the public API, the scene graph has an adaptation layer
@@ -419,13 +452,12 @@ with multiple windows.
*/
/*!
- \title Qt Quick Scene Graph OpenGL Renderer
+ \title Qt Quick Scene Graph Default Renderer
\page qtquick-visualcanvas-scenegraph-renderer.html
- This document explains how the scene graph renderer for OpenGL
- works internally
+ This document explains how the default scene graph renderer works internally,
so that one can write code that uses it in an optimal fashion, both
- performance-wise and feature-wise.
+ performance and feature-wise.
One does not need to understand the internals of the renderer to get
good performance. However, it might help when integrating with the
@@ -442,8 +474,8 @@ with multiple windows.
platforms be processed and rendered in a separate thread. The
renderer is a self contained part of the scene graph which traverses
the QSGNode tree and uses geometry defined in QSGGeometryNode and
- shader state defined in QSGMaterial to schedule OpenGL state change
- and draw calls.
+ shader state defined in QSGMaterial to update the graphics state and
+ generate draw calls.
If needed, the renderer can be completely replaced using the
internal scene graph back-end API. This is mostly interesting for
@@ -457,11 +489,15 @@ with multiple windows.
\section1 Batching
- Where a traditional 2D API, such as QPainter, Cairo or Context2D, is
- written to handle thousands of individual draw calls per frame,
- OpenGL is a pure hardware API and performs best when the number of
- draw calls is very low and state changes are kept to a
- minimum. Consider the following use case:
+ Whereas a traditional 2D API, such as QPainter, Cairo or Context2D, is
+ written to handle thousands of individual draw calls per frame, OpenGL and
+ other hardware accelerated APIs perform best when the number of draw calls is
+ very low and state changes are kept to a minimum.
+
+ \note While \c OpenGL is used as an example in the following sections, the
+ same concepts apply to other graphics APIs as well.
+
+ Consider the following use case:
\image visualcanvas_list.png
@@ -934,4 +970,78 @@ with multiple windows.
\image visualize-overdraw-1.png "overdraw-1"
\image visualize-overdraw-2.png "overdraw-2"
\c QSG_VISUALIZE=overdraw
+
+ \section1 Rendering via the Qt Rendering Hardware Interface
+
+ From Qt 5.14 onwards, the default adaptation gains the option of rendering
+ via a graphics abstraction layer, the Qt Rendering Hardware Interface (RHI),
+ provided by the \l QtGui module. When enabled, no direct OpenGL calls are
+ made. Rather, the scene graph renders by using the APIs provided by the
+ abstraction layer, which is then translated into OpenGL, Vulkan, Metal, or
+ Direct 3D calls. Shader handling is also unified by writing shader code once,
+ compiling to \l{https://www.khronos.org/spir/}{SPIR-V}, and then translating
+ to the language appropriate for the various graphics APIs.
+
+ To enable this instead of directly using OpenGL, the following environment
+ variables can be used:
+
+ \table 100%
+ \header
+ \li Environment Variable
+ \li Possible Values
+ \li Description
+
+ \row
+ \li \c QSG_RHI
+ \li \c 1
+ \li Enables rendering via the RHI. The targeted graphics API is chosen based on
+ the platform, unless overridden by \c QSG_RHI_BACKEND. The defaults are currently
+ Direct3D 11 for Windows, Metal for macOS, OpenGL elsehwere.
+
+ \row
+ \li \c QSG_RHI_BACKEND
+ \li \c vulkan, \c metal, \c opengl, \c d3d11
+ \li Requests the specific RHI backend.
+
+ \row
+ \li \c QSG_INFO
+ \li \c 1
+ \li Like with the OpenGL-based rendering path, setting this enables printing system
+ information when initializing the Qt Quick scene graph. This can be very useful for
+ troubleshooting.
+
+ \row
+ \li \c QSG_RHI_DEBUG_LAYER
+ \li \c 1
+ \li Where applicable (Vulkan, Direct3D), enables the graphics API implementation's debug
+ and/or validation layers, if available.
+
+ \row
+ \li \c QSG_RHI_PREFER_SOFTWARE_RENDERER
+ \li \c 1
+ \li Requests choosing an adapter or physical device that uses software-based
+ rasterization. Applicable only when the underlying API has support for
+ enumerating adapters (for example, Direct3D or Vulkan), and is ignored
+ otherwise.
+
+ \endtable
+
+ Applications wishing to always run with a single given graphics API, can
+ request this via C++ as well. For example, the following call made early in
+ main(), before constructing any QQuickWindow, forces the use of Vulkan (and
+ will fail otherwise);
+
+ \badcode
+ QQuickWindow::setSceneGraphBackend(QSGRendererInterface::VulkanRhi);
+ \endcode
+
+ See QSGRendererInterface::GraphicsApi. The enum values ending in \c Rhi are
+ equivalent in effect to running with both \c QSG_RHI and \c QSG_RHI_BACKEND
+ set.
+
+ All QRhi backends will choose the system default GPU adapter or physical
+ device, unless overridden by \c{QSG_RHI_PREFER_SOFTWARE_RENDERER} or a
+ backend-specific variable, such as, \c{QT_D3D_ADAPTER_INDEX} or
+ \c{QT_VK_PHYSICAL_DEVICE_INDEX}. No further adapter configurability is
+ provided at this time.
*/
diff --git a/src/quick/doc/src/concepts/visualcanvas/topic.qdoc b/src/quick/doc/src/concepts/visualcanvas/topic.qdoc
index cb6b3564f2..677e5bd739 100644
--- a/src/quick/doc/src/concepts/visualcanvas/topic.qdoc
+++ b/src/quick/doc/src/concepts/visualcanvas/topic.qdoc
@@ -56,12 +56,15 @@ See the documentation about the \l{qtquick-visualcanvas-visualparent.html}
\section1 Scene Graph
Modern computer systems and devices use graphics processing units or GPUs to
-render graphics. Qt Quick can leverage this graphics hardware by using graphics
-APIs like OpenGL. The default graphics adaptation for Qt Quick requires OpenGL and
-it is used to display applications developed with Qt Quick in QML. In particular,
-Qt Quick defines a scene graph which is then rendered. See the documentation about the
-\l{qtquick-visualcanvas-scenegraph.html}{Scene Graph} for in-depth information about
-the concept of a scene graph and why it is beneficial, and about the scene graph
-adaptations provided by Qt Quick.
+render graphics. Qt Quick can leverage this graphics hardware by using graphics
+APIs like \l{https://www.khronos.org/opengl/}{OpenGL},
+\l{https://www.khronos.org/vulkan/}{Vulkan}, or
+\l{https://developer.apple.com/documentation/metal}{Metal}. The default
+graphics adaptation for Qt Quick requires OpenGL and it is used to display
+applications developed with Qt Quick in QML. In particular, Qt Quick defines a
+scene graph which is then rendered. See the documentation about the
+\l{qtquick-visualcanvas-scenegraph.html}{Scene Graph} for in-depth information
+about the concept of a scene graph and why it is beneficial, and about the
+scene graph adaptations provided by Qt Quick.
*/
diff --git a/src/quick/doc/src/examples.qdoc b/src/quick/doc/src/examples.qdoc
index 77475e9812..a913af47c1 100644
--- a/src/quick/doc/src/examples.qdoc
+++ b/src/quick/doc/src/examples.qdoc
@@ -171,9 +171,14 @@ Creator.
\div {class="doc-column"}
\b{Scene Graph}
\list
+ \li \l{Scene Graph - Custom Geometry}{Custom Geometry}
+ \li \l{Scene Graph - Metal Under QML}{Metal Under QML}
+ \li \l{Scene Graph - Metal Texture Import}{Metal Texture Import}
\li \l{Scene Graph - OpenGL Under QML}{OpenGL Under QML}
+ \li \l{Scene Graph - Direct3D 11 Under QML}{Direct3D 11 Under QML}
+ \li \l{Scene Graph - Vulkan Under QML}{Vulkan Under QML}
+ \li \l{Scene Graph - Custom Rendering with QSGRenderNode}{Render Node}
\li \l{Scene Graph - Painted Item}{Painted Item}
- \li \l{Scene Graph - Custom Geometry}{Custom Geometry}
\li \l{Scene Graph - Graph}{Graph}
\li \l{Scene Graph - Simple Material}{Simple Material}
\li \l{Scene Graph - Rendering FBOs}{Rendering FBOs}
diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri
index 226cca22cb..49975bd1ca 100644
--- a/src/quick/handlers/handlers.pri
+++ b/src/quick/handlers/handlers.pri
@@ -3,6 +3,7 @@ HEADERS += \
$$PWD/qquickhandlerpoint_p.h \
$$PWD/qquickhoverhandler_p.h \
$$PWD/qquickmultipointhandler_p.h \
+ $$PWD/qquickmultipointhandler_p_p.h \
$$PWD/qquickpinchhandler_p.h \
$$PWD/qquickpointerdevicehandler_p.h \
$$PWD/qquickpointerdevicehandler_p_p.h \
@@ -26,3 +27,9 @@ SOURCES += \
$$PWD/qquicksinglepointhandler.cpp \
$$PWD/qquicktaphandler.cpp \
$$PWD/qquickdragaxis.cpp
+
+qtConfig(wheelevent) {
+ HEADERS += $$PWD/qquickwheelhandler_p.h $$PWD/qquickwheelhandler_p_p.h
+ SOURCES += $$PWD/qquickwheelhandler.cpp
+}
+
diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp
index e5531369cf..61b66beff4 100644
--- a/src/quick/handlers/qquickdraghandler.cpp
+++ b/src/quick/handlers/qquickdraghandler.cpp
@@ -103,7 +103,7 @@ bool QQuickDragHandler::targetContainsCentroid()
QPointF QQuickDragHandler::targetCentroidPosition()
{
- QPointF pos = m_centroid.position();
+ QPointF pos = centroid().position();
if (target() != parentItem())
pos = parentItem()->mapToItem(target(), pos);
return pos;
@@ -114,8 +114,14 @@ void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEvent
QQuickMultiPointHandler::onGrabChanged(grabber, transition, point);
if (grabber == this && transition == QQuickEventPoint::GrabExclusive && target()) {
// In case the grab got handed over from another grabber, we might not get the Press.
- if (!m_pressedInsideTarget) {
- if (target() != parentItem())
+
+ auto isDescendant = [](QQuickItem *parent, QQuickItem *target) {
+ return (target != parent) && !target->isAncestorOf(parent);
+ };
+ if (m_snapMode == SnapAlways
+ || (m_snapMode == SnapIfPressedOutsideTarget && !m_pressedInsideTarget)
+ || (m_snapMode == SnapAuto && !m_pressedInsideTarget && isDescendant(parentItem(), target()))
+ ) {
m_pressTargetPos = QPointF(target()->width(), target()->height()) / 2;
} else if (m_pressTargetPos.isNull()) {
m_pressTargetPos = targetCentroidPosition();
@@ -123,6 +129,33 @@ void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEvent
}
}
+/*!
+ \qmlproperty enumeration QtQuick.DragHandler::snapMode
+
+ This property holds the snap mode.
+
+ The snap mode configures snapping of the \l target item's center to the event point.
+
+ Possible values:
+ \value DragHandler.SnapNever Never snap
+ \value DragHandler.SnapAuto The \l target snaps if the event point was pressed outside of the \l target
+ item \e and the \l target is a descendant of \l parentItem (default)
+ \value DragHandler.SnapWhenPressedOutsideTarget The \l target snaps if the event point was pressed outside of the \l target
+ \value DragHandler.SnapAlways Always snap
+*/
+QQuickDragHandler::SnapMode QQuickDragHandler::snapMode() const
+{
+ return m_snapMode;
+}
+
+void QQuickDragHandler::setSnapMode(QQuickDragHandler::SnapMode mode)
+{
+ if (mode == m_snapMode)
+ return;
+ m_snapMode = mode;
+ emit snapModeChanged();
+}
+
void QQuickDragHandler::onActiveChanged()
{
QQuickMultiPointHandler::onActiveChanged();
@@ -153,7 +186,7 @@ void QQuickDragHandler::handlePointerEventImpl(QQuickPointerEvent *event)
if (active()) {
// Calculate drag delta, taking into account the axis enabled constraint
// i.e. if xAxis is not enabled, then ignore the horizontal component of the actual movement
- QVector2D accumulatedDragDelta = QVector2D(m_centroid.scenePosition() - m_centroid.scenePressPosition());
+ QVector2D accumulatedDragDelta = QVector2D(centroid().scenePosition() - centroid().scenePressPosition());
if (!m_xAxis.enabled())
accumulatedDragDelta.setX(0);
if (!m_yAxis.enabled())
@@ -169,9 +202,9 @@ void QQuickDragHandler::handlePointerEventImpl(QQuickPointerEvent *event)
QVector <QQuickEventPoint *> chosenPoints;
if (event->isPressEvent())
- m_pressedInsideTarget = target() && m_currentPoints.count() > 0;
+ m_pressedInsideTarget = target() && currentPoints().count() > 0;
- for (const QQuickHandlerPoint &p : m_currentPoints) {
+ for (const QQuickHandlerPoint &p : currentPoints()) {
if (!allOverThreshold)
break;
QQuickEventPoint *point = event->pointById(p.id());
diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h
index 748026488a..e8f47163ed 100644
--- a/src/quick/handlers/qquickdraghandler_p.h
+++ b/src/quick/handlers/qquickdraghandler_p.h
@@ -62,8 +62,17 @@ class Q_QUICK_PRIVATE_EXPORT QQuickDragHandler : public QQuickMultiPointHandler
Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT)
Q_PROPERTY(QQuickDragAxis * yAxis READ yAxis CONSTANT)
Q_PROPERTY(QVector2D translation READ translation NOTIFY translationChanged)
+ Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged REVISION 14)
public:
+ enum SnapMode {
+ NoSnap = 0,
+ SnapAuto,
+ SnapIfPressedOutsideTarget,
+ SnapAlways
+ };
+ Q_ENUM(SnapMode)
+
explicit QQuickDragHandler(QQuickItem *parent = nullptr);
void handlePointerEventImpl(QQuickPointerEvent *event) override;
@@ -73,11 +82,14 @@ public:
QVector2D translation() const { return m_translation; }
void setTranslation(const QVector2D &trans);
+ QQuickDragHandler::SnapMode snapMode() const;
+ void setSnapMode(QQuickDragHandler::SnapMode mode);
void enforceConstraints();
Q_SIGNALS:
void translationChanged();
+ Q_REVISION(14) void snapModeChanged();
protected:
void onActiveChanged() override;
@@ -97,6 +109,7 @@ private:
QQuickDragAxis m_xAxis;
QQuickDragAxis m_yAxis;
+ QQuickDragHandler::SnapMode m_snapMode = SnapAuto;
bool m_pressedInsideTarget = false;
friend class QQuickDragAxis;
diff --git a/src/quick/handlers/qquickhoverhandler.cpp b/src/quick/handlers/qquickhoverhandler.cpp
index a6325e084b..79cb288af8 100644
--- a/src/quick/handlers/qquickhoverhandler.cpp
+++ b/src/quick/handlers/qquickhoverhandler.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -72,9 +72,6 @@ QQuickHoverHandler::QQuickHoverHandler(QQuickItem *parent)
{
// Tell QQuickPointerDeviceHandler::wantsPointerEvent() to ignore button state
d_func()->acceptedButtons = Qt::NoButton;
- // Rule out the touchscreen for now (can be overridden in QML in case a hover-detecting touchscreen exists)
- setAcceptedDevices(static_cast<QQuickPointerDevice::DeviceType>(
- static_cast<int>(QQuickPointerDevice::AllDevices) ^ static_cast<int>(QQuickPointerDevice::TouchScreen)));
}
QQuickHoverHandler::~QQuickHoverHandler()
@@ -103,7 +100,11 @@ bool QQuickHoverHandler::wantsPointerEvent(QQuickPointerEvent *event)
void QQuickHoverHandler::handleEventPoint(QQuickEventPoint *point)
{
- setHovered(true);
+ bool hovered = true;
+ if (point->state() == QQuickEventPoint::Released &&
+ point->pointerEvent()->device()->pointerType() == QQuickPointerDevice::Finger)
+ hovered = false;
+ setHovered(hovered);
setPassiveGrab(point);
}
diff --git a/src/quick/handlers/qquickmultipointhandler.cpp b/src/quick/handlers/qquickmultipointhandler.cpp
index 6fbab29077..f404788de4 100644
--- a/src/quick/handlers/qquickmultipointhandler.cpp
+++ b/src/quick/handlers/qquickmultipointhandler.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qquickmultipointhandler_p.h"
+#include "qquickmultipointhandler_p_p.h"
#include <private/qquickitem_p.h>
#include <QLineF>
#include <QMouseEvent>
@@ -59,14 +60,13 @@ QT_BEGIN_NAMESPACE
of multiple touchpoints.
*/
QQuickMultiPointHandler::QQuickMultiPointHandler(QQuickItem *parent, int minimumPointCount, int maximumPointCount)
- : QQuickPointerDeviceHandler(parent)
- , m_minimumPointCount(minimumPointCount)
- , m_maximumPointCount(maximumPointCount)
+ : QQuickPointerDeviceHandler(*(new QQuickMultiPointHandlerPrivate(minimumPointCount, maximumPointCount)), parent)
{
}
bool QQuickMultiPointHandler::wantsPointerEvent(QQuickPointerEvent *event)
{
+ Q_D(QQuickMultiPointHandler);
if (!QQuickPointerDeviceHandler::wantsPointerEvent(event))
return false;
@@ -84,14 +84,14 @@ bool QQuickMultiPointHandler::wantsPointerEvent(QQuickPointerEvent *event)
// handle a specific number of points, so a differing number of points will
// usually result in different behavior. But otherwise if the currentPoints
// are all still there in the event, we're good to go (do not reset
- // m_currentPoints, because we don't want to lose the pressPosition, and do
+ // currentPoints, because we don't want to lose the pressPosition, and do
// not want to reshuffle the order either).
const QVector<QQuickEventPoint *> candidatePoints = eligiblePoints(event);
- if (candidatePoints.count() != m_currentPoints.count()) {
- m_currentPoints.clear();
+ if (candidatePoints.count() != d->currentPoints.count()) {
+ d->currentPoints.clear();
if (active()) {
setActive(false);
- m_centroid.reset();
+ d->centroid.reset();
emit centroidChanged();
}
} else if (hasCurrentPoints(event)) {
@@ -101,57 +101,60 @@ bool QQuickMultiPointHandler::wantsPointerEvent(QQuickPointerEvent *event)
ret = ret || (candidatePoints.size() >= minimumPointCount() && candidatePoints.size() <= maximumPointCount());
if (ret) {
const int c = candidatePoints.count();
- m_currentPoints.resize(c);
+ d->currentPoints.resize(c);
for (int i = 0; i < c; ++i) {
- m_currentPoints[i].reset(candidatePoints[i]);
- m_currentPoints[i].localize(parentItem());
+ d->currentPoints[i].reset(candidatePoints[i]);
+ d->currentPoints[i].localize(parentItem());
}
} else {
- m_currentPoints.clear();
+ d->currentPoints.clear();
}
return ret;
}
void QQuickMultiPointHandler::handlePointerEventImpl(QQuickPointerEvent *event)
{
+ Q_D(QQuickMultiPointHandler);
QQuickPointerHandler::handlePointerEventImpl(event);
- // event's points can be reordered since the previous event, which is why m_currentPoints
+ // event's points can be reordered since the previous event, which is why currentPoints
// is _not_ a shallow copy of the QQuickPointerTouchEvent::m_touchPoints vector.
- // So we have to update our m_currentPoints instances based on the given event.
- for (QQuickHandlerPoint &p : m_currentPoints) {
+ // So we have to update our currentPoints instances based on the given event.
+ for (QQuickHandlerPoint &p : d->currentPoints) {
const QQuickEventPoint *ep = event->pointById(p.id());
if (ep)
p.reset(ep);
}
- QPointF sceneGrabPos = m_centroid.sceneGrabPosition();
- m_centroid.reset(m_currentPoints);
- m_centroid.m_sceneGrabPosition = sceneGrabPos; // preserve as it was
+ QPointF sceneGrabPos = d->centroid.sceneGrabPosition();
+ d->centroid.reset(d->currentPoints);
+ d->centroid.m_sceneGrabPosition = sceneGrabPos; // preserve as it was
emit centroidChanged();
}
void QQuickMultiPointHandler::onActiveChanged()
{
+ Q_D(QQuickMultiPointHandler);
if (active()) {
- m_centroid.m_sceneGrabPosition = m_centroid.m_scenePosition;
+ d->centroid.m_sceneGrabPosition = d->centroid.m_scenePosition;
} else {
- // Don't call m_centroid.reset() here, because in a QML onActiveChanged
+ // Don't call centroid.reset() here, because in a QML onActiveChanged
// callback, we'd like to see what the position _was_, what the velocity _was_, etc.
// (having them undefined is not useful)
// But pressedButtons and pressedModifiers are meant to be more real-time than those
// (which seems a bit inconsistent, from one side).
- m_centroid.m_pressedButtons = Qt::NoButton;
- m_centroid.m_pressedModifiers = Qt::NoModifier;
+ d->centroid.m_pressedButtons = Qt::NoButton;
+ d->centroid.m_pressedModifiers = Qt::NoModifier;
}
}
void QQuickMultiPointHandler::onGrabChanged(QQuickPointerHandler *, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *)
{
+ Q_D(QQuickMultiPointHandler);
// If another handler or item takes over this set of points, assume it has
// decided that it's the better fit for them. Don't immediately re-grab
// at the next opportunity. This should help to avoid grab cycles
// (e.g. between DragHandler and PinchHandler).
if (transition == QQuickEventPoint::UngrabExclusive || transition == QQuickEventPoint::CancelGrabExclusive)
- m_currentPoints.clear();
+ d->currentPoints.clear();
}
QVector<QQuickEventPoint *> QQuickMultiPointHandler::eligiblePoints(QQuickPointerEvent *event)
@@ -191,14 +194,21 @@ QVector<QQuickEventPoint *> QQuickMultiPointHandler::eligiblePoints(QQuickPointe
The default value is 2.
*/
+int QQuickMultiPointHandler::minimumPointCount() const
+{
+ Q_D(const QQuickMultiPointHandler);
+ return d->minimumPointCount;
+}
+
void QQuickMultiPointHandler::setMinimumPointCount(int c)
{
- if (m_minimumPointCount == c)
+ Q_D(QQuickMultiPointHandler);
+ if (d->minimumPointCount == c)
return;
- m_minimumPointCount = c;
+ d->minimumPointCount = c;
emit minimumPointCountChanged();
- if (m_maximumPointCount < 0)
+ if (d->maximumPointCount < 0)
emit maximumPointCountChanged();
}
@@ -217,22 +227,61 @@ void QQuickMultiPointHandler::setMinimumPointCount(int c)
The default value is the same as \l minimumPointCount.
*/
+int QQuickMultiPointHandler::maximumPointCount() const
+{
+ Q_D(const QQuickMultiPointHandler);
+ return d->maximumPointCount >= 0 ? d->maximumPointCount : d->minimumPointCount;
+}
+
void QQuickMultiPointHandler::setMaximumPointCount(int maximumPointCount)
{
- if (m_maximumPointCount == maximumPointCount)
+ Q_D(QQuickMultiPointHandler);
+ if (d->maximumPointCount == maximumPointCount)
return;
- m_maximumPointCount = maximumPointCount;
+ d->maximumPointCount = maximumPointCount;
emit maximumPointCountChanged();
}
+/*!
+ \readonly
+ \qmlproperty QtQuick::HandlerPoint QtQuick::MultiPointHandler::centroid
+
+ A point exactly in the middle of the currently-pressed touch points.
+ If only one point is pressed, it's the same as that point.
+ A handler that has a \l target will normally transform it relative to this point.
+*/
+const QQuickHandlerPoint &QQuickMultiPointHandler::centroid() const
+{
+ Q_D(const QQuickMultiPointHandler);
+ return d->centroid;
+}
+
+/*!
+ Returns a modifiable reference to the point that will be returned by the
+ \l centroid property. If you modify it, you are responsible to emit
+ \l centroidChanged.
+*/
+QQuickHandlerPoint &QQuickMultiPointHandler::mutableCentroid()
+{
+ Q_D(QQuickMultiPointHandler);
+ return d->centroid;
+}
+
+QVector<QQuickHandlerPoint> &QQuickMultiPointHandler::currentPoints()
+{
+ Q_D(QQuickMultiPointHandler);
+ return d->currentPoints;
+}
+
bool QQuickMultiPointHandler::hasCurrentPoints(QQuickPointerEvent *event)
{
- if (event->pointCount() < m_currentPoints.size() || m_currentPoints.size() == 0)
+ Q_D(const QQuickMultiPointHandler);
+ if (event->pointCount() < d->currentPoints.size() || d->currentPoints.size() == 0)
return false;
// TODO optimize: either ensure the points are sorted,
// or use std::equal with a predicate
- for (const QQuickHandlerPoint &p : qAsConst(m_currentPoints)) {
+ for (const QQuickHandlerPoint &p : qAsConst(d->currentPoints)) {
const QQuickEventPoint *ep = event->pointById(p.id());
if (!ep)
return false;
@@ -244,30 +293,33 @@ bool QQuickMultiPointHandler::hasCurrentPoints(QQuickPointerEvent *event)
qreal QQuickMultiPointHandler::averageTouchPointDistance(const QPointF &ref)
{
+ Q_D(const QQuickMultiPointHandler);
qreal ret = 0;
- if (Q_UNLIKELY(m_currentPoints.size() == 0))
+ if (Q_UNLIKELY(d->currentPoints.size() == 0))
return ret;
- for (const QQuickHandlerPoint &p : m_currentPoints)
+ for (const QQuickHandlerPoint &p : d->currentPoints)
ret += QVector2D(p.scenePosition() - ref).length();
- return ret / m_currentPoints.size();
+ return ret / d->currentPoints.size();
}
qreal QQuickMultiPointHandler::averageStartingDistance(const QPointF &ref)
{
+ Q_D(const QQuickMultiPointHandler);
// TODO cache it in setActive()?
qreal ret = 0;
- if (Q_UNLIKELY(m_currentPoints.size() == 0))
+ if (Q_UNLIKELY(d->currentPoints.size() == 0))
return ret;
- for (const QQuickHandlerPoint &p : m_currentPoints)
+ for (const QQuickHandlerPoint &p : d->currentPoints)
ret += QVector2D(p.sceneGrabPosition() - ref).length();
- return ret / m_currentPoints.size();
+ return ret / d->currentPoints.size();
}
QVector<QQuickMultiPointHandler::PointData> QQuickMultiPointHandler::angles(const QPointF &ref) const
{
+ Q_D(const QQuickMultiPointHandler);
QVector<PointData> angles;
- angles.reserve(m_currentPoints.count());
- for (const QQuickHandlerPoint &p : m_currentPoints) {
+ angles.reserve(d->currentPoints.count());
+ for (const QQuickHandlerPoint &p : d->currentPoints) {
qreal angle = QLineF(ref, p.scenePosition()).angle();
angles.append(PointData(p.id(), -angle)); // convert to clockwise, to be consistent with QQuickItem::rotation
}
@@ -312,7 +364,7 @@ void QQuickMultiPointHandler::acceptPoints(const QVector<QQuickEventPoint *> &po
point->setAccepted();
}
-bool QQuickMultiPointHandler::grabPoints(QVector<QQuickEventPoint *> points)
+bool QQuickMultiPointHandler::grabPoints(const QVector<QQuickEventPoint *> &points)
{
if (points.isEmpty())
return false;
@@ -332,17 +384,41 @@ bool QQuickMultiPointHandler::grabPoints(QVector<QQuickEventPoint *> points)
void QQuickMultiPointHandler::moveTarget(QPointF pos)
{
- target()->setPosition(pos);
- m_centroid.m_position = target()->mapFromScene(m_centroid.m_scenePosition);
+ Q_D(QQuickMultiPointHandler);
+ if (QQuickItem *t = target()) {
+ d->xMetaProperty().write(t, pos.x());
+ d->yMetaProperty().write(t, pos.y());
+ d->centroid.m_position = t->mapFromScene(d->centroid.m_scenePosition);
+ } else {
+ qWarning() << "moveTarget: target is null";
+ }
}
-/*!
- \readonly
- \qmlproperty QtQuick::HandlerPoint QtQuick::MultiPointHandler::centroid
+QQuickMultiPointHandlerPrivate::QQuickMultiPointHandlerPrivate(int minPointCount, int maxPointCount)
+ : QQuickPointerDeviceHandlerPrivate()
+ , minimumPointCount(minPointCount)
+ , maximumPointCount(maxPointCount)
+{
+}
- A point exactly in the middle of the currently-pressed touch points.
- If only one point is pressed, it's the same as that point.
- A handler that has a \l target will normally transform it relative to this point.
-*/
+QMetaProperty &QQuickMultiPointHandlerPrivate::xMetaProperty() const
+{
+ Q_Q(const QQuickMultiPointHandler);
+ if (!xProperty.isValid() && q->target()) {
+ const QMetaObject *targetMeta = q->target()->metaObject();
+ xProperty = targetMeta->property(targetMeta->indexOfProperty("x"));
+ }
+ return xProperty;
+}
+
+QMetaProperty &QQuickMultiPointHandlerPrivate::yMetaProperty() const
+{
+ Q_Q(const QQuickMultiPointHandler);
+ if (!yProperty.isValid() && q->target()) {
+ const QMetaObject *targetMeta = q->target()->metaObject();
+ yProperty = targetMeta->property(targetMeta->indexOfProperty("y"));
+ }
+ return yProperty;
+}
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickmultipointhandler_p.h b/src/quick/handlers/qquickmultipointhandler_p.h
index 06f170154b..480f69035b 100644
--- a/src/quick/handlers/qquickmultipointhandler_p.h
+++ b/src/quick/handlers/qquickmultipointhandler_p.h
@@ -58,6 +58,8 @@
QT_BEGIN_NAMESPACE
+class QQuickMultiPointHandlerPrivate;
+
class Q_QUICK_PRIVATE_EXPORT QQuickMultiPointHandler : public QQuickPointerDeviceHandler
{
Q_OBJECT
@@ -68,13 +70,13 @@ class Q_QUICK_PRIVATE_EXPORT QQuickMultiPointHandler : public QQuickPointerDevic
public:
explicit QQuickMultiPointHandler(QQuickItem *parent = nullptr, int minimumPointCount = 2, int maximumPointCount = -1);
- int minimumPointCount() const { return m_minimumPointCount; }
+ int minimumPointCount() const;
void setMinimumPointCount(int c);
- int maximumPointCount() const { return m_maximumPointCount >= 0 ? m_maximumPointCount : m_minimumPointCount; }
+ int maximumPointCount() const;
void setMaximumPointCount(int maximumPointCount);
- QQuickHandlerPoint centroid() const { return m_centroid; }
+ const QQuickHandlerPoint &centroid() const;
signals:
void minimumPointCountChanged();
@@ -94,6 +96,8 @@ protected:
void handlePointerEventImpl(QQuickPointerEvent *event) override;
void onActiveChanged() override;
void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point) override;
+ QVector<QQuickHandlerPoint> &currentPoints();
+ QQuickHandlerPoint &mutableCentroid();
bool hasCurrentPoints(QQuickPointerEvent *event);
QVector<QQuickEventPoint *> eligiblePoints(QQuickPointerEvent *event);
qreal averageTouchPointDistance(const QPointF &ref);
@@ -103,14 +107,10 @@ protected:
QVector<PointData> angles(const QPointF &ref) const;
static qreal averageAngleDelta(const QVector<PointData> &old, const QVector<PointData> &newAngles);
void acceptPoints(const QVector<QQuickEventPoint *> &points);
- bool grabPoints(QVector<QQuickEventPoint *> points);
+ bool grabPoints(const QVector<QQuickEventPoint *> &points);
void moveTarget(QPointF pos);
-protected:
- QVector<QQuickHandlerPoint> m_currentPoints;
- QQuickHandlerPoint m_centroid;
- int m_minimumPointCount;
- int m_maximumPointCount;
+ Q_DECLARE_PRIVATE(QQuickMultiPointHandler)
};
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickmultipointhandler_p_p.h b/src/quick/handlers/qquickmultipointhandler_p_p.h
new file mode 100644
index 0000000000..406e4a1fdf
--- /dev/null
+++ b/src/quick/handlers/qquickmultipointhandler_p_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 QQUICKPOINTERMULTIHANDLER_P_H
+#define QQUICKPOINTERMULTIHANDLER_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 "qquickhandlerpoint_p.h"
+#include "qquickpointerdevicehandler_p_p.h"
+#include "qquickmultipointhandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QQuickMultiPointHandlerPrivate : public QQuickPointerDeviceHandlerPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickMultiPointHandler)
+
+public:
+ static QQuickMultiPointHandlerPrivate* get(QQuickMultiPointHandler *q) { return q->d_func(); }
+ static const QQuickMultiPointHandlerPrivate* get(const QQuickMultiPointHandler *q) { return q->d_func(); }
+
+ QQuickMultiPointHandlerPrivate(int minPointCount, int maxPointCount);
+
+ QMetaProperty &xMetaProperty() const;
+ QMetaProperty &yMetaProperty() const;
+
+ QVector<QQuickHandlerPoint> currentPoints;
+ QQuickHandlerPoint centroid;
+ int minimumPointCount;
+ int maximumPointCount;
+ mutable QMetaProperty xProperty;
+ mutable QMetaProperty yProperty;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPOINTERMULTIHANDLER_P_H
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp
index dc1a9a92f9..a5a867015c 100644
--- a/src/quick/handlers/qquickpinchhandler.cpp
+++ b/src/quick/handlers/qquickpinchhandler.cpp
@@ -267,20 +267,14 @@ void QQuickPinchHandler::onActiveChanged()
{
QQuickMultiPointHandler::onActiveChanged();
if (active()) {
- m_startMatrix = QMatrix4x4();
- m_startAngles = angles(m_centroid.sceneGrabPosition());
- m_startDistance = averageTouchPointDistance(m_centroid.sceneGrabPosition());
+ m_startAngles = angles(centroid().sceneGrabPosition());
+ m_startDistance = averageTouchPointDistance(centroid().sceneGrabPosition());
m_activeRotation = 0;
m_activeTranslation = QVector2D();
if (const QQuickItem *t = target()) {
m_startScale = t->scale(); // TODO incompatible with independent x/y scaling
m_startRotation = t->rotation();
- QVector3D xformOrigin(t->transformOriginPoint());
- m_startMatrix.translate(float(t->x()), float(t->y()));
- m_startMatrix.translate(xformOrigin);
- m_startMatrix.scale(float(m_startScale));
- m_startMatrix.rotate(float(m_startRotation), 0, 0, -1);
- m_startMatrix.translate(-xformOrigin);
+ m_startPos = t->position();
} else {
m_startScale = m_accumulatedScale;
m_startRotation = 0;
@@ -294,7 +288,7 @@ void QQuickPinchHandler::onActiveChanged()
void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
{
if (Q_UNLIKELY(lcPinchHandler().isDebugEnabled())) {
- for (const QQuickHandlerPoint &p : m_currentPoints)
+ for (const QQuickHandlerPoint &p : currentPoints())
qCDebug(lcPinchHandler) << hex << p.id() << p.sceneGrabPosition() << "->" << p.scenePosition();
}
QQuickMultiPointHandler::handlePointerEventImpl(event);
@@ -302,13 +296,13 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
qreal dist = 0;
#if QT_CONFIG(gestures)
if (const auto gesture = event->asPointerNativeGestureEvent()) {
- m_centroid.reset(event->point(0));
+ mutableCentroid().reset(event->point(0));
switch (gesture->type()) {
case Qt::EndNativeGesture:
m_activeScale = 1;
m_activeRotation = 0;
m_activeTranslation = QVector2D();
- m_centroid.reset();
+ mutableCentroid().reset();
setActive(false);
emit updated();
return;
@@ -333,7 +327,7 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
{
const bool containsReleasedPoints = event->isReleaseEvent();
QVector<QQuickEventPoint *> chosenPoints;
- for (const QQuickHandlerPoint &p : m_currentPoints) {
+ for (const QQuickHandlerPoint &p : currentPoints()) {
QQuickEventPoint *ep = event->pointById(p.id());
chosenPoints << ep;
}
@@ -341,8 +335,8 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
// Verify that at least one of the points has moved beyond threshold needed to activate the handler
int numberOfPointsDraggedOverThreshold = 0;
QVector2D accumulatedDrag;
- const QVector2D currentCentroid(m_centroid.scenePosition());
- const QVector2D pressCentroid(m_centroid.scenePressPosition());
+ const QVector2D currentCentroid(centroid().scenePosition());
+ const QVector2D pressCentroid(centroid().scenePressPosition());
QStyleHints *styleHints = QGuiApplication::styleHints();
const int dragThreshold = styleHints->startDragDistance();
@@ -410,9 +404,9 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
}
const bool requiredNumberOfPointsDraggedOverThreshold = numberOfPointsDraggedOverThreshold >= minimumPointCount() && numberOfPointsDraggedOverThreshold <= maximumPointCount();
- accumulatedMovementMagnitude /= m_currentPoints.count();
+ accumulatedMovementMagnitude /= currentPoints().count();
- QVector2D avgDrag = accumulatedDrag / m_currentPoints.count();
+ QVector2D avgDrag = accumulatedDrag / currentPoints().count();
if (!xAxis()->enabled())
avgDrag.setX(0);
if (!yAxis()->enabled())
@@ -445,12 +439,12 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
// avoid mapping the minima and maxima, as they might have unmappable values
// such as -inf/+inf. Because of this we perform the bounding to min/max in local coords.
// 1. scale
- dist = averageTouchPointDistance(m_centroid.scenePosition());
+ dist = averageTouchPointDistance(centroid().scenePosition());
m_activeScale = dist / m_startDistance;
m_activeScale = qBound(m_minimumScale/m_startScale, m_activeScale, m_maximumScale/m_startScale);
// 2. rotate
- QVector<PointData> newAngles = angles(m_centroid.scenePosition());
+ QVector<PointData> newAngles = angles(centroid().scenePosition());
const qreal angleDelta = averageAngleDelta(m_startAngles, newAngles);
m_activeRotation += angleDelta;
m_startAngles = std::move(newAngles);
@@ -465,25 +459,15 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
m_accumulatedScale = m_startScale * m_activeScale;
if (target() && target()->parentItem()) {
- const QPointF centroidParentPos = target()->parentItem()->mapFromScene(m_centroid.scenePosition());
+ const QPointF centroidParentPos = target()->parentItem()->mapFromScene(centroid().scenePosition());
// 3. Drag/translate
- const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(m_centroid.sceneGrabPosition());
+ const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(centroid().sceneGrabPosition());
m_activeTranslation = QVector2D(centroidParentPos - centroidStartParentPos);
// apply rotation + scaling around the centroid - then apply translation.
- QMatrix4x4 mat;
-
- const QVector3D centroidParentVector(centroidParentPos);
- mat.translate(centroidParentVector);
- mat.rotate(float(m_activeRotation), 0, 0, 1);
- mat.scale(float(m_activeScale));
- mat.translate(-centroidParentVector);
- mat.translate(QVector3D(m_activeTranslation));
-
- mat = mat * m_startMatrix;
-
- QPointF xformOriginPoint = target()->transformOriginPoint();
- QPointF pos = mat * xformOriginPoint;
- pos -= xformOriginPoint;
+ QPointF pos = QQuickItemPrivate::get(target())->adjustedPosForTransform(centroidParentPos,
+ m_startPos, m_activeTranslation,
+ m_startScale, m_activeScale,
+ m_startRotation, m_activeRotation);
if (xAxis()->enabled())
pos.setX(qBound(xAxis()->minimum(), pos.x(), xAxis()->maximum()));
@@ -498,10 +482,10 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
target()->setRotation(rotation);
target()->setScale(m_accumulatedScale);
} else {
- m_activeTranslation = QVector2D(m_centroid.scenePosition() - m_centroid.scenePressPosition());
+ m_activeTranslation = QVector2D(centroid().scenePosition() - centroid().scenePressPosition());
}
- qCDebug(lcPinchHandler) << "centroid" << m_centroid.scenePressPosition() << "->" << m_centroid.scenePosition()
+ qCDebug(lcPinchHandler) << "centroid" << centroid().scenePressPosition() << "->" << centroid().scenePosition()
<< ", distance" << m_startDistance << "->" << dist
<< ", startScale" << m_startScale << "->" << m_accumulatedScale
<< ", activeRotation" << m_activeRotation
diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h
index 766d57f892..cea794f0c8 100644
--- a/src/quick/handlers/qquickpinchhandler_p.h
+++ b/src/quick/handlers/qquickpinchhandler_p.h
@@ -158,7 +158,6 @@ private:
qreal m_accumulatedStartCentroidDistance = 0;
QVector<PointData> m_startAngles;
QQuickMatrix4x4 m_transform;
- QMatrix4x4 m_startMatrix;
};
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpointerdevicehandler.cpp b/src/quick/handlers/qquickpointerdevicehandler.cpp
index 2964042f39..90f31bf9fd 100644
--- a/src/quick/handlers/qquickpointerdevicehandler.cpp
+++ b/src/quick/handlers/qquickpointerdevicehandler.cpp
@@ -307,7 +307,8 @@ bool QQuickPointerDeviceHandler::wantsPointerEvent(QQuickPointerEvent *event)
return false;
// HoverHandler sets acceptedButtons to Qt::NoButton to indicate that button state is irrelevant.
if (event->device()->pointerType() != QQuickPointerDevice::Finger && acceptedButtons() != Qt::NoButton &&
- (event->buttons() & acceptedButtons()) == 0 && (event->button() & acceptedButtons()) == 0)
+ (event->buttons() & acceptedButtons()) == 0 && (event->button() & acceptedButtons()) == 0
+ && !event->asPointerScrollEvent())
return false;
return true;
}
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp
index 3b42c5e536..7b26adbe6f 100644
--- a/src/quick/handlers/qquicktaphandler.cpp
+++ b/src/quick/handlers/qquicktaphandler.cpp
@@ -78,8 +78,8 @@ int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1);
\l gesturePolicy to \c TapHandler.ReleaseWithinBounds.
For multi-tap gestures (double-tap, triple-tap etc.), the distance moved
- must not exceed QPlatformTheme::MouseDoubleClickDistance with mouse and
- QPlatformTheme::TouchDoubleTapDistance with touch, and the time between
+ must not exceed QStyleHints::mouseDoubleClickDistance() with mouse and
+ QStyleHints::touchDoubleTapDistance() with touch, and the time between
taps must not exceed QStyleHints::mouseDoubleClickInterval().
\sa MouseArea
@@ -90,11 +90,9 @@ QQuickTapHandler::QQuickTapHandler(QQuickItem *parent)
{
if (m_mouseMultiClickDistanceSquared < 0) {
m_multiTapInterval = qApp->styleHints()->mouseDoubleClickInterval() / 1000.0;
- m_mouseMultiClickDistanceSquared = QGuiApplicationPrivate::platformTheme()->
- themeHint(QPlatformTheme::MouseDoubleClickDistance).toInt();
+ m_mouseMultiClickDistanceSquared = qApp->styleHints()->mouseDoubleClickDistance();
m_mouseMultiClickDistanceSquared *= m_mouseMultiClickDistanceSquared;
- m_touchMultiTapDistanceSquared = QGuiApplicationPrivate::platformTheme()->
- themeHint(QPlatformTheme::TouchDoubleTapDistance).toInt();
+ m_touchMultiTapDistanceSquared = qApp->styleHints()->touchDoubleTapDistance();
m_touchMultiTapDistanceSquared *= m_touchMultiTapDistanceSquared;
}
}
@@ -410,9 +408,9 @@ void QQuickTapHandler::updateTimeHeld()
\since 5.11
This signal is emitted when the \c parent Item is tapped twice within a
- short span of time (QStyleHints::mouseDoubleClickInterval) and distance
- (QPlatformTheme::MouseDoubleClickDistance or
- QPlatformTheme::TouchDoubleTapDistance). This signal always occurs after
+ short span of time (QStyleHints::mouseDoubleClickInterval()) and distance
+ (QStyleHints::mouseDoubleClickDistance() or
+ QStyleHints::touchDoubleTapDistance()). This signal always occurs after
\l singleTapped, \l tapped, and \l tapCountChanged. The \a eventPoint
signal parameter contains information from the release event about the
point that was tapped.
diff --git a/src/quick/handlers/qquickwheelhandler.cpp b/src/quick/handlers/qquickwheelhandler.cpp
new file mode 100644
index 0000000000..90e4fef97e
--- /dev/null
+++ b/src/quick/handlers/qquickwheelhandler.cpp
@@ -0,0 +1,520 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "qquickwheelhandler_p.h"
+#include "qquickwheelhandler_p_p.h"
+#include <QLoggingCategory>
+#include <QtMath>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcWheelHandler, "qt.quick.handler.wheel")
+
+/*!
+ \qmltype WheelHandler
+ \instantiates QQuickWheelHandler
+ \inqmlmodule QtQuick
+ \ingroup qtquick-input-handlers
+ \brief Handler for the mouse wheel.
+
+ WheelHandler is a handler that is used to interactively manipulate some
+ numeric property of an Item as the user rotates the mouse wheel. Like other
+ Input Handlers, by default it manipulates its \l {PointerHandler::target}
+ {target}. Declare \l property to control which target property will be
+ manipulated:
+
+ \snippet pointerHandlers/wheelHandler.qml 0
+
+ \l BoundaryRule is quite useful in combination with WheelHandler (as well
+ as with other Input Handlers) to declare the allowed range of values that
+ the target property can have. For example it is possible to implement
+ scrolling using a combination of WheelHandler and \l DragHandler to
+ manipulate the scrollable Item's \l{QQuickItem::y}{y} property when the
+ user rotates the wheel or drags the item on a touchscreen, and
+ \l BoundaryRule to limit the range of motion from the top to the bottom:
+
+ \snippet pointerHandlers/handlerFlick.qml 0
+
+ Alternatively if \l targetProperty is not set or \l target is null,
+ WheelHandler will not automatically manipulate anything; but the
+ \l rotation property can be used in a binding to manipulate another
+ property, or you can implement \c onWheel and handle the wheel event
+ directly.
+
+ WheelHandler handles only a rotating mouse wheel by default.
+ Optionally it can handle smooth-scrolling events from touchpad gestures,
+ by setting \l acceptedDevices to \c{PointerDevice.Mouse | PointerDevice.TouchPad}.
+
+ \note Some non-mouse hardware (such as a touch-sensitive Wacom tablet, or
+ a Linux laptop touchpad) generates real wheel events from gestures.
+ WheelHandler will respond to those events as wheel events regardless of the
+ setting of the \l acceptedDevices property.
+
+ \sa MouseArea
+ \sa Flickable
+*/
+
+QQuickWheelHandler::QQuickWheelHandler(QQuickItem *parent)
+ : QQuickSinglePointHandler(*(new QQuickWheelHandlerPrivate), parent)
+{
+ setAcceptedDevices(QQuickPointerDevice::Mouse);
+}
+
+/*!
+ \qmlproperty enum QtQuick::WheelHandler::orientation
+
+ Which wheel to react to. The default is \c Qt.Vertical.
+
+ Not every mouse has a \c Horizontal wheel; sometimes it is emulated by
+ tilting the wheel sideways. A touchpad can usually generate both vertical
+ and horizontal wheel events.
+*/
+Qt::Orientation QQuickWheelHandler::orientation() const
+{
+ Q_D(const QQuickWheelHandler);
+ return d->orientation;
+}
+
+void QQuickWheelHandler::setOrientation(Qt::Orientation orientation)
+{
+ Q_D(QQuickWheelHandler);
+ if (d->orientation == orientation)
+ return;
+
+ d->orientation = orientation;
+ emit orientationChanged();
+}
+
+/*!
+ \qmlproperty bool QtQuick::WheelHandler::invertible
+
+ Whether or not to reverse the direction of property change if
+ \l QQuickPointerScrollEvent::inverted is true. The default is \c true.
+
+ If the operating system has a "natural scrolling" setting that causes
+ scrolling to be in the same direction as the finger movement, then if this
+ property is set to \c true, and WheelHandler is directly setting a property
+ on \l target, the direction of movement will correspond to the system setting.
+ If this property is set to \l false, it will invert the \l rotation so that
+ the direction of motion is always the same as the direction of finger movement.
+*/
+bool QQuickWheelHandler::isInvertible() const
+{
+ Q_D(const QQuickWheelHandler);
+ return d->invertible;
+}
+
+void QQuickWheelHandler::setInvertible(bool invertible)
+{
+ Q_D(QQuickWheelHandler);
+ if (d->invertible == invertible)
+ return;
+
+ d->invertible = invertible;
+ emit invertibleChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick::WheelHandler::activeTimeout
+
+ The amount of time in seconds after which the \l active property will
+ revert to \c false if no more wheel events are received. The default is
+ \c 0.1 (100 ms).
+
+ When WheelHandler handles events that contain
+ \l {Qt::ScrollPhase}{scroll phase} information, such as events from some
+ touchpads, the \l active property will become \c false as soon as an event
+ with phase \l Qt::ScrollEnd is received; in that case the timeout is not
+ necessary. But a conventional mouse with a wheel does not provide the
+ \l {QQuickPointerScrollEvent::phase}{scroll phase}: the mouse cannot detect
+ when the user has decided to stop scrolling, so the \l active property
+ transitions to \c false after this much time has elapsed.
+*/
+qreal QQuickWheelHandler::activeTimeout() const
+{
+ Q_D(const QQuickWheelHandler);
+ return d->activeTimeout;
+}
+
+void QQuickWheelHandler::setActiveTimeout(qreal timeout)
+{
+ Q_D(QQuickWheelHandler);
+ if (qFuzzyCompare(d->activeTimeout, timeout))
+ return;
+
+ if (timeout < 0) {
+ qWarning("activeTimeout must be positive");
+ return;
+ }
+
+ d->activeTimeout = timeout;
+ emit activeTimeoutChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick::WheelHandler::rotation
+
+ The angle through which the mouse wheel has been rotated since the last
+ time this property was set, in wheel degrees.
+
+ A positive value indicates that the wheel was rotated up/right;
+ a negative value indicates that the wheel was rotated down/left.
+
+ A basic mouse click-wheel works in steps of 15 degrees.
+
+ The default is \c 0 at startup. It can be programmatically set to any value
+ at any time. The value will be adjusted from there as the user rotates the
+ mouse wheel.
+
+ \sa orientation
+*/
+qreal QQuickWheelHandler::rotation() const
+{
+ Q_D(const QQuickWheelHandler);
+ return d->rotation * d->rotationScale;
+}
+
+void QQuickWheelHandler::setRotation(qreal rotation)
+{
+ Q_D(QQuickWheelHandler);
+ if (qFuzzyCompare(d->rotation, rotation / d->rotationScale))
+ return;
+
+ d->rotation = rotation / d->rotationScale;
+ emit rotationChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick::WheelHandler::rotationScale
+
+ The scaling to be applied to the \l rotation property, and to the
+ \l property on the \l target item, if any. The default is 1, such that
+ \l rotation will be in units of degrees of rotation. It can be set to a
+ negative number to invert the effect of the direction of mouse wheel
+ rotation.
+*/
+qreal QQuickWheelHandler::rotationScale() const
+{
+ Q_D(const QQuickWheelHandler);
+ return d->rotationScale;
+}
+
+void QQuickWheelHandler::setRotationScale(qreal rotationScale)
+{
+ Q_D(QQuickWheelHandler);
+ if (qFuzzyCompare(d->rotationScale, rotationScale))
+ return;
+ if (qFuzzyIsNull(rotationScale)) {
+ qWarning("rotationScale cannot be set to zero");
+ return;
+ }
+
+ d->rotationScale = rotationScale;
+ emit rotationScaleChanged();
+}
+
+/*!
+ \qmlproperty string QtQuick::WheelHandler::property
+
+ The property to be modified on the \l target when the mouse wheel is rotated.
+
+ The default is no property (empty string). When no target property is being
+ automatically modified, you can use bindings to react to mouse wheel
+ rotation in arbitrary ways.
+
+ You can use the mouse wheel to adjust any numeric property. For example if
+ \c property is set to \c x, the \l target will move horizontally as the
+ wheel is rotated. The following properties have special behavior:
+
+ \value scale
+ \l{QQuickItem::scale}{scale} will be modified in a non-linear fashion
+ as described under \l targetScaleMultiplier. If
+ \l targetTransformAroundCursor is \c true, the \l{QQuickItem::x}{x} and
+ \l{QQuickItem::y}{y} properties will be simultaneously adjusted so that
+ the user will effectively zoom into or out of the point under the mouse
+ cursor.
+ \value rotation
+ \l{QQuickItem::rotation}{rotation} will be set to \l rotation. If
+ \l targetTransformAroundCursor is \c true, the l{QQuickItem::x}{x} and
+ \l{QQuickItem::y}{y} properties will be simultaneously adjusted so
+ that the user will effectively rotate the item around the point under
+ the mouse cursor.
+
+ The adjustment of the given target property is always scaled by \l rotationScale.
+*/
+QString QQuickWheelHandler::property() const
+{
+ Q_D(const QQuickWheelHandler);
+ return d->propertyName;
+}
+
+void QQuickWheelHandler::setProperty(const QString &propertyName)
+{
+ Q_D(QQuickWheelHandler);
+ if (d->propertyName == propertyName)
+ return;
+
+ d->propertyName = propertyName;
+ d->metaPropertyDirty = true;
+ emit propertyChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick::WheelHandler::targetScaleMultiplier
+
+ The amount by which the \l target \l{QQuickItem::scale}{scale} is to be
+ multiplied whenever the \l rotation changes by 15 degrees. This
+ is relevant only when \l property is \c "scale".
+
+ The \c scale will be multiplied by
+ \c targetScaleMultiplier \sup {angleDelta * rotationScale / 15}.
+ The default is \c 2 \sup {1/3}, which means that if \l rotationScale is left
+ at its default value, and the mouse wheel is rotated by one "click"
+ (15 degrees), the \l target will be scaled by approximately 1.25; after
+ three "clicks" its size will be doubled or halved, depending on the
+ direction that the wheel is rotated. If you want to make it double or halve
+ with every 2 clicks of the wheel, set this to \c 2 \sup {1/2} (1.4142).
+ If you want to make it scale the opposite way as the wheel is rotated,
+ set \c rotationScale to a negative value.
+*/
+qreal QQuickWheelHandler::targetScaleMultiplier() const
+{
+ Q_D(const QQuickWheelHandler);
+ return d->targetScaleMultiplier;
+}
+
+void QQuickWheelHandler::setTargetScaleMultiplier(qreal targetScaleMultiplier)
+{
+ Q_D(QQuickWheelHandler);
+ if (qFuzzyCompare(d->targetScaleMultiplier, targetScaleMultiplier))
+ return;
+
+ d->targetScaleMultiplier = targetScaleMultiplier;
+ emit targetScaleMultiplierChanged();
+}
+
+/*!
+ \qmlproperty bool QtQuick::WheelHandler::targetTransformAroundCursor
+
+ Whether the \l target should automatically be repositioned in such a way
+ that it is transformed around the mouse cursor position while the
+ \l property is adjusted. The default is \c true.
+
+ If \l property is set to \c "rotation" and \l targetTransformAroundCursor
+ is \c true, then as the wheel is rotated, the \l target item will rotate in
+ place around the mouse cursor position. If \c targetTransformAroundCursor
+ is \c false, it will rotate around its
+ \l{QQuickItem::transformOrigin}{transformOrigin} instead.
+*/
+bool QQuickWheelHandler::isTargetTransformAroundCursor() const
+{
+ Q_D(const QQuickWheelHandler);
+ return d->targetTransformAroundCursor;
+}
+
+void QQuickWheelHandler::setTargetTransformAroundCursor(bool ttac)
+{
+ Q_D(QQuickWheelHandler);
+ if (d->targetTransformAroundCursor == ttac)
+ return;
+
+ d->targetTransformAroundCursor = ttac;
+ emit targetTransformAroundCursorChanged();
+}
+
+bool QQuickWheelHandler::wantsPointerEvent(QQuickPointerEvent *event)
+{
+ if (!event)
+ return false;
+ QQuickPointerScrollEvent *scroll = event->asPointerScrollEvent();
+ if (!scroll)
+ return false;
+ if (!acceptedDevices().testFlag(QQuickPointerDevice::DeviceType::TouchPad)
+ && scroll->synthSource() != Qt::MouseEventNotSynthesized)
+ return false;
+ if (!active()) {
+ switch (orientation()) {
+ case Qt::Horizontal:
+ if (qFuzzyIsNull(scroll->angleDelta().x()) && qFuzzyIsNull(scroll->pixelDelta().x()))
+ return false;
+ break;
+ case Qt::Vertical:
+ if (qFuzzyIsNull(scroll->angleDelta().y()) && qFuzzyIsNull(scroll->pixelDelta().y()))
+ return false;
+ break;
+ }
+ }
+ QQuickEventPoint *point = event->point(0);
+ if (QQuickPointerDeviceHandler::wantsPointerEvent(event) && wantsEventPoint(point) && parentContains(point)) {
+ setPointId(point->pointId());
+ return true;
+ }
+ return false;
+}
+
+void QQuickWheelHandler::handleEventPoint(QQuickEventPoint *point)
+{
+ Q_D(QQuickWheelHandler);
+ QQuickPointerScrollEvent *event = point->pointerEvent()->asPointerScrollEvent();
+ setActive(true); // ScrollEnd will not happen unless it was already active (see setActive(false) below)
+ point->setAccepted();
+ qreal inversion = !d->invertible && event->isInverted() ? -1 : 1;
+ qreal angleDelta = inversion * qreal(orientation() == Qt::Horizontal ? event->angleDelta().x() :
+ event->angleDelta().y()) / 8;
+ d->rotation += angleDelta;
+ emit rotationChanged();
+ emit wheel(event);
+ if (!d->propertyName.isEmpty() && target()) {
+ QQuickItem *t = target();
+ // writing target()'s property is done via QMetaProperty::write() so that any registered interceptors can react.
+ if (d->propertyName == QLatin1String("scale")) {
+ qreal multiplier = qPow(d->targetScaleMultiplier, angleDelta * d->rotationScale / 15); // wheel "clicks"
+ const QPointF centroidParentPos = t->parentItem()->mapFromScene(point->scenePosition());
+ const QPointF positionWas = t->position();
+ const qreal scaleWas = t->scale();
+ const qreal activePropertyValue = scaleWas * multiplier;
+ qCDebug(lcWheelHandler) << objectName() << "angle delta" << event->angleDelta() << "pixel delta" << event->pixelDelta()
+ << "@" << point->position() << "in parent" << centroidParentPos
+ << "in scene" << point->scenePosition()
+ << "multiplier" << multiplier << "scale" << scaleWas
+ << "->" << activePropertyValue;
+ d->targetMetaProperty().write(t, activePropertyValue);
+ if (d->targetTransformAroundCursor) {
+ // If an interceptor intervened, scale may now be different than we asked for. Adjust accordingly.
+ multiplier = t->scale() / scaleWas;
+ const QPointF adjPos = QQuickItemPrivate::get(t)->adjustedPosForTransform(
+ centroidParentPos, positionWas, QVector2D(), scaleWas, multiplier, t->rotation(), 0);
+ qCDebug(lcWheelHandler) << "adjusting item pos" << adjPos << "in scene" << t->parentItem()->mapToScene(adjPos);
+ t->setPosition(adjPos);
+ }
+ } else if (d->propertyName == QLatin1String("rotation")) {
+ const QPointF positionWas = t->position();
+ const qreal rotationWas = t->rotation();
+ const qreal activePropertyValue = rotationWas + angleDelta * d->rotationScale;
+ const QPointF centroidParentPos = t->parentItem()->mapFromScene(point->scenePosition());
+ qCDebug(lcWheelHandler) << objectName() << "angle delta" << event->angleDelta() << "pixel delta" << event->pixelDelta()
+ << "@" << point->position() << "in parent" << centroidParentPos
+ << "in scene" << point->scenePosition() << "rotation" << t->rotation()
+ << "->" << activePropertyValue;
+ d->targetMetaProperty().write(t, activePropertyValue);
+ if (d->targetTransformAroundCursor) {
+ // If an interceptor intervened, rotation may now be different than we asked for. Adjust accordingly.
+ const QPointF adjPos = QQuickItemPrivate::get(t)->adjustedPosForTransform(
+ centroidParentPos, positionWas, QVector2D(),
+ t->scale(), 1, rotationWas, t->rotation() - rotationWas);
+ qCDebug(lcWheelHandler) << "adjusting item pos" << adjPos << "in scene" << t->parentItem()->mapToScene(adjPos);
+ t->setPosition(adjPos);
+ }
+ } else {
+ qCDebug(lcWheelHandler) << objectName() << "angle delta" << event->angleDelta() << "scaled" << angleDelta << "total" << d->rotation << "pixel delta" << event->pixelDelta()
+ << "@" << point->position() << "in scene" << point->scenePosition() << "rotation" << t->rotation();
+ qreal delta = 0;
+ if (event->hasPixelDelta()) {
+ delta = inversion * d->rotationScale * qreal(orientation() == Qt::Horizontal ? event->pixelDelta().x() : event->pixelDelta().y());
+ qCDebug(lcWheelHandler) << "changing target" << d->propertyName << "by pixel delta" << delta << "from" << event;
+ } else {
+ delta = angleDelta * d->rotationScale;
+ qCDebug(lcWheelHandler) << "changing target" << d->propertyName << "by scaled angle delta" << delta << "from" << event;
+ }
+ bool ok = false;
+ qreal value = d->targetMetaProperty().read(t).toReal(&ok);
+ if (ok)
+ d->targetMetaProperty().write(t, value + qreal(delta));
+ else
+ qWarning() << "failed to read property" << d->propertyName << "of" << t;
+ }
+ }
+ switch (event->phase()) {
+ case Qt::ScrollEnd:
+ qCDebug(lcWheelHandler) << objectName() << "deactivating due to ScrollEnd phase";
+ setActive(false);
+ break;
+ case Qt::NoScrollPhase:
+ d->deactivationTimer.start(qRound(d->activeTimeout * 1000), this);
+ break;
+ case Qt::ScrollBegin:
+ case Qt::ScrollUpdate:
+ case Qt::ScrollMomentum:
+ break;
+ }
+}
+
+void QQuickWheelHandler::onTargetChanged(QQuickItem *oldTarget)
+{
+ Q_UNUSED(oldTarget)
+ Q_D(QQuickWheelHandler);
+ d->metaPropertyDirty = true;
+}
+
+void QQuickWheelHandler::onActiveChanged()
+{
+ Q_D(QQuickWheelHandler);
+ if (!active())
+ d->deactivationTimer.stop();
+}
+
+void QQuickWheelHandler::timerEvent(QTimerEvent *event)
+{
+ Q_D(const QQuickWheelHandler);
+ if (event->timerId() == d->deactivationTimer.timerId()) {
+ qCDebug(lcWheelHandler) << objectName() << "deactivating due to timeout";
+ setActive(false);
+ }
+}
+
+QQuickWheelHandlerPrivate::QQuickWheelHandlerPrivate()
+ : QQuickSinglePointHandlerPrivate()
+{
+}
+
+QMetaProperty &QQuickWheelHandlerPrivate::targetMetaProperty() const
+{
+ Q_Q(const QQuickWheelHandler);
+ if (metaPropertyDirty && q->target()) {
+ if (!propertyName.isEmpty()) {
+ const QMetaObject *targetMeta = q->target()->metaObject();
+ metaProperty = targetMeta->property(
+ targetMeta->indexOfProperty(propertyName.toLocal8Bit().constData()));
+ }
+ metaPropertyDirty = false;
+ }
+ return metaProperty;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickwheelhandler_p.h b/src/quick/handlers/qquickwheelhandler_p.h
new file mode 100644
index 0000000000..f8d1c00726
--- /dev/null
+++ b/src/quick/handlers/qquickwheelhandler_p.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 QQUICKWHEELHANDLER_H
+#define QQUICKWHEELHANDLER_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 "qquickitem.h"
+#include "qevent.h"
+#include "qquicksinglepointhandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickWheelEvent;
+class QQuickWheelHandlerPrivate;
+
+class Q_QUICK_PRIVATE_EXPORT QQuickWheelHandler : public QQuickSinglePointHandler
+{
+ Q_OBJECT
+ Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged)
+ Q_PROPERTY(bool invertible READ isInvertible WRITE setInvertible NOTIFY invertibleChanged)
+ Q_PROPERTY(qreal activeTimeout READ activeTimeout WRITE setActiveTimeout NOTIFY activeTimeoutChanged)
+ Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged)
+ Q_PROPERTY(qreal rotationScale READ rotationScale WRITE setRotationScale NOTIFY rotationScaleChanged)
+ Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged)
+ Q_PROPERTY(qreal targetScaleMultiplier READ targetScaleMultiplier WRITE setTargetScaleMultiplier NOTIFY targetScaleMultiplierChanged)
+ Q_PROPERTY(bool targetTransformAroundCursor READ isTargetTransformAroundCursor WRITE setTargetTransformAroundCursor NOTIFY targetTransformAroundCursorChanged)
+
+public:
+ explicit QQuickWheelHandler(QQuickItem *parent = nullptr);
+
+ Qt::Orientation orientation() const;
+ void setOrientation(Qt::Orientation orientation);
+
+ bool isInvertible() const;
+ void setInvertible(bool invertible);
+
+ qreal activeTimeout() const;
+ void setActiveTimeout(qreal timeout);
+
+ qreal rotation() const;
+ void setRotation(qreal rotation);
+
+ qreal rotationScale() const;
+ void setRotationScale(qreal rotationScale);
+
+ QString property() const;
+ void setProperty(const QString &name);
+
+ qreal targetScaleMultiplier() const;
+ void setTargetScaleMultiplier(qreal targetScaleMultiplier);
+
+ bool isTargetTransformAroundCursor() const;
+ void setTargetTransformAroundCursor(bool ttac);
+
+Q_SIGNALS:
+ void wheel(QQuickPointerScrollEvent *event);
+
+ void orientationChanged();
+ void invertibleChanged();
+ void activeTimeoutChanged();
+ void rotationChanged();
+ void rotationScaleChanged();
+ void propertyChanged();
+ void targetScaleMultiplierChanged();
+ void targetTransformAroundCursorChanged();
+
+protected:
+ bool wantsPointerEvent(QQuickPointerEvent *event) override;
+ void handleEventPoint(QQuickEventPoint *point) override;
+ void onTargetChanged(QQuickItem *oldTarget) override;
+ void onActiveChanged() override;
+ void timerEvent(QTimerEvent *event) override;
+
+ Q_DECLARE_PRIVATE(QQuickWheelHandler)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickWheelHandler)
+
+#endif // QQUICKWHEELHANDLER_H
diff --git a/src/quick/handlers/qquickwheelhandler_p_p.h b/src/quick/handlers/qquickwheelhandler_p_p.h
new file mode 100644
index 0000000000..d35e04c51b
--- /dev/null
+++ b/src/quick/handlers/qquickwheelhandler_p_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the 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 QQUICKWHEELHANDLER_P_P_H
+#define QQUICKWHEELHANDLER_P_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 "qquicksinglepointhandler_p_p.h"
+#include "qquickwheelhandler_p.h"
+#include <QtCore/qbasictimer.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QQuickWheelHandlerPrivate : public QQuickSinglePointHandlerPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickWheelHandler)
+
+public:
+ static QQuickWheelHandlerPrivate* get(QQuickWheelHandler *q) { return q->d_func(); }
+ static const QQuickWheelHandlerPrivate* get(const QQuickWheelHandler *q) { return q->d_func(); }
+
+ QQuickWheelHandlerPrivate();
+
+ QMetaProperty &targetMetaProperty() const;
+
+ QBasicTimer deactivationTimer;
+ qreal activeTimeout = 0.1;
+ qreal rotationScale = 1;
+ qreal rotation = 0; // in units of degrees
+ qreal targetScaleMultiplier = 1.25992104989487; // qPow(2, 1/3)
+ QString propertyName;
+ mutable QMetaProperty metaProperty;
+ Qt::Orientation orientation = Qt::Vertical;
+ mutable bool metaPropertyDirty = true;
+ bool invertible = true;
+ bool targetTransformAroundCursor = true;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKWHEELHANDLER_P_P_H
diff --git a/src/quick/items/context2d/qquickcanvascontext_p.h b/src/quick/items/context2d/qquickcanvascontext_p.h
index 95100d2912..3872a2ac74 100644
--- a/src/quick/items/context2d/qquickcanvascontext_p.h
+++ b/src/quick/items/context2d/qquickcanvascontext_p.h
@@ -56,7 +56,7 @@
QT_REQUIRE_CONFIG(quick_canvas);
#include <QtQuick/qquickitem.h>
-#include <private/qv8engine_p.h>
+#include <QtQml/private/qv4value_p.h>
QT_BEGIN_NAMESPACE
@@ -80,6 +80,7 @@ public:
virtual void prepare(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth, bool antialiasing);
virtual void flush();
+ virtual QV4::ExecutionEngine *v4Engine() const = 0;
virtual void setV4Engine(QV4::ExecutionEngine *engine) = 0;
virtual QV4::ReturnedValue v4value() const = 0;
diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp
index 3462b0dbac..b8e6d58af4 100644
--- a/src/quick/items/context2d/qquickcanvasitem.cpp
+++ b/src/quick/items/context2d/qquickcanvasitem.cpp
@@ -378,13 +378,10 @@ void QQuickCanvasItem::setContextType(const QString &contextType)
this property will contain the current drawing context, otherwise null.
*/
-QQmlV4Handle QQuickCanvasItem::context() const
+QJSValue QQuickCanvasItem::context() const
{
Q_D(const QQuickCanvasItem);
- if (d->context)
- return QQmlV4Handle(d->context->v4value());
-
- return QQmlV4Handle(QV4::Encode::null());
+ return d->context ? QJSValue(d->context->v4Engine(), d->context->v4value()) : QJSValue();
}
/*!
@@ -772,7 +769,8 @@ QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
QSGInternalImageNode *node = static_cast<QSGInternalImageNode *>(oldNode);
if (!node) {
- node = QQuickWindowPrivate::get(window())->context->sceneGraphContext()->createInternalImageNode();
+ QSGRenderContext *rc = QQuickWindowPrivate::get(window())->context;
+ node = rc->sceneGraphContext()->createInternalImageNode(rc);
d->node = node;
}
diff --git a/src/quick/items/context2d/qquickcanvasitem_p.h b/src/quick/items/context2d/qquickcanvasitem_p.h
index 7dc981a6eb..cd2977429b 100644
--- a/src/quick/items/context2d/qquickcanvasitem_p.h
+++ b/src/quick/items/context2d/qquickcanvasitem_p.h
@@ -56,9 +56,9 @@
QT_REQUIRE_CONFIG(quick_canvas);
#include <QtQuick/qquickitem.h>
-#include <private/qv8engine_p.h>
#include <private/qqmlrefcount_p.h>
#include <QtCore/QThread>
+#include <QtCore/qmutex.h>
#include <QtGui/QImage>
QT_BEGIN_NAMESPACE
@@ -93,7 +93,7 @@ class QQuickCanvasItem : public QQuickItem
Q_PROPERTY(bool available READ isAvailable NOTIFY availableChanged)
Q_PROPERTY(QString contextType READ contextType WRITE setContextType NOTIFY contextTypeChanged)
- Q_PROPERTY(QQmlV4Handle context READ context NOTIFY contextChanged)
+ Q_PROPERTY(QJSValue context READ context NOTIFY contextChanged)
Q_PROPERTY(QSizeF canvasSize READ canvasSize WRITE setCanvasSize NOTIFY canvasSizeChanged)
Q_PROPERTY(QSize tileSize READ tileSize WRITE setTileSize NOTIFY tileSizeChanged)
Q_PROPERTY(QRectF canvasWindow READ canvasWindow WRITE setCanvasWindow NOTIFY canvasWindowChanged)
@@ -122,7 +122,7 @@ public:
QString contextType() const;
void setContextType(const QString &contextType);
- QQmlV4Handle context() const;
+ QJSValue context() const;
QSizeF canvasSize() const;
void setCanvasSize(const QSizeF &);
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index 7cd222d3c1..54cda72a36 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -56,7 +56,6 @@
#include <private/qquickimage_p_p.h>
#include <qqmlinfo.h>
-#include <private/qv8engine_p.h>
#include <qqmlengine.h>
#include <private/qv4domerrors_p.h>
@@ -474,7 +473,7 @@ static QFont qt_font_from_string(const QString& fontString, const QFont &current
return newFont;
}
-class QQuickContext2DEngineData : public QV8Engine::Deletable
+class QQuickContext2DEngineData : public QV4::ExecutionEngine::Deletable
{
public:
QQuickContext2DEngineData(QV4::ExecutionEngine *engine);
@@ -4324,7 +4323,8 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args
m_renderTarget = QQuickCanvasItem::Image;
}
- // Disable Framebuffer Object based rendering when not running with OpenGL
+ // Disable Framebuffer Object based rendering when not running with OpenGL.
+ // Same goes for the RHI based code path (regardless of the backend in use).
if (m_renderTarget == QQuickCanvasItem::FramebufferObject) {
QSGRendererInterface *rif = canvasItem->window()->rendererInterface();
if (rif && rif->graphicsApi() != QSGRendererInterface::OpenGL)
@@ -4587,6 +4587,11 @@ void QQuickContext2D::reset()
m_buffer->clearRect(QRectF(0, 0, m_canvas->width(), m_canvas->height()));
}
+QV4::ExecutionEngine *QQuickContext2D::v4Engine() const
+{
+ return m_v4engine;
+}
+
void QQuickContext2D::setV4Engine(QV4::ExecutionEngine *engine)
{
if (m_v4engine != engine) {
diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h
index 1ece6796f3..b5626dec0c 100644
--- a/src/quick/items/context2d/qquickcontext2d_p.h
+++ b/src/quick/items/context2d/qquickcontext2d_p.h
@@ -65,7 +65,6 @@ QT_REQUIRE_CONFIG(quick_canvas);
#include <QtCore/qstring.h>
#include <QtCore/qstack.h>
#include <QtCore/qqueue.h>
-#include <private/qv8engine_p.h>
#include <QtCore/QWaitCondition>
#include <private/qv4value_p.h>
@@ -199,6 +198,7 @@ public:
QImage toImage(const QRectF& bounds) override;
QV4::ReturnedValue v4value() const override;
+ QV4::ExecutionEngine *v4Engine() const override;
void setV4Engine(QV4::ExecutionEngine *eng) override;
QQuickCanvasItem* canvas() const { return m_canvas; }
diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp
index 69ff3b3852..0ebd1a66c9 100644
--- a/src/quick/items/context2d/qquickcontext2dtexture.cpp
+++ b/src/quick/items/context2d/qquickcontext2dtexture.cpp
@@ -41,7 +41,7 @@
#include "qquickcontext2dtile_p.h"
#include "qquickcanvasitem_p.h"
#include <private/qquickitem_p.h>
-#include <QtQuick/private/qsgtexture_p.h>
+#include <QtQuick/private/qsgplaintexture_p.h>
#include "qquickcontext2dcommandbuffer_p.h"
#include <QOpenGLPaintDevice>
#if QT_CONFIG(opengl)
@@ -238,15 +238,8 @@ void QQuickContext2DTexture::paintWithoutTiles(QQuickContext2DCommandBuffer *ccb
QPainter p;
p.begin(device);
- if (m_antialiasing)
- p.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing | QPainter::TextAntialiasing, true);
- else
- p.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing | QPainter::TextAntialiasing, false);
-
- if (m_smooth)
- p.setRenderHint(QPainter::SmoothPixmapTransform, true);
- else
- p.setRenderHint(QPainter::SmoothPixmapTransform, false);
+ p.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing, m_antialiasing);
+ p.setRenderHint(QPainter::SmoothPixmapTransform, m_smooth);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri
index e649b5429d..6f2f9fbd8e 100644
--- a/src/quick/items/items.pri
+++ b/src/quick/items/items.pri
@@ -52,8 +52,6 @@ HEADERS += \
$$PWD/qquickstateoperations_p.h \
$$PWD/qquickimplicitsizeitem_p.h \
$$PWD/qquickimplicitsizeitem_p_p.h \
- $$PWD/qquickdrag_p.h \
- $$PWD/qquickdroparea_p.h \
$$PWD/qquickmultipointtoucharea_p.h \
$$PWD/qquickscreen_p.h \
$$PWD/qquickwindowattached_p.h \
@@ -95,8 +93,6 @@ SOURCES += \
$$PWD/qquickstateoperations.cpp \
$$PWD/qquickimplicitsizeitem.cpp \
$$PWD/qquickaccessibleattached.cpp \
- $$PWD/qquickdrag.cpp \
- $$PWD/qquickdroparea.cpp \
$$PWD/qquickmultipointtoucharea.cpp \
$$PWD/qquickwindowmodule.cpp \
$$PWD/qquickscreen.cpp \
@@ -105,6 +101,16 @@ SOURCES += \
$$PWD/qquickgraphicsinfo.cpp \
$$PWD/qquickitemgrabresult.cpp
+qtConfig(quick-draganddrop) {
+ HEADERS += \
+ $$PWD/qquickdrag_p.h \
+ $$PWD/qquickdroparea_p.h \
+
+ SOURCES += \
+ $$PWD/qquickdrag.cpp \
+ $$PWD/qquickdroparea.cpp \
+}
+
qtConfig(quick-animatedimage) {
HEADERS += \
$$PWD/qquickanimatedimage_p.h \
diff --git a/src/quick/items/qquickanimatedimage.cpp b/src/quick/items/qquickanimatedimage.cpp
index fe445425e7..18d492cd68 100644
--- a/src/quick/items/qquickanimatedimage.cpp
+++ b/src/quick/items/qquickanimatedimage.cpp
@@ -67,7 +67,7 @@ QQuickPixmap* QQuickAnimatedImagePrivate::infoForCurrentFrame(QQmlEngine *engine
.arg(current));
}
if (!requestedUrl.isEmpty()) {
- if (QQuickPixmap::isCached(requestedUrl, QSize(), QQuickImageProviderOptions()))
+ if (QQuickPixmap::isCached(requestedUrl, QSize(), 0, QQuickImageProviderOptions()))
pixmap = new QQuickPixmap(engine, requestedUrl);
else
pixmap = new QQuickPixmap(requestedUrl, movie->currentImage());
@@ -139,6 +139,9 @@ QQuickAnimatedImage::QQuickAnimatedImage(QQuickItem *parent)
: QQuickImage(*(new QQuickAnimatedImagePrivate), parent)
{
connect(this, &QQuickImageBase::cacheChanged, this, &QQuickAnimatedImage::onCacheChanged);
+ connect(this, &QQuickImageBase::currentFrameChanged, this, &QQuickAnimatedImage::frameChanged);
+ connect(this, &QQuickImageBase::currentFrameChanged, this, &QQuickAnimatedImage::currentFrameChanged);
+ connect(this, &QQuickImageBase::frameCountChanged, this, &QQuickAnimatedImage::frameCountChanged);
}
QQuickAnimatedImage::~QQuickAnimatedImage()
@@ -464,7 +467,7 @@ void QQuickAnimatedImage::movieUpdate()
if (d->movie) {
d->setPixmap(*d->infoForCurrentFrame(qmlEngine(this)));
- emit frameChanged();
+ emit QQuickImageBase::currentFrameChanged();
}
}
diff --git a/src/quick/items/qquickanimatedimage_p.h b/src/quick/items/qquickanimatedimage_p.h
index 6b5db215bd..13eae83350 100644
--- a/src/quick/items/qquickanimatedimage_p.h
+++ b/src/quick/items/qquickanimatedimage_p.h
@@ -85,10 +85,10 @@ public:
bool isPaused() const;
void setPaused(bool pause);
- int currentFrame() const;
- void setCurrentFrame(int frame);
+ int currentFrame() const override;
+ void setCurrentFrame(int frame) override;
- int frameCount() const;
+ int frameCount() const override;
qreal speed() const;
void setSpeed(qreal speed);
@@ -101,6 +101,7 @@ Q_SIGNALS:
void playingChanged();
void pausedChanged();
void frameChanged();
+ void currentFrameChanged();
void frameCountChanged();
Q_REVISION(11) void speedChanged();
diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp
index 18adb4e992..d22e77c8ad 100644
--- a/src/quick/items/qquickanimatedsprite.cpp
+++ b/src/quick/items/qquickanimatedsprite.cpp
@@ -532,7 +532,7 @@ void QQuickAnimatedSprite::setInterpolate(bool arg)
}
}
-void QQuickAnimatedSprite::setSource(QUrl arg)
+void QQuickAnimatedSprite::setSource(const QUrl &arg)
{
Q_D(QQuickAnimatedSprite);
diff --git a/src/quick/items/qquickanimatedsprite_p.h b/src/quick/items/qquickanimatedsprite_p.h
index ff59591c9f..d36f908c78 100644
--- a/src/quick/items/qquickanimatedsprite_p.h
+++ b/src/quick/items/qquickanimatedsprite_p.h
@@ -122,7 +122,7 @@ Q_SIGNALS:
void runningChanged(bool arg);
void interpolateChanged(bool arg);
- void sourceChanged(QUrl arg);
+ void sourceChanged(const QUrl &arg);
void reverseChanged(bool arg);
void frameSyncChanged(bool arg);
void frameCountChanged(int arg);
@@ -148,7 +148,7 @@ public Q_SLOTS:
void setRunning(bool arg);
void setPaused(bool arg);
void setInterpolate(bool arg);
- void setSource(QUrl arg);
+ void setSource(const QUrl &arg);
void setReverse(bool arg);
void setFrameSync(bool arg);
void setFrameCount(int arg);
diff --git a/src/quick/items/qquickborderimage.cpp b/src/quick/items/qquickborderimage.cpp
index b840328184..462a44634e 100644
--- a/src/quick/items/qquickborderimage.cpp
+++ b/src/quick/items/qquickborderimage.cpp
@@ -327,7 +327,7 @@ void QQuickBorderImage::load()
QNetworkRequest req(d->url);
d->sciReply = qmlEngine(this)->networkAccessManager()->get(req);
qmlobject_connect(d->sciReply, QNetworkReply, SIGNAL(finished()),
- this, QQuickBorderImage, SLOT(sciRequestFinished()))
+ this, QQuickBorderImage, SLOT(sciRequestFinished()));
#endif
}
} else {
@@ -343,7 +343,7 @@ void QQuickBorderImage::load()
QUrl loadUrl = d->url;
resolve2xLocalFile(d->url, targetDevicePixelRatio, &loadUrl, &d->devicePixelRatio);
- d->pix.load(qmlEngine(this), loadUrl, d->sourcesize * d->devicePixelRatio, options);
+ d->pix.load(qmlEngine(this), loadUrl, d->sourcesize * d->devicePixelRatio, options, QQuickImageProviderOptions(), d->currentFrame, d->frameCount);
if (d->pix.isLoading()) {
if (d->progress != 0.0) {
@@ -534,6 +534,10 @@ void QQuickBorderImage::requestFinished()
d->oldSourceSize = sourceSize();
emit sourceSizeChanged();
}
+ if (d->frameCount != d->pix.frameCount()) {
+ d->frameCount = d->pix.frameCount();
+ emit frameCountChanged();
+ }
pixmapChange();
}
@@ -647,7 +651,7 @@ QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat
bool updatePixmap = d->pixmapChanged;
d->pixmapChanged = false;
if (!node) {
- node = d->sceneGraphContext()->createInternalImageNode();
+ node = d->sceneGraphContext()->createInternalImageNode(d->sceneGraphRenderContext());
updatePixmap = true;
}
@@ -693,6 +697,18 @@ void QQuickBorderImage::pixmapChange()
update();
}
+/*!
+ \qmlproperty int QtQuick::BorderImage::currentFrame
+ \qmlproperty int QtQuick::BorderImage::frameCount
+ \since 5.14
+
+ currentFrame is the frame that is currently visible. The default is \c 0.
+ You can set it to a number between \c 0 and \c {frameCount - 1} to display a
+ different frame, if the image contains multiple frames.
+
+ frameCount is the number of frames in the image. Most images have only one frame.
+*/
+
QT_END_NAMESPACE
#include "moc_qquickborderimage_p.cpp"
diff --git a/src/quick/items/qquickborderimage_p_p.h b/src/quick/items/qquickborderimage_p_p.h
index 0f4e7acc05..17dab7d121 100644
--- a/src/quick/items/qquickborderimage_p_p.h
+++ b/src/quick/items/qquickborderimage_p_p.h
@@ -86,7 +86,7 @@ public:
if (!border) {
border = new QQuickScaleGrid(q);
qmlobject_connect(border, QQuickScaleGrid, SIGNAL(borderChanged()),
- q, QQuickBorderImage, SLOT(doUpdate()))
+ q, QQuickBorderImage, SLOT(doUpdate()));
}
return border;
}
diff --git a/src/quick/items/qquickdrag.cpp b/src/quick/items/qquickdrag.cpp
index a2cb739ff2..7c6f1caa54 100644
--- a/src/quick/items/qquickdrag.cpp
+++ b/src/quick/items/qquickdrag.cpp
@@ -45,7 +45,6 @@
#include <QtQuick/private/qquickevents_p_p.h>
#include <private/qquickitemchangelistener_p.h>
#include <private/qquickpixmapcache_p.h>
-#include <private/qv8engine_p.h>
#include <private/qv4scopedvalue_p.h>
#include <QtCore/qmimedata.h>
#include <QtQml/qqmlinfo.h>
@@ -53,7 +52,6 @@
#include <QtGui/qstylehints.h>
#include <QtGui/qguiapplication.h>
-#if QT_CONFIG(draganddrop)
#include <qpa/qplatformdrag.h>
#include <QtGui/qdrag.h>
@@ -1001,5 +999,3 @@ QQuickDragAttached *QQuickDrag::qmlAttachedProperties(QObject *obj)
QT_END_NAMESPACE
#include "moc_qquickdrag_p.cpp"
-
-#endif // draganddrop
diff --git a/src/quick/items/qquickdrag_p.h b/src/quick/items/qquickdrag_p.h
index 6bfbae74c9..094070aa2c 100644
--- a/src/quick/items/qquickdrag_p.h
+++ b/src/quick/items/qquickdrag_p.h
@@ -53,14 +53,14 @@
#include <QtQuick/qquickitem.h>
-#include <private/qv8engine_p.h>
+#include <private/qintrusivelist_p.h>
#include <private/qqmlguard_p.h>
#include <QtCore/qmimedata.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qurl.h>
-#if QT_CONFIG(draganddrop)
+QT_REQUIRE_CONFIG(quick_draganddrop);
QT_BEGIN_NAMESPACE
@@ -318,6 +318,4 @@ QT_END_NAMESPACE
QML_DECLARE_TYPE(QQuickDrag)
QML_DECLARE_TYPEINFO(QQuickDrag, QML_HAS_ATTACHED_PROPERTIES)
-#endif // draganddrop
-
#endif
diff --git a/src/quick/items/qquickdroparea.cpp b/src/quick/items/qquickdroparea.cpp
index fbbee31653..76be8cc261 100644
--- a/src/quick/items/qquickdroparea.cpp
+++ b/src/quick/items/qquickdroparea.cpp
@@ -43,7 +43,7 @@
#include <private/qv4arraybuffer_p.h>
-#if QT_CONFIG(draganddrop)
+#include <QtCore/qregularexpression.h>
QT_BEGIN_NAMESPACE
@@ -70,7 +70,7 @@ public:
QStringList getKeys(const QMimeData *mimeData) const;
QStringList keys;
- QRegExp keyRegExp;
+ QRegularExpression keyRegExp;
QPointF dragPosition;
QQuickDropAreaDrag *drag;
QPointer<QObject> source;
@@ -157,13 +157,15 @@ void QQuickDropArea::setKeys(const QStringList &keys)
d->keys = keys;
if (keys.isEmpty()) {
- d->keyRegExp = QRegExp();
+ d->keyRegExp = QRegularExpression();
} else {
- QString pattern = QLatin1Char('(') + QRegExp::escape(keys.first());
+ QString pattern = QLatin1Char('(') + QRegularExpression::escape(keys.first());
for (int i = 1; i < keys.count(); ++i)
- pattern += QLatin1Char('|') + QRegExp::escape(keys.at(i));
+ pattern += QLatin1Char('|') + QRegularExpression::escape(keys.at(i));
pattern += QLatin1Char(')');
- d->keyRegExp = QRegExp(pattern.replace(QLatin1String("\\*"), QLatin1String(".+")));
+ d->keyRegExp = QRegularExpression(
+ QRegularExpression::anchoredPattern(pattern.replace(QLatin1String("\\*"),
+ QLatin1String(".+"))));
}
emit keysChanged();
}
@@ -231,12 +233,11 @@ void QQuickDropArea::dragMoveEvent(QDragMoveEvent *event)
bool QQuickDropAreaPrivate::hasMatchingKey(const QStringList &keys) const
{
- if (keyRegExp.isEmpty())
+ if (keyRegExp.pattern().isEmpty())
return true;
- QRegExp copy = keyRegExp;
for (const QString &key : keys) {
- if (copy.exactMatch(key))
+ if (key.contains(keyRegExp))
return true;
}
return false;
@@ -619,5 +620,3 @@ void QQuickDropEvent::accept(QQmlV4Function *args)
QT_END_NAMESPACE
#include "moc_qquickdroparea_p.cpp"
-
-#endif // draganddrop
diff --git a/src/quick/items/qquickdroparea_p.h b/src/quick/items/qquickdroparea_p.h
index d25cd4decc..2b2ace2eae 100644
--- a/src/quick/items/qquickdroparea_p.h
+++ b/src/quick/items/qquickdroparea_p.h
@@ -55,7 +55,7 @@
#include <QtGui/qevent.h>
-#if QT_CONFIG(draganddrop)
+QT_REQUIRE_CONFIG(quick_draganddrop);
QT_BEGIN_NAMESPACE
@@ -190,6 +190,4 @@ QT_END_NAMESPACE
QML_DECLARE_TYPE(QQuickDropEvent)
QML_DECLARE_TYPE(QQuickDropArea)
-#endif // draganddrop
-
#endif // QQUICKDROPAREA_P_H
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp
index 7d128b4a16..0c697739fa 100644
--- a/src/quick/items/qquickevents.cpp
+++ b/src/quick/items/qquickevents.cpp
@@ -41,6 +41,7 @@
#include <QtCore/qmap.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qtouchdevice_p.h>
+#include <QtGui/private/qevent_p.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickpointerhandler_p.h>
#include <QtQuick/private/qquickwindow_p.h>
@@ -1448,7 +1449,7 @@ QQuickPointerEvent *QQuickPointerScrollEvent::reset(QEvent *event)
m_synthSource = ev->source();
m_inverted = ev->inverted();
- m_point->reset(Qt::TouchPointMoved, ev->posF(), quint64(1) << 24, ev->timestamp()); // mouse has device ID 1
+ m_point->reset(Qt::TouchPointMoved, ev->position(), quint64(1) << 24, ev->timestamp()); // mouse has device ID 1
}
#endif
// TODO else if (event->type() == QEvent::Scroll) ...
@@ -1828,6 +1829,7 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i
// but that would require changing tst_qquickwindow::touchEvent_velocity(): it expects transformed velocity
bool anyPressOrReleaseInside = false;
+ bool anyStationaryWithModifiedPropertyInside = false;
bool anyGrabber = false;
QMatrix4x4 transformMatrix(QQuickItemPrivate::get(item)->windowToItemTransform());
for (int i = 0; i < m_pointCount; ++i) {
@@ -1860,6 +1862,8 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i
anyPressOrReleaseInside = true;
const QTouchEvent::TouchPoint *tp = touchPointById(p->pointId());
if (tp) {
+ if (isInside && tp->d->stationaryWithModifiedProperty)
+ anyStationaryWithModifiedPropertyInside = true;
eventStates |= tp->state();
QTouchEvent::TouchPoint tpCopy = *tp;
tpCopy.setPos(item->mapFromScene(tpCopy.scenePos()));
@@ -1873,7 +1877,8 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i
// Now touchPoints will have only points which are inside the item.
// But if none of them were just pressed inside, and the item has no other reason to care, ignore them anyway.
- if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty() || (!anyPressOrReleaseInside && !anyGrabber && !isFiltering))
+ if ((eventStates == Qt::TouchPointStationary && !anyStationaryWithModifiedPropertyInside) ||
+ touchPoints.isEmpty() || (!anyPressOrReleaseInside && !anyGrabber && !isFiltering))
return nullptr;
// if all points have the same state, set the event type accordingly
diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h
index e614b1bd6d..1a3737091f 100644
--- a/src/quick/items/qquickevents_p_p.h
+++ b/src/quick/items/qquickevents_p_p.h
@@ -605,6 +605,7 @@ private:
bool m_inverted = false;
friend class QQuickWindowPrivate;
+ friend class QQuickWheelHandler;
Q_DISABLE_COPY(QQuickPointerScrollEvent)
};
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 7e1f54f07e..d9ec7de611 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -266,9 +266,9 @@ void QQuickFlickablePrivate::init()
QQml_setParent_noEvent(contentItem, q);
contentItem->setParentItem(q);
qmlobject_connect(&timeline, QQuickTimeLine, SIGNAL(completed()),
- q, QQuickFlickable, SLOT(timelineCompleted()))
+ q, QQuickFlickable, SLOT(timelineCompleted()));
qmlobject_connect(&velocityTimeline, QQuickTimeLine, SIGNAL(completed()),
- q, QQuickFlickable, SLOT(velocityTimelineCompleted()))
+ q, QQuickFlickable, SLOT(velocityTimelineCompleted()));
q->setAcceptedMouseButtons(Qt::LeftButton);
q->setAcceptTouchEvents(false); // rely on mouse events synthesized from touch
q->setFiltersChildMouseEvents(true);
@@ -1489,7 +1489,7 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event)
d->vData.velocity = 0;
d->hData.velocity = 0;
d->timer.start();
- d->maybeBeginDrag(currentTimestamp, event->posF());
+ d->maybeBeginDrag(currentTimestamp, event->position());
break;
case Qt::NoScrollPhase: // default phase with an ordinary wheel mouse
case Qt::ScrollUpdate:
@@ -1571,7 +1571,8 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event)
QVector2D velocity(xDelta / elapsed, yDelta / elapsed);
d->lastPosTime = currentTimestamp;
d->accumulatedWheelPixelDelta += QVector2D(event->pixelDelta());
- d->drag(currentTimestamp, event->type(), event->posF(), d->accumulatedWheelPixelDelta, true, !d->scrollingPhase, true, velocity);
+ d->drag(currentTimestamp, event->type(), event->position(), d->accumulatedWheelPixelDelta,
+ true, !d->scrollingPhase, true, velocity);
event->accept();
}
diff --git a/src/quick/items/qquickflickablebehavior_p.h b/src/quick/items/qquickflickablebehavior_p.h
index ae7fe71359..fbce62f075 100644
--- a/src/quick/items/qquickflickablebehavior_p.h
+++ b/src/quick/items/qquickflickablebehavior_p.h
@@ -93,6 +93,11 @@
#define QML_FLICK_MULTIFLICK_THRESHOLD 1250
#endif
+// If the time (ms) between the last move and the release exceeds this, then velocity will be zero.
+#ifndef QML_FLICK_VELOCITY_DECAY_TIME
+#define QML_FLICK_VELOCITY_DECAY_TIME 50
+#endif
+
// Multiflick acceleration minimum contentSize/viewSize ratio
#ifndef QML_FLICK_MULTIFLICK_RATIO
#define QML_FLICK_MULTIFLICK_RATIO 10
diff --git a/src/quick/items/qquickframebufferobject.cpp b/src/quick/items/qquickframebufferobject.cpp
index 48f8b8db5c..190bc6853c 100644
--- a/src/quick/items/qquickframebufferobject.cpp
+++ b/src/quick/items/qquickframebufferobject.cpp
@@ -109,6 +109,10 @@ public:
* and can be used directly in \l {ShaderEffect}{ShaderEffects} and other
* classes that consume texture providers.
*
+ * \warning This class is only suitable when working directly with OpenGL. It
+ * is not compatible with the \l{Scene Graph Adaptations}{RHI-based rendering
+ * path}.
+ *
* \sa {Scene Graph - Rendering FBOs}, {Scene Graph and Rendering}
*/
diff --git a/src/quick/items/qquickframebufferobject.h b/src/quick/items/qquickframebufferobject.h
index d66ca40b3a..db143e48cf 100644
--- a/src/quick/items/qquickframebufferobject.h
+++ b/src/quick/items/qquickframebufferobject.h
@@ -44,11 +44,12 @@
QT_BEGIN_NAMESPACE
-
class QOpenGLFramebufferObject;
class QQuickFramebufferObjectPrivate;
class QSGFramebufferObjectNode;
+// ### Qt 6: To be removed. To be seen if an alternative will need to be introduced.
+
class Q_QUICK_EXPORT QQuickFramebufferObject : public QQuickItem
{
Q_OBJECT
diff --git a/src/quick/items/qquickgenericshadereffect.cpp b/src/quick/items/qquickgenericshadereffect.cpp
index 248c2b6ec3..df61ee853d 100644
--- a/src/quick/items/qquickgenericshadereffect.cpp
+++ b/src/quick/items/qquickgenericshadereffect.cpp
@@ -40,15 +40,32 @@
#include <private/qquickgenericshadereffect_p.h>
#include <private/qquickwindow_p.h>
#include <private/qquickitem_p.h>
-#include <QSignalMapper>
QT_BEGIN_NAMESPACE
-// The generic shader effect is used when the scenegraph backend indicates
-// SupportsShaderEffectNode. This, unlike the monolithic and interconnected (e.g.
-// with particles) OpenGL variant, passes most of the work to a scenegraph node
-// created via the adaptation layer, thus allowing different implementation in
-// the backends.
+namespace {
+class IntSignalMapper : public QObject
+{
+ Q_OBJECT
+
+ int value;
+public:
+ explicit IntSignalMapper(int v)
+ : QObject(nullptr), value(v) {}
+
+public Q_SLOTS:
+ void map() { emit mapped(value); }
+
+Q_SIGNALS:
+ void mapped(int);
+};
+} // unnamed namespace
+
+// The generic shader effect is used whenever on the RHI code path, or when the
+// scenegraph backend indicates SupportsShaderEffectNode. This, unlike the
+// monolithic and interconnected (e.g. with particles) OpenGL variant, passes
+// most of the work to a scenegraph node created via the adaptation layer, thus
+// allowing different implementation in the backends.
QQuickGenericShaderEffect::QQuickGenericShaderEffect(QQuickShaderEffect *item, QObject *parent)
: QObject(parent)
@@ -123,8 +140,8 @@ void QQuickGenericShaderEffect::setBlending(bool enable)
QVariant QQuickGenericShaderEffect::mesh() const
{
- return m_mesh ? qVariantFromValue(static_cast<QObject *>(m_mesh))
- : qVariantFromValue(m_meshResolution);
+ return m_mesh ? QVariant::fromValue(static_cast<QObject *>(m_mesh))
+ : QVariant::fromValue(m_meshResolution);
}
void QQuickGenericShaderEffect::setMesh(const QVariant &mesh)
@@ -254,6 +271,10 @@ QSGNode *QQuickGenericShaderEffect::handleUpdatePaintNode(QSGNode *oldNode, QQui
if (!node) {
QSGRenderContext *rc = QQuickWindowPrivate::get(m_item->window())->context;
node = rc->sceneGraphContext()->createShaderEffectNode(rc, mgr);
+ if (!node) {
+ qWarning("No shader effect node");
+ return nullptr;
+ }
m_dirty = QSGShaderEffectNode::DirtyShaderAll;
}
@@ -445,7 +466,7 @@ bool QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray
// provided and monitored like with an application-provided shader.
QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
v.name = QByteArrayLiteral("source");
- v.bindPoint = 0;
+ v.bindPoint = 0; // fake
v.type = texturesSeparate ? QSGGuiThreadShaderEffectManager::ShaderInfo::Texture
: QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler;
m_shaders[shaderType].shaderInfo.variables.append(v);
@@ -543,15 +564,10 @@ void QQuickGenericShaderEffect::updateShaderVars(Shader shaderType)
if (!mp.hasNotifySignal())
qWarning("ShaderEffect: property '%s' does not have notification method", v.name.constData());
- // Have a QSignalMapper that emits mapped() with an index+type on each property change notify signal.
+ // Have a IntSignalMapper that emits mapped() with an index+type on each property change notify signal.
auto &sm(m_signalMappers[shaderType][i]);
- if (!sm.mapper) {
-QT_WARNING_PUSH
-QT_WARNING_DISABLE_DEPRECATED
- sm.mapper = new QSignalMapper;
-QT_WARNING_POP
- sm.mapper->setMapping(m_item, i | (shaderType << 16));
- }
+ if (!sm.mapper)
+ sm.mapper = new IntSignalMapper(i | (shaderType << 16));
sm.active = true;
const QByteArray signalName = '2' + mp.notifySignal().methodSignature();
QObject::connect(m_item, signalName, sm.mapper, SLOT(map()));
@@ -559,7 +575,7 @@ QT_WARNING_POP
} else {
// Do not warn for dynamic properties.
if (!m_item->property(v.name.constData()).isValid())
- qWarning("ShaderEffect: '%s' does not have a matching property!", v.name.constData());
+ qWarning("ShaderEffect: '%s' does not have a matching property", v.name.constData());
}
vd.value = m_item->property(v.name.constData());
@@ -661,3 +677,4 @@ void QQuickGenericShaderEffect::markGeometryDirtyAndUpdateIfSupportsAtlas()
QT_END_NAMESPACE
#include "moc_qquickgenericshadereffect_p.cpp"
+#include "qquickgenericshadereffect.moc"
diff --git a/src/quick/items/qquickgenericshadereffect_p.h b/src/quick/items/qquickgenericshadereffect_p.h
index 3f6f92921b..368c32d2f8 100644
--- a/src/quick/items/qquickgenericshadereffect_p.h
+++ b/src/quick/items/qquickgenericshadereffect_p.h
@@ -59,8 +59,6 @@
QT_BEGIN_NAMESPACE
-class QSignalMapper;
-
class Q_QUICK_PRIVATE_EXPORT QQuickGenericShaderEffect : public QObject
{
Q_OBJECT
@@ -142,7 +140,7 @@ private:
struct SignalMapper {
SignalMapper() : mapper(nullptr), active(false) { }
- QSignalMapper *mapper;
+ QObject *mapper;
bool active;
};
QVector<SignalMapper> m_signalMappers[NShader];
diff --git a/src/quick/items/qquickgraphicsinfo.cpp b/src/quick/items/qquickgraphicsinfo.cpp
index cd1012c690..8f6f4386fb 100644
--- a/src/quick/items/qquickgraphicsinfo.cpp
+++ b/src/quick/items/qquickgraphicsinfo.cpp
@@ -96,6 +96,12 @@ QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object)
\li GraphicsInfo.Software - Qt Quick's software renderer based on QPainter with the raster paint engine
\li GraphicsInfo.OpenGL - OpenGL or OpenGL ES
\li GraphicsInfo.Direct3D12 - Direct3D 12
+ \li GraphicsInfo.OpenVG - OpenVG
+ \li GraphicsInfo.OpenGLRhi - OpenGL on top of QRhi, a graphics abstraction layer
+ \li GraphicsInfo.Direct3D11Rhi - Direct3D 11 on top of QRhi, a graphics abstraction layer
+ \li GraphicsInfo.VulkanRhi - Vulkan on top of QRhi, a graphics abstraction layer
+ \li GraphicsInfo.MetalRhi - Metal on top of QRhi, a graphics abstraction layer
+ \li GraphicsInfo.NullRhi - Null (no output) on top of QRhi, a graphics abstraction layer
\endlist
*/
@@ -109,6 +115,7 @@ QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object)
\li GraphicsInfo.UnknownShadingLanguage - Not yet known due to no window and scenegraph associated
\li GraphicsInfo.GLSL - GLSL or GLSL ES
\li GraphicsInfo.HLSL - HLSL
+ \li GraphicsInfo.RhiShader - QShader
\endlist
\note The value is only up-to-date once the item is associated with a
diff --git a/src/quick/items/qquickgraphicsinfo_p.h b/src/quick/items/qquickgraphicsinfo_p.h
index 9ef7bacb3e..f0a18c29cc 100644
--- a/src/quick/items/qquickgraphicsinfo_p.h
+++ b/src/quick/items/qquickgraphicsinfo_p.h
@@ -80,14 +80,21 @@ public:
Unknown = QSGRendererInterface::Unknown,
Software = QSGRendererInterface::Software,
OpenGL = QSGRendererInterface::OpenGL,
- Direct3D12 = QSGRendererInterface::Direct3D12
+ Direct3D12 = QSGRendererInterface::Direct3D12,
+ OpenVG = QSGRendererInterface::OpenVG,
+ OpenGLRhi = QSGRendererInterface::OpenGLRhi,
+ Direct3D11Rhi = QSGRendererInterface::Direct3D11Rhi,
+ VulkanRhi = QSGRendererInterface::VulkanRhi,
+ MetalRhi = QSGRendererInterface::MetalRhi,
+ NullRhi = QSGRendererInterface::NullRhi
};
Q_ENUM(GraphicsApi)
enum ShaderType {
UnknownShadingLanguage = QSGRendererInterface::UnknownShadingLanguage,
GLSL = QSGRendererInterface::GLSL,
- HLSL = QSGRendererInterface::HLSL
+ HLSL = QSGRendererInterface::HLSL,
+ RhiShader = QSGRendererInterface::RhiShader
};
Q_ENUM(ShaderType)
diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp
index 16343774e2..840cfe15da 100644
--- a/src/quick/items/qquickimage.cpp
+++ b/src/quick/items/qquickimage.cpp
@@ -663,7 +663,7 @@ QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
QSGInternalImageNode *node = static_cast<QSGInternalImageNode *>(oldNode);
if (!node) {
d->pixmapChanged = true;
- node = d->sceneGraphContext()->createInternalImageNode();
+ node = d->sceneGraphContext()->createInternalImageNode(d->sceneGraphRenderContext());
}
QRectF targetRect;
@@ -687,7 +687,6 @@ QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
yOffset = qCeil(height() - pixHeight);
switch (d->fillMode) {
- default:
case Stretch:
targetRect = QRectF(0, 0, width(), height());
sourceRect = d->pix.rect();
@@ -699,7 +698,7 @@ QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
break;
case PreserveAspectCrop: {
- targetRect = QRect(0, 0, width(), height());
+ targetRect = QRectF(0, 0, width(), height());
qreal wscale = width() / qreal(d->pix.width());
qreal hscale = height() / qreal(d->pix.height());
@@ -751,7 +750,7 @@ QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
targetRect = QRectF(x + xOffset, y + yOffset, w, h);
sourceRect = QRectF(x, y, w, h);
break;
- };
+ }
qreal nsWidth = (hWrap == QSGTexture::Repeat || d->fillMode == Pad) ? d->pix.width() / d->devicePixelRatio : d->pix.width();
qreal nsHeight = (vWrap == QSGTexture::Repeat || d->fillMode == Pad) ? d->pix.height() / d->devicePixelRatio : d->pix.height();
@@ -889,4 +888,16 @@ void QQuickImage::setMipmap(bool use)
By default, this property is set to false.
*/
+/*!
+ \qmlproperty int QtQuick::Image::currentFrame
+ \qmlproperty int QtQuick::Image::frameCount
+ \since 5.14
+
+ currentFrame is the frame that is currently visible. The default is \c 0.
+ You can set it to a number between \c 0 and \c {frameCount - 1} to display a
+ different frame, if the image contains multiple frames.
+
+ frameCount is the number of frames in the image. Most images have only one frame.
+*/
+
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickimage_p.h b/src/quick/items/qquickimage_p.h
index 7fb4413900..257cde5313 100644
--- a/src/quick/items/qquickimage_p.h
+++ b/src/quick/items/qquickimage_p.h
@@ -66,8 +66,8 @@ class Q_QUICK_PRIVATE_EXPORT QQuickImage : public QQuickImageBase
Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedGeometryChanged)
Q_PROPERTY(HAlignment horizontalAlignment READ horizontalAlignment WRITE setHorizontalAlignment NOTIFY horizontalAlignmentChanged)
Q_PROPERTY(VAlignment verticalAlignment READ verticalAlignment WRITE setVerticalAlignment NOTIFY verticalAlignmentChanged)
- Q_PROPERTY(bool mipmap READ mipmap WRITE setMipmap NOTIFY mipmapChanged REVISION 1)
- Q_PROPERTY(bool autoTransform READ autoTransform WRITE setAutoTransform NOTIFY autoTransformChanged REVISION 2)
+ Q_PROPERTY(bool mipmap READ mipmap WRITE setMipmap NOTIFY mipmapChanged REVISION 3)
+ Q_PROPERTY(bool autoTransform READ autoTransform WRITE setAutoTransform NOTIFY autoTransformChanged REVISION 5)
public:
QQuickImage(QQuickItem *parent=nullptr);
@@ -112,8 +112,8 @@ Q_SIGNALS:
void paintedGeometryChanged();
void horizontalAlignmentChanged(HAlignment alignment);
void verticalAlignmentChanged(VAlignment alignment);
- Q_REVISION(1) void mipmapChanged(bool);
- Q_REVISION(2) void autoTransformChanged();
+ Q_REVISION(3) void mipmapChanged(bool);
+ Q_REVISION(5) void autoTransformChanged();
private Q_SLOTS:
void invalidateSceneGraph();
diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp
index 74ee859f77..8bab14bfd1 100644
--- a/src/quick/items/qquickimagebase.cpp
+++ b/src/quick/items/qquickimagebase.cpp
@@ -46,6 +46,8 @@
#include <QtQml/qqmlinfo.h>
#include <QtQml/qqmlfile.h>
+#include <QtQml/qqmlabstracturlinterceptor.h>
+
QT_BEGIN_NAMESPACE
@@ -53,7 +55,7 @@ QT_BEGIN_NAMESPACE
// if they're not happy with our implementation of it.
bool QQuickImageBasePrivate::updateDevicePixelRatio(qreal targetDevicePixelRatio)
{
- // QQuickImageProvider and SVG can generate a high resolution image when
+ // QQuickImageProvider and SVG and PDF can generate a high resolution image when
// sourceSize is set (this function is only called if it's set).
// If sourceSize is not set then the provider default size will be used, as usual.
bool setDevicePixelRatio = false;
@@ -62,7 +64,8 @@ bool QQuickImageBasePrivate::updateDevicePixelRatio(qreal targetDevicePixelRatio
} else {
QString stringUrl = url.path(QUrl::PrettyDecoded);
if (stringUrl.endsWith(QLatin1String("svg")) ||
- stringUrl.endsWith(QLatin1String("svgz"))) {
+ stringUrl.endsWith(QLatin1String("svgz")) ||
+ stringUrl.endsWith(QLatin1String("pdf"))) {
setDevicePixelRatio = true;
}
}
@@ -208,6 +211,36 @@ bool QQuickImageBase::mirror() const
return d->mirror;
}
+void QQuickImageBase::setCurrentFrame(int frame)
+{
+ Q_D(QQuickImageBase);
+ if (frame == d->currentFrame || frame < 0 || (isComponentComplete() && frame >= d->pix.frameCount()))
+ return;
+
+ d->currentFrame = frame;
+
+ if (isComponentComplete()) {
+ if (frame > 0)
+ d->cache = false;
+ load();
+ update();
+ }
+
+ emit currentFrameChanged();
+}
+
+int QQuickImageBase::currentFrame() const
+{
+ Q_D(const QQuickImageBase);
+ return d->currentFrame;
+}
+
+int QQuickImageBase::frameCount() const
+{
+ Q_D(const QQuickImageBase);
+ return d->frameCount;
+}
+
void QQuickImageBase::load()
{
Q_D(QQuickImageBase);
@@ -244,6 +277,9 @@ void QQuickImageBase::load()
d->devicePixelRatio = 1.0;
QUrl loadUrl = d->url;
+ QQmlEngine* engine = qmlEngine(this);
+ if (engine && engine->urlInterceptor())
+ loadUrl = engine->urlInterceptor()->intercept(loadUrl, QQmlAbstractUrlInterceptor::UrlString);
bool updatedDevicePixelRatio = false;
if (d->sourcesize.isValid())
@@ -255,7 +291,7 @@ void QQuickImageBase::load()
resolve2xLocalFile(d->url, targetDevicePixelRatio, &loadUrl, &d->devicePixelRatio);
}
- d->pix.load(qmlEngine(this), loadUrl, d->sourcesize * d->devicePixelRatio, options, d->providerOptions);
+ d->pix.load(qmlEngine(this), loadUrl, d->sourcesize * d->devicePixelRatio, options, d->providerOptions, d->currentFrame, d->frameCount);
if (d->pix.isLoading()) {
if (d->progress != 0.0) {
@@ -314,6 +350,11 @@ void QQuickImageBase::requestFinished()
d->oldAutoTransform = autoTransform();
emitAutoTransformBaseChanged();
}
+ if (d->frameCount != d->pix.frameCount()) {
+ d->frameCount = d->pix.frameCount();
+ emit frameCountChanged();
+ }
+
update();
}
diff --git a/src/quick/items/qquickimagebase_p.h b/src/quick/items/qquickimagebase_p.h
index d8d0be9b8c..8cd59c8cea 100644
--- a/src/quick/items/qquickimagebase_p.h
+++ b/src/quick/items/qquickimagebase_p.h
@@ -68,6 +68,8 @@ class Q_QUICK_PRIVATE_EXPORT QQuickImageBase : public QQuickImplicitSizeItem
Q_PROPERTY(bool cache READ cache WRITE setCache NOTIFY cacheChanged)
Q_PROPERTY(QSize sourceSize READ sourceSize WRITE setSourceSize RESET resetSourceSize NOTIFY sourceSizeChanged)
Q_PROPERTY(bool mirror READ mirror WRITE setMirror NOTIFY mirrorChanged)
+ Q_PROPERTY(int currentFrame READ currentFrame WRITE setCurrentFrame NOTIFY currentFrameChanged REVISION 14)
+ Q_PROPERTY(int frameCount READ frameCount NOTIFY frameCountChanged REVISION 14)
public:
QQuickImageBase(QQuickItem *parent=nullptr);
@@ -95,6 +97,11 @@ public:
virtual void setMirror(bool mirror);
bool mirror() const;
+ virtual void setCurrentFrame(int frame);
+ virtual int currentFrame() const;
+
+ virtual int frameCount() const;
+
virtual void setAutoTransform(bool transform);
bool autoTransform() const;
@@ -112,6 +119,8 @@ Q_SIGNALS:
void asynchronousChanged();
void cacheChanged();
void mirrorChanged();
+ Q_REVISION(14) void currentFrameChanged();
+ Q_REVISION(14) void frameCountChanged();
protected:
virtual void load();
diff --git a/src/quick/items/qquickimagebase_p_p.h b/src/quick/items/qquickimagebase_p_p.h
index 1b771166a2..88e18ba32e 100644
--- a/src/quick/items/qquickimagebase_p_p.h
+++ b/src/quick/items/qquickimagebase_p_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -68,6 +68,8 @@ public:
: status(QQuickImageBase::Null),
progress(0.0),
devicePixelRatio(1.0),
+ currentFrame(0),
+ frameCount(0),
async(false),
cache(true),
mirror(false),
@@ -85,6 +87,8 @@ public:
QSize oldSourceSize;
qreal devicePixelRatio;
QQuickImageProviderOptions providerOptions;
+ int currentFrame;
+ int frameCount;
bool async : 1;
bool cache : 1;
bool mirror: 1;
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index d90cc71997..4c20b7e2e1 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -4177,7 +4177,7 @@ void QQuickItem::hoverLeaveEvent(QHoverEvent *event)
Q_UNUSED(event);
}
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
/*!
This event handler can be reimplemented in a subclass to receive drag-enter
events for an item. The event information is provided by the
@@ -4245,7 +4245,7 @@ void QQuickItem::dropEvent(QDropEvent *event)
{
Q_UNUSED(event);
}
-#endif // draganddrop
+#endif // quick_draganddrop
/*!
Reimplement this method to filter the mouse events that are received by
@@ -5097,7 +5097,7 @@ QQuickStateGroup *QQuickItemPrivate::_states()
if (!componentComplete)
_stateGroup->classBegin();
qmlobject_connect(_stateGroup, QQuickStateGroup, SIGNAL(stateChanged(QString)),
- q, QQuickItem, SIGNAL(stateChanged(QString)))
+ q, QQuickItem, SIGNAL(stateChanged(QString)));
}
return _stateGroup;
@@ -5136,6 +5136,40 @@ void QQuickItemPrivate::transformChanged()
#endif
}
+QPointF QQuickItemPrivate::adjustedPosForTransform(const QPointF &centroidParentPos,
+ const QPointF &startPos,
+ const QVector2D &activeTranslation, //[0,0] means no additional translation from startPos
+ qreal startScale,
+ qreal activeScale, // 1.0 means no additional scale from startScale
+ qreal startRotation,
+ qreal activeRotation) // 0.0 means no additional rotation from startRotation
+{
+ Q_Q(QQuickItem);
+ QVector3D xformOrigin(q->transformOriginPoint());
+ QMatrix4x4 startMatrix;
+ startMatrix.translate(float(startPos.x()), float(startPos.y()));
+ startMatrix.translate(xformOrigin);
+ startMatrix.scale(float(startScale));
+ startMatrix.rotate(float(startRotation), 0, 0, -1);
+ startMatrix.translate(-xformOrigin);
+
+ const QVector3D centroidParentVector(centroidParentPos);
+ QMatrix4x4 mat;
+ mat.translate(centroidParentVector);
+ mat.rotate(float(activeRotation), 0, 0, 1);
+ mat.scale(float(activeScale));
+ mat.translate(-centroidParentVector);
+ mat.translate(QVector3D(activeTranslation));
+
+ mat = mat * startMatrix;
+
+ QPointF xformOriginPoint = q->transformOriginPoint();
+ QPointF pos = mat * xformOriginPoint;
+ pos -= xformOriginPoint;
+
+ return pos;
+}
+
bool QQuickItemPrivate::filterKeyEvent(QKeyEvent *e, bool post)
{
if (!extra.isAllocated() || !extra->keyHandler)
@@ -5666,6 +5700,7 @@ void QQuickItem::setRotation(qreal r)
color: "red"
x: 25; y: 25; width: 50; height: 50
scale: 1.4
+ transformOrigin: Item.TopLeft
}
}
\endqml
@@ -8098,7 +8133,7 @@ bool QQuickItem::event(QEvent *ev)
wheelEvent(static_cast<QWheelEvent*>(ev));
break;
#endif
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
case QEvent::DragEnter:
dragEnterEvent(static_cast<QDragEnterEvent*>(ev));
break;
@@ -8111,7 +8146,7 @@ bool QQuickItem::event(QEvent *ev)
case QEvent::Drop:
dropEvent(static_cast<QDropEvent*>(ev));
break;
-#endif // draganddrop
+#endif // quick_draganddrop
#if QT_CONFIG(gestures)
case QEvent::NativeGesture:
ev->ignore();
@@ -8373,7 +8408,7 @@ void QQuickItemLayer::activateEffect()
m_effect->stackAfter(m_effectSource);
}
m_effect->setVisible(m_item->isVisible());
- m_effect->setProperty(m_name, qVariantFromValue<QObject *>(m_effectSource));
+ m_effect->setProperty(m_name, QVariant::fromValue<QObject *>(m_effectSource));
QQuickItemPrivate::get(m_effect)->setTransparentForPositioner(true);
m_effectComponent->completeCreate();
}
@@ -8513,7 +8548,11 @@ void QQuickItemLayer::setSourceRect(const QRectF &sourceRect)
/*!
\qmlproperty bool QtQuick::Item::layer.smooth
- Holds whether the layer is smoothly transformed.
+ Holds whether the layer is smoothly transformed. When enabled, sampling the
+ layer's texture is performed using \c linear interpolation, while
+ non-smooth results in using the \c nearest filtering mode.
+
+ By default, this property is set to \c false.
\sa {Item Layers}
*/
@@ -8670,7 +8709,7 @@ void QQuickItemLayer::setName(const QByteArray &name) {
return;
if (m_effect) {
m_effect->setProperty(m_name, QVariant());
- m_effect->setProperty(name, qVariantFromValue<QObject *>(m_effectSource));
+ m_effect->setProperty(name, QVariant::fromValue<QObject *>(m_effectSource));
}
m_name = name;
emit nameChanged(name);
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index 6f601e0872..394a5adb8c 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -318,7 +318,7 @@ public:
void setKeepTouchGrab(bool);
// implemented in qquickitemgrabresult.cpp
- Q_REVISION(2) Q_INVOKABLE bool grabToImage(const QJSValue &callback, const QSize &targetSize = QSize());
+ Q_REVISION(4) Q_INVOKABLE bool grabToImage(const QJSValue &callback, const QSize &targetSize = QSize());
QSharedPointer<QQuickItemGrabResult> grabToImage(const QSize &targetSize = QSize());
Q_INVOKABLE virtual bool contains(const QPointF &point) const;
@@ -433,7 +433,7 @@ protected:
virtual void hoverEnterEvent(QHoverEvent *event);
virtual void hoverMoveEvent(QHoverEvent *event);
virtual void hoverLeaveEvent(QHoverEvent *event);
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
virtual void dragEnterEvent(QDragEnterEvent *);
virtual void dragMoveEvent(QDragMoveEvent *);
virtual void dragLeaveEvent(QDragLeaveEvent *);
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index f618bcf1c3..3e8feec4bf 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -592,6 +592,11 @@ public:
QPointF computeTransformOrigin() const;
virtual void transformChanged();
+ QPointF adjustedPosForTransform(const QPointF &centroid,
+ const QPointF &startPos, const QVector2D &activeTranslatation,
+ qreal startScale, qreal activeScale,
+ qreal startRotation, qreal activeRotation);
+
void deliverKeyEvent(QKeyEvent *);
bool filterKeyEvent(QKeyEvent *, bool post);
#if QT_CONFIG(im)
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index 77bcf2583c..7bc6eefe0a 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -110,10 +110,11 @@
#include "qquickshadereffect_p.h"
#include "qquickshadereffectmesh_p.h"
#endif
+#if QT_CONFIG(quick_draganddrop)
#include "qquickdrag_p.h"
#include "qquickdroparea_p.h"
+#endif
#include "qquickmultipointtoucharea_p.h"
-#include <private/qqmlmetatype_p.h>
#include <QtQuick/private/qquickaccessibleattached_p.h>
#include "handlers/qquickdraghandler_p.h"
@@ -121,6 +122,7 @@
#include "handlers/qquickpinchhandler_p.h"
#include "handlers/qquickpointhandler_p.h"
#include "handlers/qquicktaphandler_p.h"
+#include "handlers/qquickwheelhandler_p.h"
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcTransient)
@@ -224,6 +226,9 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickPathCatmullRomCurve>("QtQuick",2,0,"PathCurve");
qmlRegisterType<QQuickPathArc>("QtQuick",2,0,"PathArc");
qmlRegisterType<QQuickPathSvg>("QtQuick",2,0,"PathSvg");
+ qmlRegisterType<QQuickPath, 14>(uri, 2, 14, "Path");
+ qmlRegisterType<QQuickPathPolyline>("QtQuick", 2, 14, "PathPolyline");
+ qmlRegisterType<QQuickPathMultiline>("QtQuick", 2, 14, "PathMultiline");
#endif
#if QT_CONFIG(quick_pathview)
qmlRegisterType<QQuickPathView>(uri,major,minor,"PathView");
@@ -241,28 +246,28 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickTextEdit,1>(uri,2,1,"TextEdit");
qmlRegisterType<QQuickTextInput>(uri,major,minor,"TextInput");
qmlRegisterType<QQuickTextInput,2>(uri,2,2,"TextInput");
- qmlRegisterType<QQuickTextInput,3>(uri,2,4,"TextInput");
- qmlRegisterType<QQuickItemGrabResult>();
+ qmlRegisterType<QQuickTextInput,4>(uri,2,4,"TextInput");
+ qmlRegisterAnonymousType<QQuickItemGrabResult>(uri, major);
#if QT_CONFIG(quick_shadereffect)
- qmlRegisterType<QQuickItemLayer>();
-#endif
- qmlRegisterType<QQuickAnchors>();
- qmlRegisterType<QQuickKeyEvent>();
- qmlRegisterType<QQuickMouseEvent>();
- qmlRegisterType<QQuickWheelEvent>();
- qmlRegisterType<QQuickCloseEvent>();
- qmlRegisterType<QQuickTransform>();
+ qmlRegisterAnonymousType<QQuickItemLayer>(uri, major);
+#endif
+ qmlRegisterAnonymousType<QQuickAnchors>(uri, major);
+ qmlRegisterAnonymousType<QQuickKeyEvent>(uri, major);
+ qmlRegisterAnonymousType<QQuickMouseEvent>(uri, major);
+ qmlRegisterAnonymousType<QQuickWheelEvent>(uri, major);
+ qmlRegisterAnonymousType<QQuickCloseEvent>(uri, major);
+ qmlRegisterAnonymousType<QQuickTransform>(uri, major);
#if QT_CONFIG(quick_path)
- qmlRegisterType<QQuickPathElement>();
- qmlRegisterType<QQuickCurve>();
+ qmlRegisterAnonymousType<QQuickPathElement>(uri, major);
+ qmlRegisterAnonymousType<QQuickCurve>(uri, major);
#endif
- qmlRegisterType<QQuickScaleGrid>();
- qmlRegisterType<QQuickTextLine>();
- qmlRegisterType<QQuickPen>();
- qmlRegisterType<QQuickFlickableVisibleArea>();
+ qmlRegisterAnonymousType<QQuickScaleGrid>(uri, major);
+ qmlRegisterAnonymousType<QQuickTextLine>(uri, major);
+ qmlRegisterAnonymousType<QQuickPen>(uri, major);
+ qmlRegisterAnonymousType<QQuickFlickableVisibleArea>(uri, major);
qRegisterMetaType<QQuickAnchorLine>("QQuickAnchorLine");
- qmlRegisterType<QQuickTextDocument>();
+ qmlRegisterAnonymousType<QQuickTextDocument>(uri, major);
qmlRegisterUncreatableType<QQuickKeyNavigationAttached>(uri,major,minor,"KeyNavigation",QQuickKeyNavigationAttached::tr("KeyNavigation is only available via attached properties"));
@@ -274,7 +279,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickPinchArea>(uri,major,minor,"PinchArea");
qmlRegisterType<QQuickPinch>(uri,major,minor,"Pinch");
- qmlRegisterType<QQuickPinchEvent>();
+ qmlRegisterAnonymousType<QQuickPinchEvent>(uri, major);
#if QT_CONFIG(quick_shadereffect)
qmlRegisterType<QQuickShaderEffectSource>("QtQuick", 2, 0, "ShaderEffectSource");
@@ -297,7 +302,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickParentChange>(uri, major, minor,"ParentChange");
qmlRegisterType<QQuickAnchorChanges>(uri, major, minor,"AnchorChanges");
- qmlRegisterType<QQuickAnchorSet>();
+ qmlRegisterAnonymousType<QQuickAnchorSet>(uri, major);
qmlRegisterType<QQuickAnchorAnimation>(uri, major, minor,"AnchorAnimation");
qmlRegisterType<QQuickParentAnimation>(uri, major, minor,"ParentAnimation");
#if QT_CONFIG(quick_path)
@@ -305,10 +310,10 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickPathInterpolator>("QtQuick",2,0,"PathInterpolator");
#endif
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
qmlRegisterType<QQuickDropArea>("QtQuick", 2, 0, "DropArea");
- qmlRegisterType<QQuickDropEvent>();
- qmlRegisterType<QQuickDropAreaDrag>();
+ qmlRegisterAnonymousType<QQuickDropEvent>(uri, 2);
+ qmlRegisterAnonymousType<QQuickDropAreaDrag>(uri, 2);
qmlRegisterUncreatableType<QQuickDrag>("QtQuick", 2, 0, "Drag", QQuickDragAttached::tr("Drag is only available via attached properties"));
#endif
@@ -329,7 +334,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
const char *itemViewName = "ItemView";
const QString itemViewMessage = QQuickItemView::tr("ItemView is an abstract base class");
qmlRegisterUncreatableType<QQuickItemView, 1>(uri, 2, 1, itemViewName, itemViewMessage);
- qmlRegisterUncreatableType<QQuickItemView, 2>(uri, 2, 3, itemViewName, itemViewMessage);
+ qmlRegisterUncreatableType<QQuickItemView, 3>(uri, 2, 3, itemViewName, itemViewMessage);
#endif
#if QT_CONFIG(quick_listview)
qmlRegisterType<QQuickListView, 1>(uri, 2, 1, "ListView");
@@ -344,23 +349,23 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickText, 3>(uri, 2, 3, "Text");
qmlRegisterType<QQuickTextEdit, 3>(uri, 2, 3, "TextEdit");
- qmlRegisterType<QQuickImage, 1>(uri, 2, 3,"Image");
+ qmlRegisterType<QQuickImage, 3>(uri, 2, 3,"Image");
- qmlRegisterType<QQuickItem, 2>(uri, 2, 4, "Item");
+ qmlRegisterType<QQuickItem, 4>(uri, 2, 4, "Item");
#if QT_CONFIG(quick_listview)
- qmlRegisterType<QQuickListView, 2>(uri, 2, 4, "ListView");
+ qmlRegisterType<QQuickListView, 4>(uri, 2, 4, "ListView");
#endif
- qmlRegisterType<QQuickMouseArea, 1>(uri, 2, 4, "MouseArea");
+ qmlRegisterType<QQuickMouseArea, 4>(uri, 2, 4, "MouseArea");
#if QT_CONFIG(quick_shadereffect)
- qmlRegisterType<QQuickShaderEffect, 1>(uri, 2, 4, "ShaderEffect");
+ qmlRegisterType<QQuickShaderEffect, 4>(uri, 2, 4, "ShaderEffect");
#endif
#if QT_CONFIG(opengl)
qmlRegisterUncreatableType<QQuickOpenGLInfo>(uri, 2, 4,"OpenGLInfo", QQuickOpenGLInfo::tr("OpenGLInfo is only available via attached properties"));
#endif
- qmlRegisterType<QQuickPinchArea, 1>(uri, 2, 5,"PinchArea");
- qmlRegisterType<QQuickImage, 2>(uri, 2, 5,"Image");
- qmlRegisterType<QQuickMouseArea, 2>(uri, 2, 5, "MouseArea");
+ qmlRegisterType<QQuickPinchArea, 5>(uri, 2, 5,"PinchArea");
+ qmlRegisterType<QQuickImage, 5>(uri, 2, 5,"Image");
+ qmlRegisterType<QQuickMouseArea, 5>(uri, 2, 5, "MouseArea");
qmlRegisterType<QQuickText, 6>(uri, 2, 6, "Text");
qmlRegisterType<QQuickTextEdit, 6>(uri, 2, 6, "TextEdit");
@@ -376,7 +381,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterUncreatableType<QQuickEnterKeyAttached, 6>(uri, 2, 6, "EnterKey",
QQuickEnterKeyAttached::tr("EnterKey is only available via attached properties"));
#if QT_CONFIG(quick_shadereffect)
- qmlRegisterType<QQuickShaderEffectSource, 1>(uri, 2, 6, "ShaderEffectSource");
+ qmlRegisterType<QQuickShaderEffectSource, 6>(uri, 2, 6, "ShaderEffectSource");
#endif
qmlRegisterType<QQuickItem, 7>(uri, 2, 7, "Item");
@@ -406,7 +411,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickMouseArea, 9>(uri, 2, 9, "MouseArea");
#if QT_CONFIG(quick_path)
- qmlRegisterType<QQuickPathArc, 2>(uri, 2, 9, "PathArc");
+ qmlRegisterType<QQuickPathArc, 9>(uri, 2, 9, "PathArc");
qmlRegisterType<QQuickPathMove>(uri, 2, 9, "PathMove");
#endif
@@ -421,7 +426,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
#endif
#if QT_CONFIG(quick_shadereffect)
- qmlRegisterType<QQuickShaderEffectSource, 2>(uri, 2, 9, "ShaderEffectSource");
+ qmlRegisterType<QQuickShaderEffectSource, 9>(uri, 2, 9, "ShaderEffectSource");
#endif
qmlRegisterType<QQuickFlickable, 10>(uri, 2, 10, "Flickable");
@@ -475,9 +480,25 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickTableView>(uri, 2, 12, "TableView");
#endif
+#if QT_CONFIG(quick_itemview)
qmlRegisterUncreatableType<QQuickItemView, 13>(uri, 2, 13, itemViewName, itemViewMessage);
+#endif
+#if QT_CONFIG(quick_pathview)
qmlRegisterType<QQuickPathView, 13>(uri, 2, 13, "PathView");
+#endif
+#if QT_CONFIG(quick_gridview)
qmlRegisterType<QQuickGridView, 13>(uri, 2, 13, "GridView");
+#endif
+#if QT_CONFIG(quick_tableview)
+ qmlRegisterType<QQuickTableView, 14>(uri, 2, 14, "TableView");
+#endif
+#if QT_CONFIG(wheelevent)
+ qmlRegisterType<QQuickWheelHandler>(uri, 2, 14, "WheelHandler");
+#endif
+ qmlRegisterUncreatableType<QQuickImageBase, 14>(uri, 2, 14, "ImageBase",
+ QQuickPointerHandler::tr("ImageBase is an abstract base class"));
+ qmlRegisterType<QQuickImage, 14>(uri, 2, 14, "Image");
+ qmlRegisterType<QQuickDragHandler, 14>(uri, 2, 14, "DragHandler");
}
static void initResources()
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index 120eeb13d5..661f19509a 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -247,6 +247,8 @@ void QQuickItemView::setModel(const QVariant &m)
connect(d->model, SIGNAL(modelUpdated(QQmlChangeSet,bool)),
this, SLOT(modelUpdated(QQmlChangeSet,bool)));
+ if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel*>(d->model))
+ QObjectPrivate::connect(dataModel, &QQmlDelegateModel::delegateChanged, d, &QQuickItemViewPrivate::applyDelegateChange);
emit countChanged();
}
emit modelChanged();
@@ -277,22 +279,8 @@ void QQuickItemView::setDelegate(QQmlComponent *delegate)
if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel*>(d->model)) {
int oldCount = dataModel->count();
dataModel->setDelegate(delegate);
- if (isComponentComplete()) {
- d->releaseVisibleItems();
- d->releaseItem(d->currentItem);
- d->currentItem = nullptr;
- d->updateSectionCriteria();
- d->refill();
- d->moveReason = QQuickItemViewPrivate::SetIndex;
- d->updateCurrent(d->currentIndex);
- if (d->highlight && d->currentItem) {
- if (d->autoHighlight)
- d->resetHighlightPosition();
- d->updateTrackedItem();
- }
- d->moveReason = QQuickItemViewPrivate::Other;
- d->updateViewport();
- }
+ if (isComponentComplete())
+ d->applyDelegateChange();
if (oldCount != dataModel->count())
emit countChanged();
}
@@ -1098,6 +1086,24 @@ qreal QQuickItemViewPrivate::calculatedMaxExtent() const
return maxExtent;
}
+void QQuickItemViewPrivate::applyDelegateChange()
+{
+ releaseVisibleItems();
+ releaseItem(currentItem);
+ currentItem = nullptr;
+ updateSectionCriteria();
+ refill();
+ moveReason = QQuickItemViewPrivate::SetIndex;
+ updateCurrent(currentIndex);
+ if (highlight && currentItem) {
+ if (autoHighlight)
+ resetHighlightPosition();
+ updateTrackedItem();
+ }
+ moveReason = QQuickItemViewPrivate::Other;
+ updateViewport();
+}
+
// for debugging only
void QQuickItemViewPrivate::checkVisible() const
{
diff --git a/src/quick/items/qquickitemview_p.h b/src/quick/items/qquickitemview_p.h
index 0a0da587b4..66e09f9ed1 100644
--- a/src/quick/items/qquickitemview_p.h
+++ b/src/quick/items/qquickitemview_p.h
@@ -81,8 +81,8 @@ class Q_QUICK_PRIVATE_EXPORT QQuickItemView : public QQuickFlickable
Q_PROPERTY(bool keyNavigationWraps READ isWrapEnabled WRITE setWrapEnabled NOTIFY keyNavigationWrapsChanged)
Q_PROPERTY(bool keyNavigationEnabled READ isKeyNavigationEnabled WRITE setKeyNavigationEnabled NOTIFY keyNavigationEnabledChanged REVISION 7)
Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged)
- Q_PROPERTY(int displayMarginBeginning READ displayMarginBeginning WRITE setDisplayMarginBeginning NOTIFY displayMarginBeginningChanged REVISION 2)
- Q_PROPERTY(int displayMarginEnd READ displayMarginEnd WRITE setDisplayMarginEnd NOTIFY displayMarginEndChanged REVISION 2)
+ Q_PROPERTY(int displayMarginBeginning READ displayMarginBeginning WRITE setDisplayMarginBeginning NOTIFY displayMarginBeginningChanged REVISION 3)
+ Q_PROPERTY(int displayMarginEnd READ displayMarginEnd WRITE setDisplayMarginEnd NOTIFY displayMarginEndChanged REVISION 3)
Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged)
Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged)
diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h
index 1f42c847b3..6442fee27d 100644
--- a/src/quick/items/qquickitemview_p_p.h
+++ b/src/quick/items/qquickitemview_p_p.h
@@ -59,9 +59,9 @@ QT_REQUIRE_CONFIG(quick_itemview);
#include "qquickitemviewfxitem_p_p.h"
#include "qquickitemviewtransition_p.h"
#include "qquickflickable_p_p.h"
-#include <QtQml/private/qqmlobjectmodel_p.h>
-#include <QtQml/private/qqmldelegatemodel_p.h>
-#include <QtQml/private/qqmlchangeset_p.h>
+#include <QtQmlModels/private/qqmlobjectmodel_p.h>
+#include <QtQmlModels/private/qqmldelegatemodel_p.h>
+#include <QtQmlModels/private/qqmlchangeset_p.h>
QT_BEGIN_NAMESPACE
@@ -191,6 +191,8 @@ public:
qreal calculatedMinExtent() const;
qreal calculatedMaxExtent() const;
+ void applyDelegateChange();
+
void applyPendingChanges();
bool applyModelChanges(ChangeResult *insertionResult, ChangeResult *removalResult);
bool applyRemovalChange(const QQmlChangeSet::Change &removal, ChangeResult *changeResult, int *removedCount);
diff --git a/src/quick/items/qquicklistview_p.h b/src/quick/items/qquicklistview_p.h
index 9a9b325b1e..f2bab9e018 100644
--- a/src/quick/items/qquicklistview_p.h
+++ b/src/quick/items/qquicklistview_p.h
@@ -126,8 +126,8 @@ class Q_QUICK_PRIVATE_EXPORT QQuickListView : public QQuickItemView
Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged)
- Q_PROPERTY(HeaderPositioning headerPositioning READ headerPositioning WRITE setHeaderPositioning NOTIFY headerPositioningChanged REVISION 2)
- Q_PROPERTY(FooterPositioning footerPositioning READ footerPositioning WRITE setFooterPositioning NOTIFY footerPositioningChanged REVISION 2)
+ Q_PROPERTY(HeaderPositioning headerPositioning READ headerPositioning WRITE setHeaderPositioning NOTIFY headerPositioningChanged REVISION 4)
+ Q_PROPERTY(FooterPositioning footerPositioning READ footerPositioning WRITE setFooterPositioning NOTIFY footerPositioningChanged REVISION 4)
Q_CLASSINFO("DefaultProperty", "data")
@@ -188,8 +188,8 @@ Q_SIGNALS:
void highlightResizeVelocityChanged();
void highlightResizeDurationChanged();
void snapModeChanged();
- Q_REVISION(2) void headerPositioningChanged();
- Q_REVISION(2) void footerPositioningChanged();
+ Q_REVISION(4) void headerPositioningChanged();
+ Q_REVISION(4) void footerPositioningChanged();
protected:
void viewportMoved(Qt::Orientations orient) override;
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp
index 4bee4864d5..ddf34798d7 100644
--- a/src/quick/items/qquickmousearea.cpp
+++ b/src/quick/items/qquickmousearea.cpp
@@ -40,7 +40,9 @@
#include "qquickmousearea_p.h"
#include "qquickmousearea_p_p.h"
#include "qquickwindow.h"
+#if QT_CONFIG(quick_draganddrop)
#include "qquickdrag_p.h"
+#endif
#include <private/qqmldata_p.h>
#include <private/qsgadaptationlayer_p.h>
@@ -62,7 +64,7 @@ QQuickMouseAreaPrivate::QQuickMouseAreaPrivate()
moved(false), stealMouse(false), doubleClick(false), preventStealing(false),
propagateComposedEvents(false), overThreshold(false), pressed(nullptr),
pressAndHoldInterval(-1)
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
, drag(nullptr)
#endif
#if QT_CONFIG(cursor)
@@ -73,7 +75,7 @@ QQuickMouseAreaPrivate::QQuickMouseAreaPrivate()
QQuickMouseAreaPrivate::~QQuickMouseAreaPrivate()
{
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
delete drag;
#endif
#if QT_CONFIG(cursor)
@@ -685,7 +687,7 @@ void QQuickMouseArea::mousePressEvent(QMouseEvent *event)
} else {
d->longPress = false;
d->saveEvent(event);
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
if (d->drag)
d->drag->setActive(false);
#endif
@@ -712,7 +714,7 @@ void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event)
// ### can GV handle this for us?
setHovered(contains(d->lastPos));
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
if (d->drag && d->drag->target()) {
if (!d->moved) {
d->targetStartPos = d->drag->target()->parentItem()
@@ -759,8 +761,10 @@ void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event)
QPointF targetPos = d->drag->target()->position();
- if (d->drag->active())
+ if (d->drag->active()) {
d->drag->target()->setPosition(boundedDragPos);
+ d->lastPos = d->lastScenePos - mapToScene(position());
+ }
bool dragOverThresholdX = QQuickWindowPrivate::dragOverThreshold(dragPos.x() - startPos.x(),
Qt::XAxis, event, d->drag->threshold());
@@ -806,7 +810,7 @@ void QQuickMouseArea::mouseReleaseEvent(QMouseEvent *event)
setPressed(event->button(), false, event->source());
if (!d->pressed) {
// no other buttons are pressed
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
if (d->drag)
d->drag->setActive(false);
#endif
@@ -895,7 +899,7 @@ void QQuickMouseArea::wheelEvent(QWheelEvent *event)
}
QQuickWheelEvent &we = d->quickWheelEvent;
- we.reset(event->posF().x(), event->posF().y(), event->angleDelta(), event->pixelDelta(),
+ we.reset(event->position().x(), event->position().y(), event->angleDelta(), event->pixelDelta(),
event->buttons(), event->modifiers(), event->inverted());
we.setAccepted(d->isWheelConnected());
emit wheel(&we);
@@ -916,7 +920,7 @@ void QQuickMouseArea::ungrabMouse()
d->overThreshold = false;
setKeepMouseGrab(false);
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
if (d->drag)
d->drag->setActive(false);
#endif
@@ -999,7 +1003,7 @@ bool QQuickMouseArea::childMouseEventFilter(QQuickItem *i, QEvent *e)
Q_D(QQuickMouseArea);
if (!d->pressed &&
(!d->enabled || !isVisible()
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
|| !d->drag || !d->drag->filterChildren()
#endif
)
@@ -1022,7 +1026,7 @@ void QQuickMouseArea::timerEvent(QTimerEvent *event)
Q_D(QQuickMouseArea);
if (event->timerId() == d->pressAndHoldTimer.timerId()) {
d->pressAndHoldTimer.stop();
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
bool dragged = d->drag && d->drag->active();
#else
bool dragged = false;
@@ -1065,7 +1069,7 @@ void QQuickMouseArea::itemChange(ItemChange change, const ItemChangeData &value)
Q_D(QQuickMouseArea);
switch (change) {
case ItemVisibleHasChanged:
- if (acceptHoverEvents() && d->hovered != (isVisible() && isUnderMouse())) {
+ if (d->effectiveEnable && d->enabled && acceptHoverEvents() && d->hovered != (isVisible() && isUnderMouse())) {
if (!d->hovered) {
QPointF cursorPos = QGuiApplicationPrivate::lastCursorPosition;
d->lastScenePos = d->window->mapFromGlobal(cursorPos.toPoint());
@@ -1198,7 +1202,7 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS
{
Q_D(QQuickMouseArea);
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
bool dragged = d->drag && d->drag->active();
#else
bool dragged = false;
@@ -1404,7 +1408,7 @@ void QQuickMouseArea::resetPressAndHoldInterval()
*/
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
QQuickDrag *QQuickMouseArea::drag()
{
Q_D(QQuickMouseArea);
diff --git a/src/quick/items/qquickmousearea_p.h b/src/quick/items/qquickmousearea_p.h
index 0a8449957f..0e01fa7915 100644
--- a/src/quick/items/qquickmousearea_p.h
+++ b/src/quick/items/qquickmousearea_p.h
@@ -71,11 +71,11 @@ class Q_QUICK_PRIVATE_EXPORT QQuickMouseArea : public QQuickItem
Q_PROPERTY(bool containsMouse READ hovered NOTIFY hoveredChanged)
Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
- Q_PROPERTY(bool scrollGestureEnabled READ isScrollGestureEnabled WRITE setScrollGestureEnabled NOTIFY scrollGestureEnabledChanged REVISION 2)
+ Q_PROPERTY(bool scrollGestureEnabled READ isScrollGestureEnabled WRITE setScrollGestureEnabled NOTIFY scrollGestureEnabledChanged REVISION 5)
Q_PROPERTY(Qt::MouseButtons pressedButtons READ pressedButtons NOTIFY pressedButtonsChanged)
Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged)
Q_PROPERTY(bool hoverEnabled READ hoverEnabled WRITE setHoverEnabled NOTIFY hoverEnabledChanged)
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
Q_PROPERTY(QQuickDrag *drag READ drag CONSTANT) //### add flicking to QQuickDrag or add a QQuickFlick ???
#endif
Q_PROPERTY(bool preventStealing READ preventStealing WRITE setPreventStealing NOTIFY preventStealingChanged)
@@ -83,7 +83,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickMouseArea : public QQuickItem
#if QT_CONFIG(cursor)
Q_PROPERTY(Qt::CursorShape cursorShape READ cursorShape WRITE setCursorShape RESET unsetCursor NOTIFY cursorShapeChanged)
#endif
- Q_PROPERTY(bool containsPress READ containsPress NOTIFY containsPressChanged REVISION 1)
+ Q_PROPERTY(bool containsPress READ containsPress NOTIFY containsPressChanged REVISION 4)
Q_PROPERTY(int pressAndHoldInterval READ pressAndHoldInterval WRITE setPressAndHoldInterval NOTIFY pressAndHoldIntervalChanged RESET resetPressAndHoldInterval REVISION 9)
public:
@@ -111,7 +111,7 @@ public:
bool hoverEnabled() const;
void setHoverEnabled(bool h);
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
QQuickDrag *drag();
#endif
@@ -134,7 +134,7 @@ Q_SIGNALS:
void hoveredChanged();
void pressedChanged();
void enabledChanged();
- Q_REVISION(2) void scrollGestureEnabledChanged();
+ Q_REVISION(5) void scrollGestureEnabledChanged();
void pressedButtonsChanged();
void acceptedButtonsChanged();
void hoverEnabledChanged();
@@ -156,7 +156,7 @@ Q_SIGNALS:
void entered();
void exited();
void canceled();
- Q_REVISION(1) void containsPressChanged();
+ Q_REVISION(4) void containsPressChanged();
Q_REVISION(9) void pressAndHoldIntervalChanged();
protected:
diff --git a/src/quick/items/qquickmousearea_p_p.h b/src/quick/items/qquickmousearea_p_p.h
index 0dd2690d43..fba383e268 100644
--- a/src/quick/items/qquickmousearea_p_p.h
+++ b/src/quick/items/qquickmousearea_p_p.h
@@ -97,7 +97,7 @@ public:
bool overThreshold : 1;
Qt::MouseButtons pressed;
int pressAndHoldInterval;
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
QQuickDrag *drag;
#endif
QPointer<QQuickPointerMask> mask;
diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp
index 70426a6a8c..b02e58fae4 100644
--- a/src/quick/items/qquickmultipointtoucharea.cpp
+++ b/src/quick/items/qquickmultipointtoucharea.cpp
@@ -40,6 +40,7 @@
#include "qquickmultipointtoucharea_p.h"
#include <QtQuick/qquickwindow.h>
#include <private/qsgadaptationlayer_p.h>
+#include <private/qevent_p.h>
#include <private/qquickitem_p.h>
#include <private/qguiapplication_p.h>
#include <QEvent>
@@ -655,7 +656,8 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
// (we may have just obtained enough points to start tracking them -- in that case moved or stationary count as newly pressed)
addTouchPoint(&p);
started = true;
- } else if (touchPointState & Qt::TouchPointMoved) {
+ } else if ((touchPointState & Qt::TouchPointMoved) || p.d->stationaryWithModifiedProperty) {
+ // React to a stationary point with a property change (velocity, pressure) as if the point moved. (QTBUG-77142)
QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints.value(id));
Q_ASSERT(dtp);
_movedTouchPoints.append(dtp);
diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp
index 3aa00340b2..e217fdb5d0 100644
--- a/src/quick/items/qquickopenglshadereffect.cpp
+++ b/src/quick/items/qquickopenglshadereffect.cpp
@@ -57,6 +57,10 @@
QT_BEGIN_NAMESPACE
+// Note: this legacy ShaderEffect implementation is used only when running
+// directly with OpenGL. This is going to go away in the future (Qt 6?), since
+// the RHI path uses QQuickGenericShaderEffect always.
+
namespace {
enum VariableQualifier {
@@ -185,8 +189,8 @@ class MappedSlotObject: public QtPrivate::QSlotObjectBase
public:
typedef std::function<void()> PropChangedFunc;
- explicit MappedSlotObject(PropChangedFunc func)
- : QSlotObjectBase(&impl), _signalIndex(-1), func(func)
+ explicit MappedSlotObject(PropChangedFunc f)
+ : QSlotObjectBase(&impl), _signalIndex(-1), func(std::move(f))
{ ref(); }
void setSignalIndex(int idx) { _signalIndex = idx; }
@@ -710,8 +714,8 @@ void QQuickOpenGLShaderEffect::setBlending(bool enable)
QVariant QQuickOpenGLShaderEffect::mesh() const
{
- return m_mesh ? qVariantFromValue(static_cast<QObject *>(m_mesh))
- : qVariantFromValue(m_meshResolution);
+ return m_mesh ? QVariant::fromValue(static_cast<QObject *>(m_mesh))
+ : QVariant::fromValue(m_meshResolution);
}
void QQuickOpenGLShaderEffect::setMesh(const QVariant &mesh)
diff --git a/src/quick/items/qquickopenglshadereffectnode.cpp b/src/quick/items/qquickopenglshadereffectnode.cpp
index 3ccd2a76f7..26ef59c20e 100644
--- a/src/quick/items/qquickopenglshadereffectnode.cpp
+++ b/src/quick/items/qquickopenglshadereffectnode.cpp
@@ -366,7 +366,6 @@ class QQuickOpenGLShaderEffectMaterialCache : public QObject
public:
static QQuickOpenGLShaderEffectMaterialCache *get(bool create = true) {
QOpenGLContext *ctx = QOpenGLContext::currentContext();
- Q_ASSERT(ctx);
QQuickOpenGLShaderEffectMaterialCache *me = ctx->findChild<QQuickOpenGLShaderEffectMaterialCache *>(QStringLiteral("__qt_ShaderEffectCache"), Qt::FindDirectChildrenOnly);
if (!me && create) {
me = new QQuickOpenGLShaderEffectMaterialCache();
diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp
index 8abb3f29cd..6976665134 100644
--- a/src/quick/items/qquickpathview.cpp
+++ b/src/quick/items/qquickpathview.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -60,8 +60,9 @@
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcItemViewDelegateLifecycle)
+Q_LOGGING_CATEGORY(lcPathView, "qt.quick.pathview")
-const qreal MinimumFlickVelocity = 75.0;
+static const qreal MinimumFlickVelocity = 75;
static QQmlOpenMetaObjectType *qPathViewAttachedType = nullptr;
@@ -90,8 +91,8 @@ void QQuickPathViewAttached::setValue(const QByteArray &name, const QVariant &va
}
QQuickPathViewPrivate::QQuickPathViewPrivate()
- : path(nullptr), currentIndex(0), currentItemOffset(0.0), startPc(0)
- , offset(0.0), offsetAdj(0.0), mappedRange(1.0), mappedCache(0.0)
+ : path(nullptr), currentIndex(0), currentItemOffset(0), startPc(0)
+ , offset(0), offsetAdj(0), mappedRange(1), mappedCache(0)
, stealMouse(false), ownModel(false), interactive(true), haveHighlightRange(true)
, autoHighlight(true), highlightUp(false), layoutScheduled(false)
, moving(false), flicking(false), dragging(false), inRequest(false), delegateValidated(false)
@@ -117,10 +118,10 @@ void QQuickPathViewPrivate::init()
q->setFlag(QQuickItem::ItemIsFocusScope);
q->setFiltersChildMouseEvents(true);
qmlobject_connect(&tl, QQuickTimeLine, SIGNAL(updated()),
- q, QQuickPathView, SLOT(ticked()))
+ q, QQuickPathView, SLOT(ticked()));
timer.invalidate();
qmlobject_connect(&tl, QQuickTimeLine, SIGNAL(completed()),
- q, QQuickPathView, SLOT(movementEnding()))
+ q, QQuickPathView, SLOT(movementEnding()));
}
QQuickItem *QQuickPathViewPrivate::getItem(int modelIndex, qreal z, bool async)
@@ -163,7 +164,7 @@ void QQuickPathView::createdItem(int index, QObject *object)
att->setOnPath(false);
}
item->setParentItem(this);
- d->updateItem(item, 1.0);
+ d->updateItem(item, 1);
} else {
d->requestedIndex = -1;
if (!d->inRequest)
@@ -184,13 +185,13 @@ void QQuickPathView::initItem(int index, QObject *object)
if (att) {
att->m_view = this;
qreal percent = d->positionOfIndex(index);
- if (percent < 1.0 && d->path) {
+ if (percent < 1 && d->path) {
const auto attributes = d->path->attributes();
for (const QString &attr : attributes)
att->setValue(attr.toUtf8(), d->path->attributeAt(attr, percent));
item->setZ(d->requestedZ);
}
- att->setOnPath(percent < 1.0);
+ att->setOnPath(percent < 1);
}
}
}
@@ -264,17 +265,17 @@ void QQuickPathViewPrivate::updateMappedRange()
mappedRange = qreal(modelCount)/pathItems;
mappedCache = qreal(cacheSize)/pathItems/2; // Half of cache at each end
} else {
- mappedRange = 1.0;
- mappedCache = 0.0;
+ mappedRange = 1;
+ mappedCache = 0;
}
}
qreal QQuickPathViewPrivate::positionOfIndex(qreal index) const
{
- qreal pos = -1.0;
+ qreal pos = -1;
if (model && index >= 0 && index < modelCount) {
- qreal start = 0.0;
+ qreal start = 0;
if (haveHighlightRange && (highlightRangeMode != QQuickPathView::NoHighlightRange
|| snapMode != QQuickPathView::NoSnap))
start = highlightRangeStart;
@@ -282,10 +283,10 @@ qreal QQuickPathViewPrivate::positionOfIndex(qreal index) const
globalPos = std::fmod(globalPos, qreal(modelCount)) / modelCount;
if (pathItems != -1 && pathItems < modelCount) {
globalPos += start / mappedRange;
- globalPos = std::fmod(globalPos, qreal(1.0));
+ globalPos = std::fmod(globalPos, qreal(1));
pos = globalPos * mappedRange;
} else {
- pos = std::fmod(globalPos + start, qreal(1.0));
+ pos = std::fmod(globalPos + start, qreal(1));
}
}
@@ -296,7 +297,7 @@ qreal QQuickPathViewPrivate::positionOfIndex(qreal index) const
// account the circular space.
bool QQuickPathViewPrivate::isInBound(qreal position, qreal lower, qreal upper) const
{
- if (lower == upper)
+ if (qFuzzyCompare(lower, upper))
return true;
if (lower > upper) {
if (position > upper && position > lower)
@@ -358,7 +359,7 @@ void QQuickPathViewPrivate::updateHighlight()
} else {
qreal target = currentIndex;
- offsetAdj = 0.0;
+ offsetAdj = 0;
tl.reset(moveHighlight);
moveHighlight.setValue(highlightPosition);
@@ -367,14 +368,14 @@ void QQuickPathViewPrivate::updateHighlight()
if (target - highlightPosition > modelCount/2) {
highlightUp = false;
qreal distance = modelCount - target + highlightPosition;
- tl.move(moveHighlight, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * highlightPosition / distance));
+ tl.move(moveHighlight, 0, QEasingCurve(QEasingCurve::InQuad), int(duration * highlightPosition / distance));
tl.set(moveHighlight, modelCount-0.01);
tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * (modelCount-target) / distance));
} else if (target - highlightPosition <= -modelCount/2) {
highlightUp = true;
qreal distance = modelCount - highlightPosition + target;
tl.move(moveHighlight, modelCount-0.01, QEasingCurve(QEasingCurve::InQuad), int(duration * (modelCount-highlightPosition) / distance));
- tl.set(moveHighlight, 0.0);
+ tl.set(moveHighlight, 0);
tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * target / distance));
} else {
highlightUp = highlightPosition - target < 0;
@@ -386,9 +387,9 @@ void QQuickPathViewPrivate::updateHighlight()
void QQuickPathViewPrivate::setHighlightPosition(qreal pos)
{
- if (pos != highlightPosition) {
- qreal start = 0.0;
- qreal end = 1.0;
+ if (!(qFuzzyCompare(pos, highlightPosition))) {
+ qreal start = 0;
+ qreal end = 1;
if (haveHighlightRange && highlightRangeMode != QQuickPathView::NoHighlightRange) {
start = highlightRangeStart;
end = highlightRangeEnd;
@@ -399,7 +400,7 @@ void QQuickPathViewPrivate::setHighlightPosition(qreal pos)
qreal relativeHighlight = std::fmod(pos + offset, range) / range;
if (!highlightUp && relativeHighlight > end / mappedRange) {
- qreal diff = 1.0 - relativeHighlight;
+ qreal diff = 1 - relativeHighlight;
setOffset(offset + diff * range);
} else if (highlightUp && relativeHighlight >= (end - start) / mappedRange) {
qreal diff = relativeHighlight - (end - start) / mappedRange;
@@ -410,7 +411,7 @@ void QQuickPathViewPrivate::setHighlightPosition(qreal pos)
qreal pathPos = positionOfIndex(pos);
updateItem(highlightItem, pathPos);
if (QQuickPathViewAttached *att = attached(highlightItem))
- att->setOnPath(pathPos < 1.0);
+ att->setOnPath(pathPos < 1);
}
}
@@ -435,10 +436,10 @@ void QQuickPathViewPrivate::updateItem(QQuickItem *item, qreal percent)
const auto attributes = path->attributes();
for (const QString &attr : attributes)
att->setValue(attr.toUtf8(), path->attributeAt(attr, percent));
- att->setOnPath(percent < 1.0);
+ att->setOnPath(percent < 1);
}
- QQuickItemPrivate::get(item)->setCulled(percent >= 1.0);
- QPointF pf = path->pointAt(qMin(percent, qreal(1.0)));
+ QQuickItemPrivate::get(item)->setCulled(percent >= 1);
+ QPointF pf = path->pointAtPercent(qMin(percent, qreal(1)));
item->setX(pf.x() - item->width()/2);
item->setY(pf.y() - item->height()/2);
}
@@ -668,7 +669,7 @@ void QQuickPathView::setModel(const QVariant &m)
d->currentIndex = 0;
emit currentIndexChanged();
}
- if (d->offset != 0.0) {
+ if (!(qFuzzyIsNull(d->offset))) {
d->offset = 0;
emit offsetChanged();
}
@@ -820,7 +821,7 @@ void QQuickPathView::decrementCurrentIndex()
\qmlproperty real QtQuick::PathView::offset
The offset specifies how far along the path the items are from their initial positions.
- This is a real number that ranges from 0.0 to the count of items in the model.
+ This is a real number that ranges from \c 0 to the count of items in the model.
*/
qreal QQuickPathView::offset() const
{
@@ -839,7 +840,7 @@ void QQuickPathView::setOffset(qreal offset)
void QQuickPathViewPrivate::setOffset(qreal o)
{
Q_Q(QQuickPathView);
- if (offset != o) {
+ if (!qFuzzyCompare(offset, o)) {
if (isValid() && q->isComponentComplete()) {
qreal oldOffset = offset;
offset = std::fmod(o, qreal(modelCount));
@@ -882,7 +883,6 @@ void QQuickPathViewPrivate::setAdjustedOffset(qreal o)
\sa highlightItem, highlightRangeMode
*/
-
QQmlComponent *QQuickPathView::highlight() const
{
Q_D(const QQuickPathView);
@@ -901,25 +901,26 @@ void QQuickPathView::setHighlight(QQmlComponent *highlight)
}
/*!
- \qmlproperty Item QtQuick::PathView::highlightItem
+ \qmlproperty Item QtQuick::PathView::highlightItem
- \c highlightItem holds the highlight item, which was created
- from the \l highlight component.
+ \c highlightItem holds the highlight item, which was created
+ from the \l highlight component.
- \sa highlight
+ \sa highlight
*/
QQuickItem *QQuickPathView::highlightItem() const
{
Q_D(const QQuickPathView);
return d->highlightItem;
}
+
/*!
\qmlproperty real QtQuick::PathView::preferredHighlightBegin
\qmlproperty real QtQuick::PathView::preferredHighlightEnd
\qmlproperty enumeration QtQuick::PathView::highlightRangeMode
These properties set the preferred range of the highlight (current item)
- within the view. The preferred values must be in the range 0.0-1.0.
+ within the view. The preferred values must be in the range from \c 0 to \c 1.
Valid values for \c highlightRangeMode are:
@@ -960,7 +961,7 @@ qreal QQuickPathView::preferredHighlightBegin() const
void QQuickPathView::setPreferredHighlightBegin(qreal start)
{
Q_D(QQuickPathView);
- if (d->highlightRangeStart == start || start < 0 || start > 1.0)
+ if (qFuzzyCompare(d->highlightRangeStart, start) || start < 0 || start > 1)
return;
d->highlightRangeStart = start;
d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd;
@@ -977,7 +978,7 @@ qreal QQuickPathView::preferredHighlightEnd() const
void QQuickPathView::setPreferredHighlightEnd(qreal end)
{
Q_D(QQuickPathView);
- if (d->highlightRangeEnd == end || end < 0 || end > 1.0)
+ if (qFuzzyCompare(d->highlightRangeEnd, end) || end < 0 || end > 1)
return;
d->highlightRangeEnd = end;
d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd;
@@ -1048,7 +1049,7 @@ qreal QQuickPathView::dragMargin() const
void QQuickPathView::setDragMargin(qreal dragMargin)
{
Q_D(QQuickPathView);
- if (d->dragMargin == dragMargin)
+ if (qFuzzyCompare(d->dragMargin, dragMargin))
return;
d->dragMargin = dragMargin;
emit dragMarginChanged();
@@ -1069,7 +1070,7 @@ qreal QQuickPathView::flickDeceleration() const
void QQuickPathView::setFlickDeceleration(qreal dec)
{
Q_D(QQuickPathView);
- if (d->deceleration == dec)
+ if (qFuzzyCompare(d->deceleration, dec))
return;
d->deceleration = dec;
emit flickDecelerationChanged();
@@ -1090,7 +1091,7 @@ qreal QQuickPathView::maximumFlickVelocity() const
void QQuickPathView::setMaximumFlickVelocity(qreal vel)
{
Q_D(QQuickPathView);
- if (vel == d->maximumFlickVelocity)
+ if (qFuzzyCompare(vel, d->maximumFlickVelocity))
return;
d->maximumFlickVelocity = vel;
emit maximumFlickVelocityChanged();
@@ -1580,16 +1581,16 @@ QQuickItem *QQuickPathView::itemAtIndex(int index) const
QPointF QQuickPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const
{
const auto pathLength = path->path().length();
- qreal samples = qMin(pathLength / 5, qreal(500.0));
+ qreal samples = qMin(pathLength / 5, qreal(500));
qreal res = pathLength / samples;
qreal mindist = 1e10; // big number
- QPointF nearPoint = path->pointAt(0);
+ QPointF nearPoint = path->pointAtPercent(0);
qreal nearPc = 0;
// get rough pos
for (qreal i=1; i < samples; i++) {
- QPointF pt = path->pointAt(i/samples);
+ QPointF pt = path->pointAtPercent(i/samples);
QPointF diff = pt - point;
qreal dist = diff.x()*diff.x() + diff.y()*diff.y();
if (dist < mindist) {
@@ -1601,8 +1602,8 @@ QPointF QQuickPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercen
// now refine
qreal approxPc = nearPc;
- for (qreal i = approxPc-1.0; i < approxPc+1.0; i += 1/(2*res)) {
- QPointF pt = path->pointAt(i/samples);
+ for (qreal i = approxPc-1; i < approxPc+1; i += 1/(2*res)) {
+ QPointF pt = path->pointAtPercent(i/samples);
QPointF diff = pt - point;
qreal dist = diff.x()*diff.x() + diff.y()*diff.y();
if (dist < mindist) {
@@ -1623,6 +1624,7 @@ void QQuickPathViewPrivate::addVelocitySample(qreal v)
velocityBuffer.append(v);
if (velocityBuffer.count() > QML_FLICK_SAMPLEBUFFER)
velocityBuffer.remove(0);
+ qCDebug(lcPathView) << "instantaneous velocity" << v;
}
qreal QQuickPathViewPrivate::calcVelocity() const
@@ -1635,6 +1637,7 @@ qreal QQuickPathViewPrivate::calcVelocity() const
velocity += v;
}
velocity /= count;
+ qCDebug(lcPathView) << "average velocity" << velocity << "based on" << count << "samples";
}
return velocity;
}
@@ -1642,7 +1645,7 @@ qreal QQuickPathViewPrivate::calcVelocity() const
qint64 QQuickPathViewPrivate::computeCurrentTime(QInputEvent *event) const
{
if (0 != event->timestamp())
- return event->timestamp();
+ return qint64(event->timestamp());
return timer.elapsed();
}
@@ -1669,7 +1672,7 @@ void QQuickPathViewPrivate::handleMousePressEvent(QMouseEvent *event)
if (item->contains(item->mapFromScene(event->windowPos())))
break;
}
- if (idx == items.count() && dragMargin == 0.) // didn't click on an item
+ if (idx == items.count() && qFuzzyIsNull(dragMargin)) // didn't click on an item
return;
startPoint = pointNear(event->localPos(), &startPc);
@@ -1731,7 +1734,7 @@ void QQuickPathViewPrivate::handleMouseMoveEvent(QMouseEvent *event)
moveReason = QQuickPathViewPrivate::Mouse;
int count = pathItems == -1 ? modelCount : qMin(pathItems, modelCount);
qreal diff = (newPc - startPc)*count;
- if (diff) {
+ if (!qFuzzyIsNull(diff)) {
q->setOffset(offset + diff);
if (diff > modelCount/2)
@@ -1741,7 +1744,7 @@ void QQuickPathViewPrivate::handleMouseMoveEvent(QMouseEvent *event)
qint64 elapsed = currentTimestamp - lastPosTime;
if (elapsed > 0)
- addVelocitySample(diff / (qreal(elapsed) / 1000.));
+ addVelocitySample(diff / (qreal(elapsed) / 1000));
}
if (!moving) {
moving = true;
@@ -1766,7 +1769,7 @@ void QQuickPathView::mouseReleaseEvent(QMouseEvent *event)
}
}
-void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *)
+void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *event)
{
Q_Q(QQuickPathView);
stealMouse = false;
@@ -1780,6 +1783,12 @@ void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *)
}
qreal velocity = calcVelocity();
+ qint64 elapsed = computeCurrentTime(event) - lastPosTime;
+ // Let the velocity linearly decay such that it becomes 0 if elapsed time > QML_FLICK_VELOCITY_DECAY_TIME
+ // The intention is that if you are flicking at some speed, then stop in one place for some time before releasing,
+ // the previous velocity is lost. (QTBUG-77173, QTBUG-59052)
+ velocity *= qreal(qMax(0LL, QML_FLICK_VELOCITY_DECAY_TIME - elapsed)) / QML_FLICK_VELOCITY_DECAY_TIME;
+ qCDebug(lcPathView) << "after elapsed time" << elapsed << "velocity decayed to" << velocity;
qreal count = pathItems == -1 ? modelCount : qMin(pathItems, modelCount);
const auto averageItemLength = path->path().length() / count;
qreal pixelVelocity = averageItemLength * velocity;
@@ -1797,32 +1806,32 @@ void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *)
|| snapMode != QQuickPathView::NoSnap)) {
if (snapMode == QQuickPathView::SnapOneItem) {
// encourage snapping one item in direction of motion
- if (velocity > 0.)
+ if (velocity > 0)
dist = qRound(0.5 + offset) - offset;
else
dist = qRound(0.5 - offset) + offset;
} else {
// + 0.25 to encourage moving at least one item in the flick direction
- dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0) + 0.25));
+ dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2) + 0.25));
// round to nearest item.
- if (velocity > 0.)
+ if (velocity > 0)
dist = qRound(dist + offset) - offset;
else
dist = qRound(dist - offset) + offset;
}
// Calculate accel required to stop on item boundary
- if (dist <= 0.) {
- dist = 0.;
- accel = 0.;
+ if (dist <= 0) {
+ dist = 0;
+ accel = 0;
} else {
- accel = v2 / (2.0f * qAbs(dist));
+ accel = v2 / (2 * qAbs(dist));
}
} else {
- dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0)));
+ dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2)));
}
- flickDuration = static_cast<int>(1000 * qAbs(velocity) / accel);
- offsetAdj = 0.0;
+ flickDuration = int(1000 * qAbs(velocity) / accel);
+ offsetAdj = 0;
moveOffset.setValue(offset);
tl.accel(moveOffset, velocity, accel, dist);
tl.callback(QQuickTimeLineCallback(&moveOffset, fixOffsetCallback, this));
@@ -1996,7 +2005,7 @@ void QQuickPathView::refill()
else
qCDebug(lcItemViewDelegateLifecycle) << "idx" << idx << "@" << pos << ":" << item;
}
- if (pos < 1.0) {
+ if (pos < 1) {
d->updateItem(item, pos);
if (idx == d->currentIndex) {
currentVisible = true;
@@ -2006,9 +2015,9 @@ void QQuickPathView::refill()
} else {
d->updateItem(item, pos);
if (QQuickPathViewAttached *att = d->attached(item))
- att->setOnPath(pos < 1.0);
- if (!d->isInBound(pos, d->mappedRange - d->mappedCache, 1.0 + d->mappedCache)) {
- qCDebug(lcItemViewDelegateLifecycle) << "release" << idx << "@" << pos << ", !isInBound: lower" << (d->mappedRange - d->mappedCache) << "upper" << (1.0 + d->mappedCache);
+ att->setOnPath(pos < 1);
+ if (!d->isInBound(pos, d->mappedRange - d->mappedCache, 1 + d->mappedCache)) {
+ qCDebug(lcItemViewDelegateLifecycle) << "release" << idx << "@" << pos << ", !isInBound: lower" << (d->mappedRange - d->mappedCache) << "upper" << (1 + d->mappedCache);
d->releaseItem(item);
it = d->items.erase(it);
} else {
@@ -2024,12 +2033,12 @@ void QQuickPathView::refill()
int endIdx = 0;
qreal endPos;
int startIdx = 0;
- qreal startPos = 0.0;
+ qreal startPos = 0;
const bool wasEmpty = d->items.isEmpty();
if (!wasEmpty) {
//Find the beginning and end, items may not be in sorted order
- endPos = -1.0;
- startPos = 2.0;
+ endPos = -1;
+ startPos = 2;
for (QQuickItem * item : qAsConst(d->items)) {
int idx = d->model->indexOf(item, nullptr);
@@ -2058,10 +2067,10 @@ void QQuickPathView::refill()
if (idx >= d->modelCount)
idx = 0;
qreal nextPos = d->positionOfIndex(idx);
- while ((d->isInBound(nextPos, endPos, 1.0 + d->mappedCache) || !d->items.count())
+ while ((d->isInBound(nextPos, endPos, 1 + d->mappedCache) || !d->items.count())
&& d->items.count() < count+d->cacheSize) {
qCDebug(lcItemViewDelegateLifecycle) << "append" << idx << "@" << nextPos << (d->currentIndex == idx ? "current" : "") << "items count was" << d->items.count();
- QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1.0);
+ QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1);
if (!item) {
waiting = true;
break;
@@ -2093,7 +2102,7 @@ void QQuickPathView::refill()
while (!waiting && d->isInBound(nextPos, d->mappedRange - d->mappedCache, startPos)
&& d->items.count() < count+d->cacheSize) {
qCDebug(lcItemViewDelegateLifecycle) << "prepend" << idx << "@" << nextPos << (d->currentIndex == idx ? "current" : "") << "items count was" << d->items.count();
- QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1.0);
+ QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1);
if (!item) {
waiting = true;
break;
@@ -2126,9 +2135,9 @@ void QQuickPathView::refill()
QQuickItem *lastItem = d->items.at(0);
while (idx != endIdx) {
nextPos = d->positionOfIndex(idx);
- if (d->isInBound(nextPos, d->mappedRange - d->mappedCache, 1.0 + d->mappedCache)) {
+ if (d->isInBound(nextPos, d->mappedRange - d->mappedCache, 1 + d->mappedCache)) {
//This gets the reference from the delegate model, and will not re-create
- QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1.0);
+ QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1);
if (!item) {
waiting = true;
break;
@@ -2162,13 +2171,13 @@ void QQuickPathView::refill()
bool currentChanged = false;
if (!currentVisible) {
- d->currentItemOffset = 1.0;
+ d->currentItemOffset = 1;
if (d->currentItem) {
- d->updateItem(d->currentItem, 1.0);
+ d->updateItem(d->currentItem, 1);
} else if (!waiting && d->currentIndex >= 0 && d->currentIndex < d->modelCount) {
if ((d->currentItem = d->getItem(d->currentIndex, d->currentIndex))) {
currentChanged = true;
- d->updateItem(d->currentItem, 1.0);
+ d->updateItem(d->currentItem, 1);
if (QQuickPathViewAttached *att = d->attached(d->currentItem))
att->setIsCurrentItem(true);
}
@@ -2362,7 +2371,7 @@ void QQuickPathViewPrivate::createCurrentItem()
}
} else if (currentIndex >= 0 && currentIndex < modelCount) {
if ((currentItem = getItem(currentIndex, currentIndex))) {
- updateItem(currentItem, 1.0);
+ updateItem(currentItem, 1);
if (QQuickPathViewAttached *att = attached(currentItem))
att->setIsCurrentItem(true);
}
@@ -2396,7 +2405,7 @@ void QQuickPathViewPrivate::updateCurrent()
void QQuickPathViewPrivate::fixOffsetCallback(void *d)
{
- ((QQuickPathViewPrivate *)d)->fixOffset();
+ static_cast<QQuickPathViewPrivate *>(d)->fixOffset();
}
void QQuickPathViewPrivate::fixOffset()
@@ -2421,7 +2430,7 @@ void QQuickPathViewPrivate::snapToIndex(int index, MovementReason reason)
qreal targetOffset = std::fmod(qreal(modelCount - index), qreal(modelCount));
moveReason = reason;
- offsetAdj = 0.0;
+ offsetAdj = 0;
tl.reset(moveOffset);
moveOffset.setValue(offset);
@@ -2433,20 +2442,20 @@ void QQuickPathViewPrivate::snapToIndex(int index, MovementReason reason)
if (!duration || qAbs(offset - targetOffset) < threshold || (qFuzzyIsNull(targetOffset) && qAbs(modelCount - offset) < threshold)) {
tl.set(moveOffset, targetOffset);
- } else if (moveDirection == QQuickPathView::Positive || (moveDirection == QQuickPathView::Shortest && targetOffset - offset > modelCount/2.0)) {
+ } else if (moveDirection == QQuickPathView::Positive || (moveDirection == QQuickPathView::Shortest && targetOffset - offset > modelCount/2)) {
qreal distance = modelCount - targetOffset + offset;
if (targetOffset > moveOffset) {
- tl.move(moveOffset, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * offset / distance));
+ tl.move(moveOffset, 0, QEasingCurve(QEasingCurve::InQuad), int(duration * offset / distance));
tl.set(moveOffset, modelCount);
- tl.move(moveOffset, targetOffset, QEasingCurve(offset == 0.0 ? QEasingCurve::InOutQuad : QEasingCurve::OutQuad), int(duration * (modelCount-targetOffset) / distance));
+ tl.move(moveOffset, targetOffset, QEasingCurve(qFuzzyIsNull(offset) ? QEasingCurve::InOutQuad : QEasingCurve::OutQuad), int(duration * (modelCount-targetOffset) / distance));
} else {
tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
}
- } else if (moveDirection == QQuickPathView::Negative || targetOffset - offset <= -modelCount/2.0) {
+ } else if (moveDirection == QQuickPathView::Negative || targetOffset - offset <= -modelCount/2) {
qreal distance = modelCount - offset + targetOffset;
if (targetOffset < moveOffset) {
- tl.move(moveOffset, modelCount, QEasingCurve(targetOffset == 0 ? QEasingCurve::InOutQuad : QEasingCurve::InQuad), int(duration * (modelCount-offset) / distance));
- tl.set(moveOffset, 0.0);
+ tl.move(moveOffset, modelCount, QEasingCurve(qFuzzyIsNull(targetOffset) ? QEasingCurve::InOutQuad : QEasingCurve::InQuad), int(duration * (modelCount-offset) / distance));
+ tl.set(moveOffset, 0);
tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * targetOffset / distance));
} else {
tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
diff --git a/src/quick/items/qquickpincharea_p.h b/src/quick/items/qquickpincharea_p.h
index 8eff53e6dc..cf21555823 100644
--- a/src/quick/items/qquickpincharea_p.h
+++ b/src/quick/items/qquickpincharea_p.h
@@ -59,7 +59,7 @@ class Q_AUTOTEST_EXPORT QQuickPinch : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQuickItem *target READ target WRITE setTarget RESET resetTarget)
+ Q_PROPERTY(QQuickItem *target READ target WRITE setTarget RESET resetTarget NOTIFY targetChanged)
Q_PROPERTY(qreal minimumScale READ minimumScale WRITE setMinimumScale NOTIFY minimumScaleChanged)
Q_PROPERTY(qreal maximumScale READ maximumScale WRITE setMaximumScale NOTIFY maximumScaleChanged)
Q_PROPERTY(qreal minimumRotation READ minimumRotation WRITE setMinimumRotation NOTIFY minimumRotationChanged)
@@ -283,7 +283,7 @@ Q_SIGNALS:
void pinchStarted(QQuickPinchEvent *pinch);
void pinchUpdated(QQuickPinchEvent *pinch);
void pinchFinished(QQuickPinchEvent *pinch);
- Q_REVISION(1) void smartZoom(QQuickPinchEvent *pinch);
+ Q_REVISION(5) void smartZoom(QQuickPinchEvent *pinch);
protected:
bool childMouseEventFilter(QQuickItem *i, QEvent *e) override;
diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp
index 5e217dcd0c..f3cefc7463 100644
--- a/src/quick/items/qquickrectangle.cpp
+++ b/src/quick/items/qquickrectangle.cpp
@@ -451,8 +451,30 @@ void QQuickRectangle::setGradient(const QJSValue &gradient)
d->gradient = QJSValue();
}
} else if (gradient.isNumber() || gradient.isString()) {
- QGradient preset(gradient.toVariant().value<QGradient::Preset>());
- if (preset.type() != QGradient::NoGradient) {
+ static const QMetaEnum gradientPresetMetaEnum = QMetaEnum::fromType<QGradient::Preset>();
+ Q_ASSERT(gradientPresetMetaEnum.isValid());
+
+ QGradient result;
+
+ // This code could simply use gradient.toVariant().convert<QGradient::Preset>(),
+ // but QTBUG-76377 prevents us from doing error checks. So we need to
+ // do them manually. Also, NumPresets cannot be used.
+
+ if (gradient.isNumber()) {
+ const auto preset = QGradient::Preset(gradient.toInt());
+ if (preset != QGradient::NumPresets && gradientPresetMetaEnum.valueToKey(preset))
+ result = QGradient(preset);
+ } else if (gradient.isString()) {
+ const auto presetName = gradient.toString();
+ if (presetName != QLatin1String("NumPresets")) {
+ bool ok;
+ const auto presetInt = gradientPresetMetaEnum.keyToValue(qPrintable(presetName), &ok);
+ if (ok)
+ result = QGradient(QGradient::Preset(presetInt));
+ }
+ }
+
+ if (result.type() != QGradient::NoGradient) {
d->gradient = gradient;
} else {
qmlWarning(this) << "No such gradient preset '" << gradient.toString() << "'";
diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp
index 76bcae1003..9f9777f199 100644
--- a/src/quick/items/qquickrendercontrol.cpp
+++ b/src/quick/items/qquickrendercontrol.cpp
@@ -188,8 +188,7 @@ void QQuickRenderControlPrivate::windowDestroyed()
if (window) {
rc->invalidate();
- delete QQuickWindowPrivate::get(window)->animationController;
- QQuickWindowPrivate::get(window)->animationController = nullptr;
+ QQuickWindowPrivate::get(window)->animationController.reset();
#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl)
if (QOpenGLContext::currentContext())
@@ -242,7 +241,18 @@ void QQuickRenderControl::initialize(QOpenGLContext *gl)
// It cannot be done here since the surface to use may not be the
// surface belonging to window. In fact window may not have a native
// window/surface at all.
- d->rc->initialize(gl);
+ QSGDefaultRenderContext *rc = qobject_cast<QSGDefaultRenderContext *>(d->rc);
+ if (rc) {
+ QSGDefaultRenderContext::InitParams params;
+ params.sampleCount = qMax(1, gl->format().samples());
+ params.openGLContext = gl;
+ params.initialSurfacePixelSize = d->window->size() * d->window->effectiveDevicePixelRatio();
+ params.maybeSurface = d->window;
+ rc->initialize(&params);
+ } else {
+ // can this happen?
+ d->rc->initialize(nullptr);
+ }
#else
Q_UNUSED(gl)
#endif
diff --git a/src/quick/items/qquickrepeater.cpp b/src/quick/items/qquickrepeater.cpp
index 0c00fecead..c8c5281089 100644
--- a/src/quick/items/qquickrepeater.cpp
+++ b/src/quick/items/qquickrepeater.cpp
@@ -41,7 +41,6 @@
#include "qquickrepeater_p_p.h"
#include <private/qqmlglobal_p.h>
-#include <private/qqmllistaccessor_p.h>
#include <private/qqmlchangeset_p.h>
#include <private/qqmldelegatemodel_p.h>
diff --git a/src/quick/items/qquickscreen_p.h b/src/quick/items/qquickscreen_p.h
index e9db07d14c..10e524e4a0 100644
--- a/src/quick/items/qquickscreen_p.h
+++ b/src/quick/items/qquickscreen_p.h
@@ -83,8 +83,8 @@ class Q_AUTOTEST_EXPORT QQuickScreenInfo : public QObject
// TODO Qt 6 Remove this orientation -> incomplete device orientation -> better use OrientationSensor
Q_PROPERTY(Qt::ScreenOrientation orientation READ orientation NOTIFY orientationChanged)
- Q_PROPERTY(int virtualX READ virtualX NOTIFY virtualXChanged REVISION 1)
- Q_PROPERTY(int virtualY READ virtualY NOTIFY virtualYChanged REVISION 1)
+ Q_PROPERTY(int virtualX READ virtualX NOTIFY virtualXChanged REVISION 3)
+ Q_PROPERTY(int virtualY READ virtualY NOTIFY virtualYChanged REVISION 3)
public:
QQuickScreenInfo(QObject *parent = nullptr, QScreen *wrappedScreen = nullptr);
@@ -121,8 +121,8 @@ Q_SIGNALS:
void devicePixelRatioChanged();
void primaryOrientationChanged();
void orientationChanged();
- Q_REVISION(1) void virtualXChanged();
- Q_REVISION(1) void virtualYChanged();
+ Q_REVISION(3) void virtualXChanged();
+ Q_REVISION(3) void virtualYChanged();
protected:
QPointer<QScreen> m_screen;
diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp
index eb5256fa4e..b3c8386fd9 100644
--- a/src/quick/items/qquickshadereffect.cpp
+++ b/src/quick/items/qquickshadereffect.cpp
@@ -44,6 +44,9 @@
#include <private/qquickopenglshadereffect_p.h>
#endif
#include <private/qquickgenericshadereffect_p.h>
+#if QT_CONFIG(opengl) /* || QT_CONFIG(vulkan) || defined(Q_OS_WIN) || defined(Q_OS_DARWIN) */
+#include <private/qsgrhisupport_p.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -506,13 +509,20 @@ QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent)
{
setFlag(QQuickItem::ItemHasContents);
+#if QT_CONFIG(opengl) /* || QT_CONFIG(vulkan) || defined(Q_OS_WIN) || defined(Q_OS_DARWIN) */
+ if (QSGRhiSupport::instance()->isRhiEnabled()) {
+ m_impl = new QQuickGenericShaderEffect(this, this);
+ } else
+#endif
+ {
#if QT_CONFIG(opengl)
- if (!qsg_backend_flags().testFlag(QSGContextFactoryInterface::SupportsShaderEffectNode))
- m_glImpl = new QQuickOpenGLShaderEffect(this, this);
+ if (!qsg_backend_flags().testFlag(QSGContextFactoryInterface::SupportsShaderEffectNode))
+ m_glImpl = new QQuickOpenGLShaderEffect(this, this);
- if (!m_glImpl)
+ if (!m_glImpl)
#endif
- m_impl = new QQuickGenericShaderEffect(this, this);
+ m_impl = new QQuickGenericShaderEffect(this, this);
+ }
}
QQuickShaderEffect::~QQuickShaderEffect()
diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h
index c5bddc40d2..6e2f35882b 100644
--- a/src/quick/items/qquickshadereffect_p.h
+++ b/src/quick/items/qquickshadereffect_p.h
@@ -74,7 +74,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffect : public QQuickItem
Q_PROPERTY(CullMode cullMode READ cullMode WRITE setCullMode NOTIFY cullModeChanged)
Q_PROPERTY(QString log READ log NOTIFY logChanged)
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
- Q_PROPERTY(bool supportsAtlasTextures READ supportsAtlasTextures WRITE setSupportsAtlasTextures NOTIFY supportsAtlasTexturesChanged REVISION 1)
+ Q_PROPERTY(bool supportsAtlasTextures READ supportsAtlasTextures WRITE setSupportsAtlasTextures NOTIFY supportsAtlasTexturesChanged REVISION 4)
public:
enum CullMode {
diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp
index 505940e673..f9f3e5cfa3 100644
--- a/src/quick/items/qquickshadereffectsource.cpp
+++ b/src/quick/items/qquickshadereffectsource.cpp
@@ -750,7 +750,7 @@ QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaint
QSGInternalImageNode *node = static_cast<QSGInternalImageNode *>(oldNode);
if (!node) {
- node = d->sceneGraphContext()->createInternalImageNode();
+ node = d->sceneGraphContext()->createInternalImageNode(d->sceneGraphRenderContext());
node->setFlag(QSGNode::UsePreprocess);
node->setTexture(m_texture);
QQuickShaderSourceAttachedNode *attached = new QQuickShaderSourceAttachedNode;
diff --git a/src/quick/items/qquickshadereffectsource_p.h b/src/quick/items/qquickshadereffectsource_p.h
index d5bb33902a..d612d1179f 100644
--- a/src/quick/items/qquickshadereffectsource_p.h
+++ b/src/quick/items/qquickshadereffectsource_p.h
@@ -87,8 +87,8 @@ class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectSource : public QQuickItem, publi
Q_PROPERTY(bool hideSource READ hideSource WRITE setHideSource NOTIFY hideSourceChanged)
Q_PROPERTY(bool mipmap READ mipmap WRITE setMipmap NOTIFY mipmapChanged)
Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged)
- Q_PROPERTY(TextureMirroring textureMirroring READ textureMirroring WRITE setTextureMirroring NOTIFY textureMirroringChanged REVISION 1)
- Q_PROPERTY(int samples READ samples WRITE setSamples NOTIFY samplesChanged REVISION 2)
+ Q_PROPERTY(TextureMirroring textureMirroring READ textureMirroring WRITE setTextureMirroring NOTIFY textureMirroringChanged REVISION 6)
+ Q_PROPERTY(int samples READ samples WRITE setSamples NOTIFY samplesChanged REVISION 9)
public:
enum WrapMode {
diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp
index be297bbe76..8c52703938 100644
--- a/src/quick/items/qquickspriteengine.cpp
+++ b/src/quick/items/qquickspriteengine.cpp
@@ -554,7 +554,7 @@ void QQuickStochasticEngine::restart(int index)
bool randomStart = (m_startTimes.at(index) == NINF);
m_startTimes[index] = m_timeOffset;
if (m_addAdvance)
- m_startTimes[index] += m_advanceTime.elapsed();
+ m_startTimes[index] += m_advanceTimer.elapsed();
if (randomStart)
m_startTimes[index] -= QRandomGenerator::global()->bounded(m_duration.at(index));
int time = m_duration.at(index) + m_startTimes.at(index);
@@ -574,12 +574,12 @@ void QQuickSpriteEngine::restart(int index) //Reimplemented to recognize and han
} else {
m_startTimes[index] = m_timeOffset;
if (m_addAdvance)
- m_startTimes[index] += m_advanceTime.elapsed();
+ m_startTimes[index] += m_advanceTimer.elapsed();
if (randomStart)
m_startTimes[index] -= QRandomGenerator::global()->bounded(m_duration.at(index));
int time = spriteDuration(index) + m_startTimes.at(index);
if (randomStart) {
- int curTime = m_timeOffset + (m_addAdvance ? m_advanceTime.elapsed() : 0);
+ int curTime = m_timeOffset + (m_addAdvance ? m_advanceTimer.elapsed() : 0);
while (time < curTime) //Fast forward through psuedostates as needed
time += spriteDuration(index);
}
@@ -623,10 +623,10 @@ void QQuickSpriteEngine::advance(int idx) //Reimplemented to recognize and handl
}
//just go past the pseudostate logic
} else if (m_startTimes.at(idx) + m_duration.at(idx)
- > int(m_timeOffset + (m_addAdvance ? m_advanceTime.elapsed() : 0))) {
+ > int(m_timeOffset + (m_addAdvance ? m_advanceTimer.elapsed() : 0))) {
//only a pseduostate ended
emit stateChanged(idx);
- addToUpdateList(spriteStart(idx) + spriteDuration(idx) + (m_addAdvance ? m_advanceTime.elapsed() : 0), idx);
+ addToUpdateList(spriteStart(idx) + spriteDuration(idx) + (m_addAdvance ? m_advanceTimer.elapsed() : 0), idx);
return;
}
int nextIdx = nextState(m_things.at(idx), idx);
@@ -685,7 +685,7 @@ uint QQuickStochasticEngine::updateSprites(uint time)//### would returning a lis
}
m_stateUpdates.remove(0, i);
- m_advanceTime.start();
+ m_advanceTimer.start();
m_addAdvance = true;
if (m_stateUpdates.isEmpty())
return uint(-1);
diff --git a/src/quick/items/qquickspriteengine_p.h b/src/quick/items/qquickspriteengine_p.h
index d3944b4620..da505be911 100644
--- a/src/quick/items/qquickspriteengine_p.h
+++ b/src/quick/items/qquickspriteengine_p.h
@@ -58,7 +58,7 @@ QT_REQUIRE_CONFIG(quick_sprite);
#include <QObject>
#include <QVector>
#include <QTimer>
-#include <QTime>
+#include <QElapsedTimer>
#include <QList>
#include <QQmlListProperty>
#include <QImage>
@@ -254,7 +254,7 @@ protected:
QVector<int> m_startTimes;
QVector<QPair<uint, QVector<int> > > m_stateUpdates;//### This could be done faster - priority queue?
- QTime m_advanceTime;
+ QElapsedTimer m_advanceTimer;
uint m_timeOffset;
QString m_globalGoal;
int m_maxFrames;
diff --git a/src/quick/items/qquickspritesequence_p.h b/src/quick/items/qquickspritesequence_p.h
index 899ce79e0e..12c80d6a27 100644
--- a/src/quick/items/qquickspritesequence_p.h
+++ b/src/quick/items/qquickspritesequence_p.h
@@ -56,7 +56,6 @@
QT_REQUIRE_CONFIG(quick_sprite);
#include <QtQuick/QQuickItem>
-#include <QTime>
QT_BEGIN_NAMESPACE
diff --git a/src/quick/items/qquickspritesequence_p_p.h b/src/quick/items/qquickspritesequence_p_p.h
index 3579833116..4788cd15aa 100644
--- a/src/quick/items/qquickspritesequence_p_p.h
+++ b/src/quick/items/qquickspritesequence_p_p.h
@@ -57,6 +57,7 @@ QT_REQUIRE_CONFIG(quick_sprite);
#include "qquickitem_p.h"
#include "qquicksprite_p.h"
+#include <QElapsedTimer>
QT_BEGIN_NAMESPACE
@@ -78,7 +79,7 @@ public:
}
QList<QQuickSprite*> m_sprites;
QQuickSpriteEngine* m_spriteEngine;
- QTime m_timestamp;
+ QElapsedTimer m_timestamp;
int m_curFrame;
bool m_pleaseReset;
bool m_running;
diff --git a/src/quick/items/qquickstateoperations.cpp b/src/quick/items/qquickstateoperations.cpp
index fe1dfd349e..31d1c91649 100644
--- a/src/quick/items/qquickstateoperations.cpp
+++ b/src/quick/items/qquickstateoperations.cpp
@@ -200,7 +200,7 @@ QQmlScriptString QQuickParentChange::x() const
return d->xString.value;
}
-void QQuickParentChange::setX(QQmlScriptString x)
+void QQuickParentChange::setX(const QQmlScriptString &x)
{
Q_D(QQuickParentChange);
d->xString = x;
@@ -218,7 +218,7 @@ QQmlScriptString QQuickParentChange::y() const
return d->yString.value;
}
-void QQuickParentChange::setY(QQmlScriptString y)
+void QQuickParentChange::setY(const QQmlScriptString &y)
{
Q_D(QQuickParentChange);
d->yString = y;
@@ -236,7 +236,7 @@ QQmlScriptString QQuickParentChange::width() const
return d->widthString.value;
}
-void QQuickParentChange::setWidth(QQmlScriptString width)
+void QQuickParentChange::setWidth(const QQmlScriptString &width)
{
Q_D(QQuickParentChange);
d->widthString = width;
@@ -254,7 +254,7 @@ QQmlScriptString QQuickParentChange::height() const
return d->heightString.value;
}
-void QQuickParentChange::setHeight(QQmlScriptString height)
+void QQuickParentChange::setHeight(const QQmlScriptString &height)
{
Q_D(QQuickParentChange);
d->heightString = height;
@@ -272,7 +272,7 @@ QQmlScriptString QQuickParentChange::scale() const
return d->scaleString.value;
}
-void QQuickParentChange::setScale(QQmlScriptString scale)
+void QQuickParentChange::setScale(const QQmlScriptString &scale)
{
Q_D(QQuickParentChange);
d->scaleString = scale;
@@ -290,7 +290,7 @@ QQmlScriptString QQuickParentChange::rotation() const
return d->rotationString.value;
}
-void QQuickParentChange::setRotation(QQmlScriptString rotation)
+void QQuickParentChange::setRotation(const QQmlScriptString &rotation)
{
Q_D(QQuickParentChange);
d->rotationString = rotation;
diff --git a/src/quick/items/qquickstateoperations_p.h b/src/quick/items/qquickstateoperations_p.h
index e947b2213f..357366bc60 100644
--- a/src/quick/items/qquickstateoperations_p.h
+++ b/src/quick/items/qquickstateoperations_p.h
@@ -87,27 +87,27 @@ public:
QQuickItem *originalParent() const;
QQmlScriptString x() const;
- void setX(QQmlScriptString x);
+ void setX(const QQmlScriptString &x);
bool xIsSet() const;
QQmlScriptString y() const;
- void setY(QQmlScriptString y);
+ void setY(const QQmlScriptString &y);
bool yIsSet() const;
QQmlScriptString width() const;
- void setWidth(QQmlScriptString width);
+ void setWidth(const QQmlScriptString &width);
bool widthIsSet() const;
QQmlScriptString height() const;
- void setHeight(QQmlScriptString height);
+ void setHeight(const QQmlScriptString &height);
bool heightIsSet() const;
QQmlScriptString scale() const;
- void setScale(QQmlScriptString scale);
+ void setScale(const QQmlScriptString &scale);
bool scaleIsSet() const;
QQmlScriptString rotation() const;
- void setRotation(QQmlScriptString rotation);
+ void setRotation(const QQmlScriptString &rotation);
bool rotationIsSet() const;
ActionList actions() override;
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
index 58cd80500c..295c6898bc 100644
--- a/src/quick/items/qquicktableview.cpp
+++ b/src/quick/items/qquicktableview.cpp
@@ -42,10 +42,10 @@
#include <QtCore/qtimer.h>
#include <QtCore/qdir.h>
-#include <QtQml/private/qqmldelegatemodel_p.h>
-#include <QtQml/private/qqmldelegatemodel_p_p.h>
+#include <QtQmlModels/private/qqmldelegatemodel_p.h>
+#include <QtQmlModels/private/qqmldelegatemodel_p_p.h>
#include <QtQml/private/qqmlincubator_p.h>
-#include <QtQml/private/qqmlchangeset_p.h>
+#include <QtQmlModels/private/qqmlchangeset_p.h>
#include <QtQml/qqmlinfo.h>
#include <QtQuick/private/qquickflickable_p_p.h>
@@ -53,12 +53,11 @@
/*!
\qmltype TableView
- \instantiates QQuickTableView
\inqmlmodule QtQuick
\since 5.12
\ingroup qtquick-views
\inherits Flickable
- \brief Provides a table view of items provided by the model.
+ \brief Provides a table view of items to display data from a model.
A TableView has a \l model that defines the data to be displayed, and a
\l delegate that defines how the data should be displayed.
@@ -73,25 +72,34 @@
A TableView displays data from models created from built-in QML types
such as ListModel and XmlListModel, which populates the first column only
- in a TableView. To create models with multiple columns, create a model in
- C++ that inherits QAbstractItemModel, and expose it to QML.
+ in a TableView. To create models with multiple columns, either use
+ \l TableModel or a C++ model that inherits QAbstractItemModel.
\section1 Example Usage
+ \section2 C++ Models
+
The following example shows how to create a model from C++ with multiple
columns:
- \snippet qml/tableview/tablemodel.cpp 0
+ \snippet qml/tableview/cpp-tablemodel.cpp 0
And then how to use it from QML:
- \snippet qml/tableview/tablemodel.qml 0
+ \snippet qml/tableview/cpp-tablemodel.qml 0
+
+ \section2 QML Models
+
+ For prototyping and displaying very simple data (from a web API, for
+ example), \l TableModel can be used:
+
+ \snippet qml/tableview/qml-tablemodel.qml 0
\section1 Reusing items
TableView recycles delegate items by default, instead of instantiating from
the \l delegate whenever new rows and columns are flicked into view. This
- can give a huge performance boost, depending on the complexity of the
+ approach gives a huge performance boost, depending on the complexity of the
delegate.
When an item is flicked out, it moves to the \e{reuse pool}, which is an
@@ -125,8 +133,8 @@
\section1 Row heights and column widths
When a new column is flicked into view, TableView will determine its width
- by calling the \l columnWidthProvider function. TableView itself will never
- store row height or column width, as it's designed to support large models
+ by calling the \l columnWidthProvider function. TableView does not store
+ row height or column width, as it's designed to support large models
containing any number of rows and columns. Instead, it will ask the
application whenever it needs to know.
@@ -140,9 +148,9 @@
\note The calculated width of a column is discarded when it is flicked out
of the viewport, and is recalculated if the column is flicked back in. The
calculation is always based on the items that are visible when the column
- is flicked in. This means that it can end up different each time, depending
- on which row you're at when the column enters. You should therefore have the
- same \c implicitWidth for all items in a column, or set
+ is flicked in. This means that column width can be different each time,
+ depending on which row you're at when the column enters. You should
+ therefore have the same \c implicitWidth for all items in a column, or set
\l columnWidthProvider. The same logic applies for the row height
calculation.
@@ -151,10 +159,11 @@
must call \l forceLayout. This informs TableView that it needs to use the
provider functions again to recalculate and update the layout.
- Since Qt 5.13, if you want to hide a specific column, you can return \c 0 from the
- \l columnWidthProvider for that column. Likewise, you can return 0 from the
- \l rowHeightProvider to hide a row. If you return a negative number, TableView
- will fall back to calculate the size based on the delegate items.
+ Since Qt 5.13, if you want to hide a specific column, you can return \c 0
+ from the \l columnWidthProvider for that column. Likewise, you can return 0
+ from the \l rowHeightProvider to hide a row. If you return a negative
+ number, TableView will fall back to calculate the size based on the delegate
+ items.
\note The size of a row or column should be a whole number to avoid
sub-pixel alignment of items.
@@ -168,11 +177,11 @@
\section1 Overlays and underlays
- Tableview inherits \l Flickable. And when new items are instantiated from the
- delegate, it will parent them to the \l{Flickable::}{contentItem}
- with a \c z value equal to \c 1. You can add your own items inside the
- Tableview, as child items of the Flickable. By controlling their \c z
- value, you can make them be on top of or underneath the table items.
+ All new items that are instantiated from the delegate are parented to the
+ \l{Flickable::}{contentItem} with the \c z value, \c 1. You can add your
+ own items inside the Tableview, as child items of the Flickable. By
+ controlling their \c z value, you can make them be on top of or
+ underneath the table items.
Here is an example that shows how to add some text on top of the table, that
moves together with the table as you flick:
@@ -182,6 +191,7 @@
/*!
\qmlproperty int QtQuick::TableView::rows
+ \readonly
This property holds the number of rows in the table. This is
equal to the number of rows in the model.
@@ -191,10 +201,11 @@
/*!
\qmlproperty int QtQuick::TableView::columns
+ \readonly
This property holds the number of columns in the table. This is
equal to the number of columns in the model. If the model is
- a list, columns will be 1.
+ a list, columns will be \c 1.
This property is read only.
*/
@@ -204,7 +215,7 @@
This property holds the spacing between the rows.
- The default value is 0.
+ The default value is \c 0.
*/
/*!
@@ -212,20 +223,20 @@
This property holds the spacing between the columns.
- The default value is 0.
+ The default value is \c 0.
*/
/*!
\qmlproperty var QtQuick::TableView::rowHeightProvider
This property can hold a function that returns the row height for each row
- in the model. When assigned, it will be called whenever TableView needs to
- know the height of a specific row. The function takes one argument, \c row,
- for which the TableView needs to know the height.
+ in the model. It is called whenever TableView needs to know the height of
+ a specific row. The function takes one argument, \c row, for which the
+ TableView needs to know the height.
- Since Qt 5.13, if you want to hide a specific row, you can return \c 0 height for
- that row. If you return a negative number, TableView will fall back to
- calculate the height based on the delegate items.
+ Since Qt 5.13, if you want to hide a specific row, you can return \c 0
+ height for that row. If you return a negative number, TableView calculates
+ the height based on the delegate items.
\sa columnWidthProvider, {Row heights and column widths}
*/
@@ -234,13 +245,13 @@
\qmlproperty var QtQuick::TableView::columnWidthProvider
This property can hold a function that returns the column width for each
- column in the model. When assigned, it is called whenever TableView needs
- to know the width of a specific column. The function takes one argument,
- \c column, for which the TableView needs to know the width.
+ column in the model. It is called whenever TableView needs to know the
+ width of a specific column. The function takes one argument, \c column,
+ for which the TableView needs to know the width.
- Since Qt 5.13, if you want to hide a specific column, you can return \c 0 width for
- that column. If you return a negative number, TableView will fall back to
- calculate the width based on the delegate items.
+ Since Qt 5.13, if you want to hide a specific column, you can return \c 0
+ width for that column. If you return a negative number, TableView
+ calculates the width based on the delegate items.
\sa rowHeightProvider, {Row heights and column widths}
*/
@@ -250,9 +261,9 @@
This property holds the model that provides data for the table.
The model provides the set of data that is used to create the items
- in the view. Models can be created directly in QML using \l ListModel,
- \l XmlListModel or \l ObjectModel, or provided by a custom C++ model
- class. If it is a C++ model, it must be a subclass of \l QAbstractItemModel
+ in the view. Models can be created directly in QML using \l TableModel,
+ \l ListModel, \l XmlListModel, or \l ObjectModel, or provided by a custom
+ C++ model class. The C++ model must be a subclass of \l QAbstractItemModel
or a simple list.
\sa {qml-data-models}{Data Models}
@@ -266,10 +277,9 @@
applies to \c row and \c column. Properties of the model are also available
depending upon the type of \l {qml-data-models}{Data Model}.
- A delegate should specify its size using \l [QML]{Item::implicitWidth}{implicitWidth} and
- \l [QML]{Item::implicitHeight}{implicitHeight}.
- The TableView lays out the items based on that information. Explicit width or
- height settings are ignored and overwritten.
+ A delegate should specify its size using \l{Item::}{implicitWidth} and
+ \l {Item::}{implicitHeight}. The TableView lays out the items based on that
+ information. Explicit width or height settings are ignored and overwritten.
\note Delegates are instantiated as needed and may be destroyed at any time.
They are also reused if the \l reuseItems property is set to \c true. You
@@ -291,37 +301,35 @@
/*!
\qmlproperty real QtQuick::TableView::contentWidth
- This property holds the width of the \l view, which is also
- the width of the table (including margins). As a TableView cannot
- always know the exact width of the table without loading all columns
- in the model, the \c contentWidth is usually an estimated width based on
- the columns it has seen so far. This estimate is recalculated whenever
- new columns are flicked into view, which means that the content width
- can change dynamically.
+ This property holds the table width required to accommodate the number of
+ columns in the model. This is usually not the same as the \c width of the
+ \l view, which means that the table's width could be larger or smaller than
+ the viewport width. As a TableView cannot always know the exact width of
+ the table without loading all columns in the model, the \c contentWidth is
+ usually an estimate based on the initially loaded table.
- If you know up front what the width of the table will be, assign a value
- to \c contentWidth explicitly, to avoid unnecessary calculations and
- updates to the TableView.
+ If you know what the width of the table will be, assign a value to
+ \c contentWidth, to avoid unnecessary calculations and updates to the
+ TableView.
- \sa contentHeight
+ \sa contentHeight, columnWidthProvider
*/
/*!
\qmlproperty real QtQuick::TableView::contentHeight
- This property holds the height of the \l view, which is also
- the height of the table (including margins). As a TableView cannot
- always know the exact height of the table without loading all rows
- in the model, the \c contentHeight is usually an estimated height
- based on the rows it has seen so far. This estimate is recalculated
- whenever new rows are flicked into view, which means that the content height
- can change dynamically.
+ This property holds the table height required to accommodate the number of
+ rows in the data model. This is usually not the same as the \c height of the
+ \c view, which means that the table's height could be larger or smaller than the
+ viewport height. As a TableView cannot always know the exact height of the
+ table without loading all rows in the model, the \c contentHeight is
+ usually an estimate based on the initially loaded table.
- If you know up front what the height of the table will be, assign a
- value to \c contentHeight explicitly, to avoid unnecessary calculations and
- updates to the TableView.
+ If you know what the height of the table will be, assign a
+ value to \c contentHeight, to avoid unnecessary calculations and updates to
+ the TableView.
- \sa contentWidth
+ \sa contentWidth, rowHeightProvider
*/
/*!
@@ -330,7 +338,7 @@
Responding to changes in the model are batched so that they are handled
only once per frame. This means the TableView delays showing any changes
while a script is being run. The same is also true when changing
- properties such as \l rowSpacing or \l {Item::anchors.leftMargin}{leftMargin}.
+ properties, such as \l rowSpacing or \l{Item::anchors.leftMargin}{leftMargin}.
This method forces the TableView to immediately update the layout so
that any recent changes take effect.
@@ -367,9 +375,9 @@
item has been taken out of the pool and placed inside the content view,
and the model properties such as index, row, and column have been updated.
- Other properties that are not provided by the model does not change when an item
- is reused. You should avoid storing any state inside a delegate, but if you do,
- manually reset that state on receiving this signal.
+ Other properties that are not provided by the model does not change when an
+ item is reused. You should avoid storing any state inside a delegate, but if
+ you do, manually reset that state on receiving this signal.
This signal is emitted when the item is reused, and not the first time the
item is created.
@@ -425,6 +433,10 @@ bool QQuickTableViewPrivate::EdgeRange::containsIndex(Qt::Edge edge, int index)
QQuickTableViewPrivate::QQuickTableViewPrivate()
: QQuickFlickablePrivate()
{
+ QObject::connect(&columnWidths, &QQuickTableSectionSizeProvider::sizeChanged,
+ [this] { this->forceLayout();});
+ QObject::connect(&rowHeights, &QQuickTableSectionSizeProvider::sizeChanged,
+ [this] { this->forceLayout();});
}
QQuickTableViewPrivate::~QQuickTableViewPrivate()
@@ -604,6 +616,12 @@ void QQuickTableViewPrivate::updateContentWidth()
{
Q_Q(QQuickTableView);
+ if (syncHorizontally) {
+ QBoolBlocker fixupGuard(inUpdateContentSize, true);
+ q->QQuickFlickable::setContentWidth(syncView->contentWidth());
+ return;
+ }
+
if (explicitContentWidth.isValid()) {
// Don't calculate contentWidth when it
// was set explicitly by the application.
@@ -616,6 +634,8 @@ void QQuickTableViewPrivate::updateContentWidth()
const qreal remainingSpacing = columnsRemaining * cellSpacing.width();
const qreal estimatedRemainingWidth = remainingColumnWidths + remainingSpacing;
const qreal estimatedWidth = loadedTableOuterRect.right() + estimatedRemainingWidth;
+
+ QBoolBlocker fixupGuard(inUpdateContentSize, true);
q->QQuickFlickable::setContentWidth(estimatedWidth);
}
@@ -623,6 +643,12 @@ void QQuickTableViewPrivate::updateContentHeight()
{
Q_Q(QQuickTableView);
+ if (syncVertically) {
+ QBoolBlocker fixupGuard(inUpdateContentSize, true);
+ q->QQuickFlickable::setContentHeight(syncView->contentHeight());
+ return;
+ }
+
if (explicitContentHeight.isValid()) {
// Don't calculate contentHeight when it
// was set explicitly by the application.
@@ -635,64 +661,204 @@ void QQuickTableViewPrivate::updateContentHeight()
const qreal remainingSpacing = rowsRemaining * cellSpacing.height();
const qreal estimatedRemainingHeight = remainingRowHeights + remainingSpacing;
const qreal estimatedHeight = loadedTableOuterRect.bottom() + estimatedRemainingHeight;
+
+ QBoolBlocker fixupGuard(inUpdateContentSize, true);
q->QQuickFlickable::setContentHeight(estimatedHeight);
}
-void QQuickTableViewPrivate::enforceTableAtOrigin()
+void QQuickTableViewPrivate::updateExtents()
{
- // Gaps before the first row/column can happen if rows/columns
- // changes size while flicking e.g because of spacing changes or
- // changes to a column maxWidth/row maxHeight. Check for this, and
- // move the whole table rect accordingly.
- bool layoutNeeded = false;
- const qreal flickMargin = 50;
-
- const bool noMoreColumns = nextVisibleEdgeIndexAroundLoadedTable(Qt::LeftEdge) == kEdgeIndexAtEnd;
- const bool noMoreRows = nextVisibleEdgeIndexAroundLoadedTable(Qt::TopEdge) == kEdgeIndexAtEnd;
+ // When rows or columns outside the viewport are removed or added, or a rebuild
+ // forces us to guesstimate a new top-left, the edges of the table might end up
+ // out of sync with the edges of the content view. We detect this situation here, and
+ // move the origin to ensure that there will never be gaps at the end of the table.
+ // Normally we detect that the size of the whole table is not going to be equal to the
+ // size of the content view already when we load the last row/column, and especially
+ // before it's flicked completely inside the viewport. For those cases we simply adjust
+ // the origin/endExtent, to give a smooth flicking experience.
+ // But if flicking fast (e.g with a scrollbar), it can happen that the viewport ends up
+ // outside the end of the table in just one viewport update. To avoid a "blink" in the
+ // viewport when that happens, we "move" the loaded table into the viewport to cover it.
+ Q_Q(QQuickTableView);
- if (noMoreColumns) {
- if (!qFuzzyIsNull(loadedTableOuterRect.left())) {
- // There are no more columns, but the table rect
- // is not at origin. So we move it there.
- loadedTableOuterRect.moveLeft(0);
- layoutNeeded = true;
+ bool tableMovedHorizontally = false;
+ bool tableMovedVertically = false;
+
+ const int nextLeftColumn = nextVisibleEdgeIndexAroundLoadedTable(Qt::LeftEdge);
+ const int nextRightColumn = nextVisibleEdgeIndexAroundLoadedTable(Qt::RightEdge);
+ const int nextTopRow = nextVisibleEdgeIndexAroundLoadedTable(Qt::TopEdge);
+ const int nextBottomRow = nextVisibleEdgeIndexAroundLoadedTable(Qt::BottomEdge);
+
+ if (syncHorizontally) {
+ const auto syncView_d = syncView->d_func();
+ origin.rx() = syncView_d->origin.x();
+ endExtent.rwidth() = syncView_d->endExtent.width();
+ hData.markExtentsDirty();
+ } else if (nextLeftColumn == kEdgeIndexAtEnd) {
+ // There are no more columns to load on the left side of the table.
+ // In that case, we ensure that the origin match the beginning of the table.
+ if (loadedTableOuterRect.left() > viewportRect.left()) {
+ // We have a blank area at the left end of the viewport. In that case we don't have time to
+ // wait for the viewport to move (after changing origin), since that will take an extra
+ // update cycle, which will be visible as a blink. Instead, unless the blank spot is just
+ // us overshooting, we brute force the loaded table inside the already existing viewport.
+ if (loadedTableOuterRect.left() > origin.x()) {
+ const qreal diff = loadedTableOuterRect.left() - origin.x();
+ loadedTableOuterRect.moveLeft(loadedTableOuterRect.left() - diff);
+ loadedTableInnerRect.moveLeft(loadedTableInnerRect.left() - diff);
+ tableMovedHorizontally = true;
+ }
}
- } else {
- if (loadedTableOuterRect.left() <= 0) {
- // The table rect is at origin, or outside. But we still have
- // more visible columns to the left. So we need to make some
- // space so that they can be flicked in.
- loadedTableOuterRect.moveLeft(flickMargin);
- layoutNeeded = true;
+ origin.rx() = loadedTableOuterRect.left();
+ hData.markExtentsDirty();
+ } else if (loadedTableOuterRect.left() <= origin.x() + cellSpacing.width()) {
+ // The table rect is at the origin, or outside, but we still have more
+ // visible columns to the left. So we try to guesstimate how much space
+ // the rest of the columns will occupy, and move the origin accordingly.
+ const int columnsRemaining = nextLeftColumn + 1;
+ const qreal remainingColumnWidths = columnsRemaining * averageEdgeSize.width();
+ const qreal remainingSpacing = columnsRemaining * cellSpacing.width();
+ const qreal estimatedRemainingWidth = remainingColumnWidths + remainingSpacing;
+ origin.rx() = loadedTableOuterRect.left() - estimatedRemainingWidth;
+ hData.markExtentsDirty();
+ } else if (nextRightColumn == kEdgeIndexAtEnd) {
+ // There are no more columns to load on the right side of the table.
+ // In that case, we ensure that the end of the content view match the end of the table.
+ if (loadedTableOuterRect.right() < viewportRect.right()) {
+ // We have a blank area at the right end of the viewport. In that case we don't have time to
+ // wait for the viewport to move (after changing endExtent), since that will take an extra
+ // update cycle, which will be visible as a blink. Instead, unless the blank spot is just
+ // us overshooting, we brute force the loaded table inside the already existing viewport.
+ const qreal w = qMin(viewportRect.right(), q->contentWidth() + endExtent.width());
+ if (loadedTableOuterRect.right() < w) {
+ const qreal diff = loadedTableOuterRect.right() - w;
+ loadedTableOuterRect.moveRight(loadedTableOuterRect.right() - diff);
+ loadedTableInnerRect.moveRight(loadedTableInnerRect.right() - diff);
+ tableMovedHorizontally = true;
+ }
}
- }
-
- if (noMoreRows) {
- if (!qFuzzyIsNull(loadedTableOuterRect.top())) {
- loadedTableOuterRect.moveTop(0);
- layoutNeeded = true;
+ endExtent.rwidth() = loadedTableOuterRect.right() - q->contentWidth();
+ hData.markExtentsDirty();
+ } else if (loadedTableOuterRect.right() >= q->contentWidth() + endExtent.width() - cellSpacing.width()) {
+ // The right-most column is outside the end of the content view, and we
+ // still have more visible columns in the model. This can happen if the application
+ // has set a fixed content width.
+ const int columnsRemaining = tableSize.width() - nextRightColumn;
+ const qreal remainingColumnWidths = columnsRemaining * averageEdgeSize.width();
+ const qreal remainingSpacing = columnsRemaining * cellSpacing.width();
+ const qreal estimatedRemainingWidth = remainingColumnWidths + remainingSpacing;
+ const qreal pixelsOutsideContentWidth = loadedTableOuterRect.right() - q->contentWidth();
+ endExtent.rwidth() = pixelsOutsideContentWidth + estimatedRemainingWidth;
+ hData.markExtentsDirty();
+ }
+
+ if (syncVertically) {
+ const auto syncView_d = syncView->d_func();
+ origin.ry() = syncView_d->origin.y();
+ endExtent.rheight() = syncView_d->endExtent.height();
+ vData.markExtentsDirty();
+ } else if (nextTopRow == kEdgeIndexAtEnd) {
+ // There are no more rows to load on the top side of the table.
+ // In that case, we ensure that the origin match the beginning of the table.
+ if (loadedTableOuterRect.top() > viewportRect.top()) {
+ // We have a blank area at the top of the viewport. In that case we don't have time to
+ // wait for the viewport to move (after changing origin), since that will take an extra
+ // update cycle, which will be visible as a blink. Instead, unless the blank spot is just
+ // us overshooting, we brute force the loaded table inside the already existing viewport.
+ if (loadedTableOuterRect.top() > origin.y()) {
+ const qreal diff = loadedTableOuterRect.top() - origin.y();
+ loadedTableOuterRect.moveTop(loadedTableOuterRect.top() - diff);
+ loadedTableInnerRect.moveTop(loadedTableInnerRect.top() - diff);
+ tableMovedVertically = true;
+ }
}
- } else {
- if (loadedTableOuterRect.top() <= 0) {
- loadedTableOuterRect.moveTop(flickMargin);
- layoutNeeded = true;
+ origin.ry() = loadedTableOuterRect.top();
+ vData.markExtentsDirty();
+ } else if (loadedTableOuterRect.top() <= origin.y() + cellSpacing.height()) {
+ // The table rect is at the origin, or outside, but we still have more
+ // visible rows at the top. So we try to guesstimate how much space
+ // the rest of the rows will occupy, and move the origin accordingly.
+ const int rowsRemaining = nextTopRow + 1;
+ const qreal remainingRowHeights = rowsRemaining * averageEdgeSize.height();
+ const qreal remainingSpacing = rowsRemaining * cellSpacing.height();
+ const qreal estimatedRemainingHeight = remainingRowHeights + remainingSpacing;
+ origin.ry() = loadedTableOuterRect.top() - estimatedRemainingHeight;
+ vData.markExtentsDirty();
+ } else if (nextBottomRow == kEdgeIndexAtEnd) {
+ // There are no more rows to load on the bottom side of the table.
+ // In that case, we ensure that the end of the content view match the end of the table.
+ if (loadedTableOuterRect.bottom() < viewportRect.bottom()) {
+ // We have a blank area at the bottom of the viewport. In that case we don't have time to
+ // wait for the viewport to move (after changing endExtent), since that will take an extra
+ // update cycle, which will be visible as a blink. Instead, unless the blank spot is just
+ // us overshooting, we brute force the loaded table inside the already existing viewport.
+ const qreal h = qMin(viewportRect.bottom(), q->contentHeight() + endExtent.height());
+ if (loadedTableOuterRect.bottom() < h) {
+ const qreal diff = loadedTableOuterRect.bottom() - h;
+ loadedTableOuterRect.moveBottom(loadedTableOuterRect.bottom() - diff);
+ loadedTableInnerRect.moveBottom(loadedTableInnerRect.bottom() - diff);
+ tableMovedVertically = true;
+ }
+ }
+ endExtent.rheight() = loadedTableOuterRect.bottom() - q->contentHeight();
+ vData.markExtentsDirty();
+ } else if (loadedTableOuterRect.bottom() >= q->contentHeight() + endExtent.height() - cellSpacing.height()) {
+ // The bottom-most row is outside the end of the content view, and we
+ // still have more visible rows in the model. This can happen if the application
+ // has set a fixed content height.
+ const int rowsRemaining = tableSize.height() - nextBottomRow;
+ const qreal remainingRowHeigts = rowsRemaining * averageEdgeSize.height();
+ const qreal remainingSpacing = rowsRemaining * cellSpacing.height();
+ const qreal estimatedRemainingHeight = remainingRowHeigts + remainingSpacing;
+ const qreal pixelsOutsideContentHeight = loadedTableOuterRect.bottom() - q->contentHeight();
+ endExtent.rheight() = pixelsOutsideContentHeight + estimatedRemainingHeight;
+ vData.markExtentsDirty();
+ }
+
+ if (tableMovedHorizontally || tableMovedVertically) {
+ qCDebug(lcTableViewDelegateLifecycle) << "move table to" << loadedTableOuterRect;
+
+ // relayoutTableItems() will take care of moving the existing
+ // delegate items into the new loadedTableOuterRect.
+ relayoutTableItems();
+
+ // Inform the sync children that they need to rebuild to stay in sync
+ for (auto syncChild : qAsConst(syncChildren)) {
+ auto syncChild_d = syncChild->d_func();
+ syncChild_d->scheduledRebuildOptions |= RebuildOption::ViewportOnly;
+ if (tableMovedHorizontally)
+ syncChild_d->scheduledRebuildOptions |= RebuildOption::CalculateNewTopLeftColumn;
+ if (tableMovedVertically)
+ syncChild_d->scheduledRebuildOptions |= RebuildOption::CalculateNewTopLeftRow;
}
}
- if (layoutNeeded) {
- qCDebug(lcTableViewDelegateLifecycle);
- relayoutTableItems();
+ if (hData.minExtentDirty || vData.minExtentDirty) {
+ qCDebug(lcTableViewDelegateLifecycle) << "move origin and endExtent to:" << origin << endExtent;
+ // updateBeginningEnd() will let the new extents take effect. This will also change the
+ // visualArea of the flickable, which again will cause any attached scrollbars to adjust
+ // the position of the handle. Note the latter will cause the viewport to move once more.
+ updateBeginningEnd();
}
}
void QQuickTableViewPrivate::updateAverageEdgeSize()
{
- const int loadedRowCount = loadedRows.count();
- const int loadedColumnCount = loadedColumns.count();
- const qreal accRowSpacing = (loadedRowCount - 1) * cellSpacing.height();
- const qreal accColumnSpacing = (loadedColumnCount - 1) * cellSpacing.width();
- averageEdgeSize.setHeight((loadedTableOuterRect.height() - accRowSpacing) / loadedRowCount);
- averageEdgeSize.setWidth((loadedTableOuterRect.width() - accColumnSpacing) / loadedColumnCount);
+ if (explicitContentWidth.isValid()) {
+ const qreal accColumnSpacing = (tableSize.width() - 1) * cellSpacing.width();
+ averageEdgeSize.setWidth((explicitContentWidth - accColumnSpacing) / tableSize.width());
+ } else {
+ const qreal accColumnSpacing = (loadedColumns.count() - 1) * cellSpacing.width();
+ averageEdgeSize.setWidth((loadedTableOuterRect.width() - accColumnSpacing) / loadedColumns.count());
+ }
+
+ if (explicitContentHeight.isValid()) {
+ const qreal accRowSpacing = (tableSize.height() - 1) * cellSpacing.height();
+ averageEdgeSize.setHeight((explicitContentHeight - accRowSpacing) / tableSize.height());
+ } else {
+ const qreal accRowSpacing = (loadedRows.count() - 1) * cellSpacing.height();
+ averageEdgeSize.setHeight((loadedTableOuterRect.height() - accRowSpacing) / loadedRows.count());
+ }
}
void QQuickTableViewPrivate::syncLoadedTableRectFromLoadedTable()
@@ -710,9 +876,8 @@ void QQuickTableViewPrivate::forceLayout()
if (loadedItems.isEmpty())
return;
- columnRowPositionsInvalid = true;
clearEdgeSizeCache();
- RebuildOptions rebuildOptions = RebuildOption::None;
+ RebuildOptions rebuildOptions = RebuildOption::LayoutOnly;
// Go through all columns from first to last, find the columns that used
// to be hidden and not loaded, and check if they should become visible
@@ -751,16 +916,14 @@ void QQuickTableViewPrivate::forceLayout()
break;
}
- if (rebuildOptions)
- scheduleRebuildTable(rebuildOptions);
+ scheduleRebuildTable(rebuildOptions);
- if (polishing) {
+ auto rootView = rootSyncView();
+ const bool updated = rootView->d_func()->updateTableRecursive();
+ if (!updated) {
qWarning() << "TableView::forceLayout(): Cannot do an immediate re-layout during an ongoing layout!";
- q_func()->polish();
- return;
+ rootView->polish();
}
-
- updatePolish();
}
void QQuickTableViewPrivate::syncLoadedTableFromLoadRequest()
@@ -1051,6 +1214,11 @@ qreal QQuickTableViewPrivate::getColumnLayoutWidth(int column)
if (explicitColumnWidth >= 0)
return explicitColumnWidth;
+ if (syncHorizontally) {
+ if (syncView->d_func()->loadedColumns.contains(column))
+ return syncView->d_func()->getColumnLayoutWidth(column);
+ }
+
// Iterate over the currently visible items in the column. The downside
// of doing that, is that the column width will then only be based on the implicit
// width of the currently loaded items (which can be different depending on which
@@ -1080,6 +1248,11 @@ qreal QQuickTableViewPrivate::getRowLayoutHeight(int row)
if (explicitRowHeight >= 0)
return explicitRowHeight;
+ if (syncVertically) {
+ if (syncView->d_func()->loadedRows.contains(row))
+ return syncView->d_func()->getRowLayoutHeight(row);
+ }
+
// Iterate over the currently visible items in the row. The downside
// of doing that, is that the row height will then only be based on the implicit
// height of the currently loaded items (which can be different depending on which
@@ -1109,6 +1282,13 @@ qreal QQuickTableViewPrivate::getColumnWidth(int column)
if (cachedColumnWidth.startIndex == column)
return cachedColumnWidth.size;
+ if (syncHorizontally)
+ return syncView->d_func()->getColumnWidth(column);
+
+ auto cw = columnWidths.size(column);
+ if (cw >= 0)
+ return cw;
+
if (columnWidthProvider.isUndefined())
return noExplicitColumnWidth;
@@ -1143,6 +1323,13 @@ qreal QQuickTableViewPrivate::getRowHeight(int row)
if (cachedRowHeight.startIndex == row)
return cachedRowHeight.size;
+ if (syncVertically)
+ return syncView->d_func()->getRowHeight(row);
+
+ auto rh = rowHeights.size(row);
+ if (rh >= 0)
+ return rh;
+
if (rowHeightProvider.isUndefined())
return noExplicitRowHeight;
@@ -1180,23 +1367,9 @@ bool QQuickTableViewPrivate::isRowHidden(int row)
return qFuzzyIsNull(getRowHeight(row));
}
-void QQuickTableViewPrivate::relayoutTable()
-{
- clearEdgeSizeCache();
- relayoutTableItems();
- syncLoadedTableRectFromLoadedTable();
- enforceTableAtOrigin();
- updateContentWidth();
- updateContentHeight();
- // Return back to updatePolish to loadAndUnloadVisibleEdges()
- // since the re-layout might have caused some edges to be pushed
- // out, while others might have been pushed in.
-}
-
void QQuickTableViewPrivate::relayoutTableItems()
{
qCDebug(lcTableViewDelegateLifecycle);
- columnRowPositionsInvalid = false;
qreal nextColumnX = loadedTableOuterRect.x();
qreal nextRowY = loadedTableOuterRect.y();
@@ -1378,20 +1551,7 @@ void QQuickTableViewPrivate::processLoadRequest()
if (rebuildState == RebuildState::Done) {
// Loading of this edge was not done as a part of a rebuild, but
// instead as an incremental build after e.g a flick.
- switch (loadRequest.edge()) {
- case Qt::LeftEdge:
- case Qt::TopEdge:
- enforceTableAtOrigin();
- break;
- case Qt::RightEdge:
- updateAverageEdgeSize();
- updateContentWidth();
- break;
- case Qt::BottomEdge:
- updateAverageEdgeSize();
- updateContentHeight();
- break;
- }
+ updateExtents();
drainReusePoolAfterLoadRequest();
}
@@ -1402,6 +1562,22 @@ void QQuickTableViewPrivate::processLoadRequest()
void QQuickTableViewPrivate::processRebuildTable()
{
+ Q_Q(QQuickTableView);
+
+ if (rebuildState == RebuildState::Begin) {
+ if (Q_UNLIKELY(lcTableViewDelegateLifecycle().isDebugEnabled())) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "begin rebuild:" << q;
+ if (rebuildOptions & RebuildOption::All)
+ qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::All, options:" << rebuildOptions;
+ else if (rebuildOptions & RebuildOption::ViewportOnly)
+ qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::ViewportOnly, options:" << rebuildOptions;
+ else if (rebuildOptions & RebuildOption::LayoutOnly)
+ qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::LayoutOnly, options:" << rebuildOptions;
+ else
+ Q_TABLEVIEW_UNREACHABLE(rebuildOptions);
+ }
+ }
+
moveToNextRebuildState();
if (rebuildState == RebuildState::LoadInitalTable) {
@@ -1412,12 +1588,11 @@ void QQuickTableViewPrivate::processRebuildTable()
if (rebuildState == RebuildState::VerifyTable) {
if (loadedItems.isEmpty()) {
- qCDebug(lcTableViewDelegateLifecycle()) << "no items loaded, meaning empty model, all rows or columns hidden, or no delegate";
+ qCDebug(lcTableViewDelegateLifecycle()) << "no items loaded!";
rebuildState = RebuildState::Done;
+ } else if (!moveToNextRebuildState()) {
return;
}
- if (!moveToNextRebuildState())
- return;
}
if (rebuildState == RebuildState::LayoutTable) {
@@ -1457,6 +1632,7 @@ void QQuickTableViewPrivate::processRebuildTable()
}
Q_TABLEVIEW_ASSERT(rebuildState == RebuildState::Done, int(rebuildState));
+ qCDebug(lcTableViewDelegateLifecycle()) << "rebuild complete:" << q;
}
bool QQuickTableViewPrivate::moveToNextRebuildState()
@@ -1466,53 +1642,114 @@ bool QQuickTableViewPrivate::moveToNextRebuildState()
// that the current state is not yet done.
return false;
}
- rebuildState = RebuildState(int(rebuildState) + 1);
- qCDebug(lcTableViewDelegateLifecycle()) << int(rebuildState);
- return true;
-}
-QPoint QQuickTableViewPrivate::calculateNewTopLeft()
-{
- const int firstVisibleLeft = nextVisibleEdgeIndex(Qt::RightEdge, 0);
- const int firstVisibleTop = nextVisibleEdgeIndex(Qt::BottomEdge, 0);
+ if (rebuildState == RebuildState::Begin
+ && rebuildOptions.testFlag(RebuildOption::LayoutOnly))
+ rebuildState = RebuildState::LayoutTable;
+ else
+ rebuildState = RebuildState(int(rebuildState) + 1);
- return QPoint(firstVisibleLeft, firstVisibleTop);
+ qCDebug(lcTableViewDelegateLifecycle()) << int(rebuildState);
+ return true;
}
-void QQuickTableViewPrivate::calculateTopLeft(QPoint &topLeft, QPointF &topLeftPos)
+void QQuickTableViewPrivate::calculateTopLeft(QPoint &topLeftCell, QPointF &topLeftPos)
{
if (tableSize.isEmpty()) {
- releaseLoadedItems(QQmlTableInstanceModel::NotReusable);
- topLeft = QPoint(kEdgeIndexAtEnd, kEdgeIndexAtEnd);
+ // There is no cell that can be top left
+ topLeftCell.rx() = kEdgeIndexAtEnd;
+ topLeftCell.ry() = kEdgeIndexAtEnd;
return;
}
- if (rebuildOptions & RebuildOption::All) {
- qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::All";
- releaseLoadedItems(QQmlTableInstanceModel::NotReusable);
- topLeft = calculateNewTopLeft();
- } else if (rebuildOptions & RebuildOption::ViewportOnly) {
- qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::ViewportOnly";
- releaseLoadedItems(reusableFlag);
+ if (syncHorizontally || syncVertically) {
+ const auto syncView_d = syncView->d_func();
- if (rebuildOptions & RebuildOption::CalculateNewTopLeftRow) {
- const int newRow = int(viewportRect.y() / (averageEdgeSize.height() + cellSpacing.height()));
- topLeft.ry() = qBound(0, newRow, tableSize.height() - 1);
- topLeftPos.ry() = topLeft.y() * (averageEdgeSize.height() + cellSpacing.height());
- } else {
- topLeft.ry() = qBound(0, topRow(), tableSize.height() - 1);
- topLeftPos.ry() = loadedTableOuterRect.topLeft().y();
+ if (syncView_d->loadedItems.isEmpty()) {
+ // The sync view contains no loaded items. This probably means
+ // that it has not been rebuilt yet. Which also means that
+ // we cannot rebuild anything before this happens.
+ topLeftCell.rx() = kEdgeIndexNotSet;
+ topLeftCell.ry() = kEdgeIndexNotSet;
+ return;
+ }
+
+ // Get sync view top left, and use that as our own top left (if possible)
+ const QPoint syncViewTopLeftCell(syncView_d->leftColumn(), syncView_d->topRow());
+ const auto syncViewTopLeftFxItem = syncView_d->loadedTableItem(syncViewTopLeftCell);
+ const QPointF syncViewTopLeftPos = syncViewTopLeftFxItem->geometry().topLeft();
+
+ if (syncHorizontally) {
+ topLeftCell.rx() = syncViewTopLeftCell.x();
+ topLeftPos.rx() = syncViewTopLeftPos.x();
+
+ if (topLeftCell.x() >= tableSize.width()) {
+ // Top left is outside our own model.
+ topLeftCell.rx() = kEdgeIndexAtEnd;
+ topLeftPos.rx() = kEdgeIndexAtEnd;
+ }
+ }
+
+ if (syncVertically) {
+ topLeftCell.ry() = syncViewTopLeftCell.y();
+ topLeftPos.ry() = syncViewTopLeftPos.y();
+
+ if (topLeftCell.y() >= tableSize.height()) {
+ // Top left is outside our own model.
+ topLeftCell.ry() = kEdgeIndexAtEnd;
+ topLeftPos.ry() = kEdgeIndexAtEnd;
+ }
}
- if (rebuildOptions & RebuildOption::CalculateNewTopLeftColumn) {
+
+ if (syncHorizontally && syncVertically) {
+ // We have a valid top left, so we're done
+ return;
+ }
+ }
+
+ // Since we're not sync-ing both horizontal and vertical, calculate the missing
+ // dimention(s) ourself. If we rebuild all, we find the first visible top-left
+ // item starting from cell(0, 0). Otherwise, guesstimate which row or column that
+ // should be the new top-left given the geometry of the viewport.
+
+ if (!syncHorizontally) {
+ if (rebuildOptions & RebuildOption::All) {
+ // Find the first visible column from the beginning
+ topLeftCell.rx() = nextVisibleEdgeIndex(Qt::RightEdge, 0);
+ if (topLeftCell.x() == kEdgeIndexAtEnd) {
+ // No visible column found
+ return;
+ }
+ } else if (rebuildOptions & RebuildOption::CalculateNewTopLeftColumn) {
+ // Guesstimate new top left
const int newColumn = int(viewportRect.x() / (averageEdgeSize.width() + cellSpacing.width()));
- topLeft.rx() = qBound(0, newColumn, tableSize.width() - 1);
- topLeftPos.rx() = topLeft.x() * (averageEdgeSize.width() + cellSpacing.width());
+ topLeftCell.rx() = qBound(0, newColumn, tableSize.width() - 1);
+ topLeftPos.rx() = topLeftCell.x() * (averageEdgeSize.width() + cellSpacing.width());
} else {
- topLeft.rx() = qBound(0, leftColumn(), tableSize.width() - 1);
+ // Keep the current top left, unless it's outside model
+ topLeftCell.rx() = qBound(0, leftColumn(), tableSize.width() - 1);
topLeftPos.rx() = loadedTableOuterRect.topLeft().x();
}
- } else {
- Q_TABLEVIEW_UNREACHABLE(rebuildOptions);
+ }
+
+ if (!syncVertically) {
+ if (rebuildOptions & RebuildOption::All) {
+ // Find the first visible row from the beginning
+ topLeftCell.ry() = nextVisibleEdgeIndex(Qt::BottomEdge, 0);
+ if (topLeftCell.y() == kEdgeIndexAtEnd) {
+ // No visible row found
+ return;
+ }
+ } else if (rebuildOptions & RebuildOption::CalculateNewTopLeftRow) {
+ // Guesstimate new top left
+ const int newRow = int(viewportRect.y() / (averageEdgeSize.height() + cellSpacing.height()));
+ topLeftCell.ry() = qBound(0, newRow, tableSize.height() - 1);
+ topLeftPos.ry() = topLeftCell.y() * (averageEdgeSize.height() + cellSpacing.height());
+ } else {
+ // Keep the current top left, unless it's outside model
+ topLeftCell.ry() = qBound(0, topRow(), tableSize.height() - 1);
+ topLeftPos.ry() = loadedTableOuterRect.topLeft().y();
+ }
}
}
@@ -1524,41 +1761,91 @@ void QQuickTableViewPrivate::beginRebuildTable()
QPointF topLeftPos;
calculateTopLeft(topLeft, topLeftPos);
+ if (rebuildOptions & RebuildOption::All)
+ releaseLoadedItems(QQmlTableInstanceModel::NotReusable);
+ else if (rebuildOptions & RebuildOption::ViewportOnly)
+ releaseLoadedItems(reusableFlag);
+
+ if (rebuildOptions & RebuildOption::All) {
+ origin = QPointF(0, 0);
+ endExtent = QSizeF(0, 0);
+ hData.markExtentsDirty();
+ vData.markExtentsDirty();
+ updateBeginningEnd();
+ }
+
loadedColumns.clear();
loadedRows.clear();
loadedTableOuterRect = QRect();
loadedTableInnerRect = QRect();
- columnRowPositionsInvalid = false;
clearEdgeSizeCache();
- if (topLeft.x() == kEdgeIndexAtEnd || topLeft.y() == kEdgeIndexAtEnd) {
- // No visible columns or rows, so nothing to load
+ if (syncHorizontally) {
+ setLocalViewportX(syncView->contentX());
+ viewportRect.moveLeft(syncView->d_func()->viewportRect.left());
+ }
+
+ if (syncVertically) {
+ setLocalViewportY(syncView->contentY());
+ viewportRect.moveTop(syncView->d_func()->viewportRect.top());
+ }
+
+ if (!model) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "no model found, leaving table empty";
return;
}
- loadInitialTopLeftItem(topLeft, topLeftPos);
- loadAndUnloadVisibleEdges();
-}
+ if (model->count() == 0) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "empty model found, leaving table empty";
+ return;
+ }
-void QQuickTableViewPrivate::layoutAfterLoadingInitialTable()
-{
- relayoutTable();
- updateAverageEdgeSize();
- updateContentWidth();
- updateContentHeight();
-}
+ if (tableModel && !tableModel->delegate()) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "no delegate found, leaving table empty";
+ return;
+ }
-void QQuickTableViewPrivate::loadInitialTopLeftItem(const QPoint &cell, const QPointF &pos)
-{
- Q_TABLEVIEW_ASSERT(loadedItems.isEmpty(), "");
+ if (topLeft.x() == kEdgeIndexAtEnd || topLeft.y() == kEdgeIndexAtEnd) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "no visible row or column found, leaving table empty";
+ return;
+ }
- if (tableModel && !tableModel->delegate())
+ if (topLeft.x() == kEdgeIndexNotSet || topLeft.y() == kEdgeIndexNotSet) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "could not resolve top-left item, leaving table empty";
return;
+ }
// Load top-left item. After loaded, loadItemsInsideRect() will take
// care of filling out the rest of the table.
- loadRequest.begin(cell, pos, QQmlIncubator::AsynchronousIfNested);
+ loadRequest.begin(topLeft, topLeftPos, QQmlIncubator::AsynchronousIfNested);
processLoadRequest();
+ loadAndUnloadVisibleEdges();
+}
+
+void QQuickTableViewPrivate::layoutAfterLoadingInitialTable()
+{
+ clearEdgeSizeCache();
+ relayoutTableItems();
+ syncLoadedTableRectFromLoadedTable();
+
+ if (syncView || rebuildOptions.testFlag(RebuildOption::All)) {
+ // We try to limit how often we update the content size. The main reason is that is has a
+ // tendency to cause flicker in the viewport if it happens while flicking. But another just
+ // as valid reason is that we actually never really know what the size of the full table will
+ // ever be. Even if e.g spacing changes, and we normally would assume that the size of the table
+ // would increase accordingly, the model might also at some point have removed/hidden/resized
+ // rows/columns outside the viewport. This would also affect the size, but since we don't load
+ // rows or columns outside the viewport, this information is ignored. And even if we did, we
+ // might also have been fast-flicked to a new location at some point, and started a new rebuild
+ // there based on a new guesstimated top-left cell. Either way, changing the content size
+ // based on the currently visible row/columns/spacing can be really off. So instead of pretending
+ // that we know what the actual size of the table is, we just keep the first guesstimate.
+ updateAverageEdgeSize();
+ updateContentWidth();
+ updateContentHeight();
+ }
+
+ updateExtents();
}
void QQuickTableViewPrivate::unloadEdge(Qt::Edge edge)
@@ -1573,8 +1860,6 @@ void QQuickTableViewPrivate::unloadEdge(Qt::Edge edge)
unloadItem(QPoint(column, r.key()));
loadedColumns.remove(column);
syncLoadedTableRectFromLoadedTable();
- updateAverageEdgeSize();
- updateContentWidth();
break; }
case Qt::TopEdge:
case Qt::BottomEdge: {
@@ -1583,8 +1868,6 @@ void QQuickTableViewPrivate::unloadEdge(Qt::Edge edge)
unloadItem(QPoint(c.key(), row));
loadedRows.remove(row);
syncLoadedTableRectFromLoadedTable();
- updateAverageEdgeSize();
- updateContentHeight();
break; }
}
@@ -1697,21 +1980,63 @@ void QQuickTableViewPrivate::scheduleRebuildTable(RebuildOptions options) {
return;
}
- rebuildScheduled = true;
scheduledRebuildOptions |= options;
q_func()->polish();
}
-void QQuickTableViewPrivate::invalidateColumnRowPositions() {
- columnRowPositionsInvalid = true;
- q_func()->polish();
+QQuickTableView *QQuickTableViewPrivate::rootSyncView() const
+{
+ QQuickTableView *root = const_cast<QQuickTableView *>(q_func());
+ while (QQuickTableView *view = root->d_func()->syncView)
+ root = view;
+ return root;
}
void QQuickTableViewPrivate::updatePolish()
{
+ // We always start updating from the top of the syncView tree, since
+ // the layout of a syncView child will depend on the layout of the syncView.
+ // E.g when a new column is flicked in, the syncView should load and layout
+ // the column first, before any syncChildren gets a chance to do the same.
+ Q_TABLEVIEW_ASSERT(!polishing, "recursive updatePolish() calls are not allowed!");
+ rootSyncView()->d_func()->updateTableRecursive();
+}
+
+bool QQuickTableViewPrivate::updateTableRecursive()
+{
+ if (polishing) {
+ // We're already updating the Table in this view, so
+ // we cannot continue. Signal this back by returning false.
+ // The caller can then choose to call "polish()" instead, to
+ // do the update later.
+ return false;
+ }
+
+ const bool updateComplete = updateTable();
+ if (!updateComplete)
+ return false;
+
+ for (auto syncChild : qAsConst(syncChildren)) {
+ auto syncChild_d = syncChild->d_func();
+ syncChild_d->scheduledRebuildOptions |= rebuildOptions;
+
+ const bool descendantUpdateComplete = syncChild_d->updateTableRecursive();
+ if (!descendantUpdateComplete)
+ return false;
+ }
+
+ rebuildOptions = RebuildOption::None;
+
+ return true;
+}
+
+bool QQuickTableViewPrivate::updateTable()
+{
// Whenever something changes, e.g viewport moves, spacing is set to a
// new value, model changes etc, this function will end up being called. Here
// we check what needs to be done, and load/unload cells accordingly.
+ // If we cannot complete the update (because we need to wait for an item
+ // to load async), we return false.
Q_TABLEVIEW_ASSERT(!polishing, "recursive updatePolish() calls are not allowed!");
QBoolBlocker polishGuard(polishing, true);
@@ -1721,38 +2046,42 @@ void QQuickTableViewPrivate::updatePolish()
// as an atomic operation, which means that we don't continue doing anything else until all
// items have been received and laid out. Note that updatePolish is then called once more
// after the loadRequest has completed to handle anything that might have occurred in-between.
- return;
+ return false;
}
if (rebuildState != RebuildState::Done) {
processRebuildTable();
- return;
+ return rebuildState == RebuildState::Done;
}
syncWithPendingChanges();
if (rebuildState == RebuildState::Begin) {
processRebuildTable();
- return;
+ return rebuildState == RebuildState::Done;
}
if (loadedItems.isEmpty())
- return;
-
- if (columnRowPositionsInvalid) {
- relayoutTable();
- updateAverageEdgeSize();
- updateContentWidth();
- updateContentHeight();
- }
+ return !loadRequest.isActive();
loadAndUnloadVisibleEdges();
+
+ return !loadRequest.isActive();
}
void QQuickTableViewPrivate::fixup(QQuickFlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent)
{
- if (rebuildScheduled || rebuildState != RebuildState::Done)
+ if (inUpdateContentSize) {
+ // We update the content size dynamically as we load and unload edges.
+ // Unfortunately, this also triggers a call to this function. The base
+ // implementation will do things like start a momentum animation or move
+ // the content view somewhere else, which causes glitches. This can
+ // especially happen if flicking on one of the syncView children, which triggers
+ // an update to our content size. In that case, the base implementation don't know
+ // that the view is being indirectly dragged, and will therefore do strange things as
+ // it tries to 'fixup' the geometry. So we use a guard to prevent this from happening.
return;
+ }
QQuickFlickablePrivate::fixup(data, minExtent, maxExtent);
}
@@ -1836,25 +2165,32 @@ void QQuickTableViewPrivate::syncWithPendingChanges()
// such assignments into effect until we're in a state that allows it.
Q_Q(QQuickTableView);
viewportRect = QRectF(q->contentX(), q->contentY(), q->width(), q->height());
- syncRebuildOptions();
+
syncModel();
syncDelegate();
+ syncSyncView();
+
+ syncRebuildOptions();
}
void QQuickTableViewPrivate::syncRebuildOptions()
{
- if (!rebuildScheduled)
+ if (!scheduledRebuildOptions)
return;
rebuildState = RebuildState::Begin;
rebuildOptions = scheduledRebuildOptions;
scheduledRebuildOptions = RebuildOption::None;
- rebuildScheduled = false;
- if (loadedItems.isEmpty()) {
- // If we have no items from before, we cannot just rebuild the viewport, but need
- // to rebuild everything, since we have no top-left loaded item to start from.
+ if (loadedItems.isEmpty())
rebuildOptions.setFlag(RebuildOption::All);
+
+ // Some options are exclusive:
+ if (rebuildOptions.testFlag(RebuildOption::All)) {
+ rebuildOptions.setFlag(RebuildOption::ViewportOnly, false);
+ rebuildOptions.setFlag(RebuildOption::LayoutOnly, false);
+ } else if (rebuildOptions.testFlag(RebuildOption::ViewportOnly)) {
+ rebuildOptions.setFlag(RebuildOption::LayoutOnly, false);
}
}
@@ -1899,6 +2235,60 @@ void QQuickTableViewPrivate::syncModel()
connectToModel();
}
+void QQuickTableViewPrivate::syncSyncView()
+{
+ Q_Q(QQuickTableView);
+
+ if (assignedSyncView != syncView) {
+ if (syncView)
+ syncView->d_func()->syncChildren.removeOne(q);
+
+ if (assignedSyncView) {
+ QQuickTableView *view = assignedSyncView;
+
+ while (view) {
+ if (view == q) {
+ if (!layoutWarningIssued) {
+ layoutWarningIssued = true;
+ qmlWarning(q) << "TableView: recursive syncView connection detected!";
+ }
+ syncView = nullptr;
+ return;
+ }
+ view = view->d_func()->syncView;
+ }
+
+ assignedSyncView->d_func()->syncChildren.append(q);
+ scheduledRebuildOptions |= RebuildOption::ViewportOnly;
+ }
+
+ syncView = assignedSyncView;
+ }
+
+ syncHorizontally = syncView && assignedSyncDirection & Qt::Horizontal;
+ syncVertically = syncView && assignedSyncDirection & Qt::Vertical;
+
+ if (syncHorizontally)
+ q->setColumnSpacing(syncView->columnSpacing());
+ if (syncVertically)
+ q->setRowSpacing(syncView->rowSpacing());
+
+ if (syncView && loadedItems.isEmpty() && !tableSize.isEmpty()) {
+ // When we have a syncView, we can sometimes temporarily end up with no loaded items.
+ // This can happen if the syncView has a model with more rows or columns than us, in
+ // which case the viewport can end up in a place where we have no rows or columns to
+ // show. In that case, check now if the viewport has been flicked back again, and
+ // that we can rebuild the table with a visible top-left cell.
+ const auto syncView_d = syncView->d_func();
+ if (!syncView_d->loadedItems.isEmpty()) {
+ if (syncHorizontally && syncView_d->leftColumn() <= tableSize.width() - 1)
+ scheduledRebuildOptions |= QQuickTableViewPrivate::RebuildOption::ViewportOnly;
+ else if (syncVertically && syncView_d->topRow() <= tableSize.height() - 1)
+ scheduledRebuildOptions |= QQuickTableViewPrivate::RebuildOption::ViewportOnly;
+ }
+ }
+}
+
void QQuickTableViewPrivate::connectToModel()
{
Q_TABLEVIEW_ASSERT(model, "");
@@ -2028,6 +2418,83 @@ void QQuickTableViewPrivate::modelResetCallback()
scheduleRebuildTable(RebuildOption::All);
}
+void QQuickTableViewPrivate::scheduleRebuildIfFastFlick()
+{
+ Q_Q(QQuickTableView);
+ // If the viewport has moved more than one page vertically or horizontally, we switch
+ // strategy from refilling edges around the current table to instead rebuild the table
+ // from scratch inside the new viewport. This will greatly improve performance when flicking
+ // a long distance in one go, which can easily happen when dragging on scrollbars.
+
+ // Check the viewport moved more than one page vertically
+ if (!viewportRect.intersects(QRectF(viewportRect.x(), q->contentY(), 1, q->height()))) {
+ scheduledRebuildOptions |= RebuildOption::CalculateNewTopLeftRow;
+ scheduledRebuildOptions |= RebuildOption::ViewportOnly;
+ }
+
+ // Check the viewport moved more than one page horizontally
+ if (!viewportRect.intersects(QRectF(q->contentX(), viewportRect.y(), q->width(), 1))) {
+ scheduledRebuildOptions |= RebuildOption::CalculateNewTopLeftColumn;
+ scheduledRebuildOptions |= RebuildOption::ViewportOnly;
+ }
+}
+
+void QQuickTableViewPrivate::setLocalViewportX(qreal contentX)
+{
+ // Set the new viewport position if changed, but don't trigger any
+ // rebuilds or updates. We use this function internally to distinguish
+ // external flicking from internal sync-ing of the content view.
+ Q_Q(QQuickTableView);
+ QBoolBlocker blocker(inSetLocalViewportPos, true);
+
+ if (qFuzzyCompare(contentX, q->contentX()))
+ return;
+
+ q->setContentX(contentX);
+}
+
+void QQuickTableViewPrivate::setLocalViewportY(qreal contentY)
+{
+ // Set the new viewport position if changed, but don't trigger any
+ // rebuilds or updates. We use this function internally to distinguish
+ // external flicking from internal sync-ing of the content view.
+ Q_Q(QQuickTableView);
+ QBoolBlocker blocker(inSetLocalViewportPos, true);
+
+ if (qFuzzyCompare(contentY, q->contentY()))
+ return;
+
+ q->setContentY(contentY);
+}
+
+void QQuickTableViewPrivate::syncViewportPosRecursive()
+{
+ Q_Q(QQuickTableView);
+ QBoolBlocker recursionGuard(inSyncViewportPosRecursive, true);
+
+ if (syncView) {
+ auto syncView_d = syncView->d_func();
+ if (!syncView_d->inSyncViewportPosRecursive) {
+ if (syncHorizontally)
+ syncView_d->setLocalViewportX(q->contentX());
+ if (syncVertically)
+ syncView_d->setLocalViewportY(q->contentY());
+ syncView_d->syncViewportPosRecursive();
+ }
+ }
+
+ for (auto syncChild : qAsConst(syncChildren)) {
+ auto syncChild_d = syncChild->d_func();
+ if (!syncChild_d->inSyncViewportPosRecursive) {
+ if (syncChild_d->syncHorizontally)
+ syncChild_d->setLocalViewportX(q->contentX());
+ if (syncChild_d->syncVertically)
+ syncChild_d->setLocalViewportY(q->contentY());
+ syncChild_d->syncViewportPosRecursive();
+ }
+ }
+}
+
QQuickTableView::QQuickTableView(QQuickItem *parent)
: QQuickFlickable(*(new QQuickTableViewPrivate), parent)
{
@@ -2044,6 +2511,26 @@ QQuickTableView::QQuickTableView(QQuickTableViewPrivate &dd, QQuickItem *parent)
setFlag(QQuickItem::ItemIsFocusScope);
}
+qreal QQuickTableView::minXExtent() const
+{
+ return QQuickFlickable::minXExtent() - d_func()->origin.x();
+}
+
+qreal QQuickTableView::maxXExtent() const
+{
+ return QQuickFlickable::maxXExtent() - d_func()->endExtent.width();
+}
+
+qreal QQuickTableView::minYExtent() const
+{
+ return QQuickFlickable::minYExtent() - d_func()->origin.y();
+}
+
+qreal QQuickTableView::maxYExtent() const
+{
+ return QQuickFlickable::maxYExtent() - d_func()->endExtent.height();
+}
+
int QQuickTableView::rows() const
{
return d_func()->tableSize.height();
@@ -2068,7 +2555,7 @@ void QQuickTableView::setRowSpacing(qreal spacing)
return;
d->cellSpacing.setHeight(spacing);
- d->invalidateColumnRowPositions();
+ d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::LayoutOnly);
emit rowSpacingChanged();
}
@@ -2086,7 +2573,7 @@ void QQuickTableView::setColumnSpacing(qreal spacing)
return;
d->cellSpacing.setWidth(spacing);
- d->invalidateColumnRowPositions();
+ d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::LayoutOnly);
emit columnSpacingChanged();
}
@@ -2095,7 +2582,7 @@ QJSValue QQuickTableView::rowHeightProvider() const
return d_func()->rowHeightProvider;
}
-void QQuickTableView::setRowHeightProvider(QJSValue provider)
+void QQuickTableView::setRowHeightProvider(const QJSValue &provider)
{
Q_D(QQuickTableView);
if (provider.strictlyEquals(d->rowHeightProvider))
@@ -2111,7 +2598,7 @@ QJSValue QQuickTableView::columnWidthProvider() const
return d_func()->columnWidthProvider;
}
-void QQuickTableView::setColumnWidthProvider(QJSValue provider)
+void QQuickTableView::setColumnWidthProvider(const QJSValue &provider)
{
Q_D(QQuickTableView);
if (provider.strictlyEquals(d->columnWidthProvider))
@@ -2191,6 +2678,41 @@ void QQuickTableView::setContentHeight(qreal height)
QQuickFlickable::setContentHeight(height);
}
+QQuickTableView *QQuickTableView::syncView() const
+{
+ return d_func()->assignedSyncView;
+}
+
+void QQuickTableView::setSyncView(QQuickTableView *view)
+{
+ Q_D(QQuickTableView);
+ if (d->assignedSyncView == view)
+ return;
+
+ d->assignedSyncView = view;
+ d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::ViewportOnly);
+
+ emit syncViewChanged();
+}
+
+Qt::Orientations QQuickTableView::syncDirection() const
+{
+ return d_func()->assignedSyncDirection;
+}
+
+void QQuickTableView::setSyncDirection(Qt::Orientations direction)
+{
+ Q_D(QQuickTableView);
+ if (d->assignedSyncDirection == direction)
+ return;
+
+ d->assignedSyncDirection = direction;
+ if (d->assignedSyncView)
+ d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::ViewportOnly);
+
+ emit syncDirectionChanged();
+}
+
void QQuickTableView::forceLayout()
{
d_func()->forceLayout();
@@ -2218,45 +2740,42 @@ void QQuickTableView::geometryChanged(const QRectF &newGeometry, const QRectF &o
void QQuickTableView::viewportMoved(Qt::Orientations orientation)
{
Q_D(QQuickTableView);
+
+ // If the new viewport position was set from the setLocalViewportXY()
+ // functions, we just update the position silently and return. Otherwise, if
+ // the viewport was flicked by the user, or some other control, we
+ // recursively sync all the views in the hierarchy to the same position.
QQuickFlickable::viewportMoved(orientation);
+ if (d->inSetLocalViewportPos)
+ return;
- QQuickTableViewPrivate::RebuildOptions options = QQuickTableViewPrivate::RebuildOption::None;
+ // Move all views in the syncView hierarchy to the same contentX/Y.
+ // We need to start from this view (and not the root syncView) to
+ // ensure that we respect all the individual syncDirection flags
+ // between the individual views in the hierarchy.
+ d->syncViewportPosRecursive();
- // Check the viewport moved more than one page vertically
- if (!d->viewportRect.intersects(QRectF(d->viewportRect.x(), contentY(), 1, height())))
- options |= QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftRow;
- // Check the viewport moved more than one page horizontally
- if (!d->viewportRect.intersects(QRectF(contentX(), d->viewportRect.y(), width(), 1)))
- options |= QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftColumn;
-
- if (options) {
- // When the viewport has moved more than one page vertically or horizontally, we switch
- // strategy from refilling edges around the current table to instead rebuild the table
- // from scratch inside the new viewport. This will greatly improve performance when flicking
- // a long distance in one go, which can easily happen when dragging on scrollbars.
- options |= QQuickTableViewPrivate::RebuildOption::ViewportOnly;
- d->scheduleRebuildTable(options);
- }
-
- if (d->rebuildScheduled) {
- // No reason to do anything, since we're about to rebuild the whole table anyway.
- // Besides, calling updatePolish, which will start the rebuild, can easily cause
- // binding loops to happen since we usually end up modifying the geometry of the
- // viewport (contentItem) as well.
- return;
- }
+ auto rootView = d->rootSyncView();
+ auto rootView_d = rootView->d_func();
- // Calling polish() will schedule a polish event. But while the user is flicking, several
- // mouse events will be handled before we get an updatePolish() call. And the updatePolish()
- // call will only see the last mouse position. This results in a stuttering flick experience
- // (especially on windows). We improve on this by calling updatePolish() directly. But this
- // has the pitfall that we open up for recursive callbacks. E.g while inside updatePolish(), we
- // load/unload items, and emit signals. The application can listen to those signals and set a
- // new contentX/Y on the flickable. So we need to guard for this, to avoid unexpected behavior.
- if (d->polishing)
- polish();
- else
- d->updatePolish();
+ rootView_d->scheduleRebuildIfFastFlick();
+
+ if (!rootView_d->polishScheduled) {
+ if (rootView_d->scheduledRebuildOptions) {
+ // When we need to rebuild, collecting several viewport
+ // moves and do a single polish gives a quicker UI.
+ rootView->polish();
+ } else {
+ // Updating the table right away when flicking
+ // slowly gives a smoother experience.
+ const bool updated = rootView->d_func()->updateTableRecursive();
+ if (!updated) {
+ // One, or more, of the views are already in an
+ // update, so we need to wait a cycle.
+ rootView->polish();
+ }
+ }
+ }
}
void QQuickTableViewPrivate::_q_componentFinalized()
@@ -2292,6 +2811,71 @@ void QQuickTableView::componentComplete()
d_func()->registerCallbackWhenBindingsAreEvaluated();
}
+class QObjectPrivate;
+class QQuickTableSectionSizeProviderPrivate : public QObjectPrivate {
+public:
+ QQuickTableSectionSizeProviderPrivate();
+ ~QQuickTableSectionSizeProviderPrivate();
+ QHash<int, qreal> hash;
+};
+
+QQuickTableSectionSizeProvider::QQuickTableSectionSizeProvider(QObject *parent)
+ : QObject (*(new QQuickTableSectionSizeProviderPrivate), parent)
+{
+}
+
+void QQuickTableSectionSizeProvider::setSize(int section, qreal size)
+{
+ Q_D(QQuickTableSectionSizeProvider);
+ if (section < 0 || size < 0) {
+ qmlWarning(this) << "setSize: section or size less than zero";
+ return;
+ }
+ if (qFuzzyCompare(QQuickTableSectionSizeProvider::size(section), size))
+ return;
+ d->hash.insert(section, size);
+ emit sizeChanged();
+}
+
+// return -1.0 if no valid explicit size retrieved
+qreal QQuickTableSectionSizeProvider::size(int section)
+{
+ Q_D(QQuickTableSectionSizeProvider);
+ auto it = d->hash.find(section);
+ if (it != d->hash.end())
+ return *it;
+ return -1.0;
+}
+
+// return true if section is valid
+bool QQuickTableSectionSizeProvider::resetSize(int section)
+{
+ Q_D(QQuickTableSectionSizeProvider);
+ if (d->hash.empty())
+ return false;
+
+ auto ret = d->hash.remove(section);
+ if (ret)
+ emit sizeChanged();
+ return ret;
+}
+
+void QQuickTableSectionSizeProvider::resetAll()
+{
+ Q_D(QQuickTableSectionSizeProvider);
+ d->hash.clear();
+ emit sizeChanged();
+}
+
+QQuickTableSectionSizeProviderPrivate::QQuickTableSectionSizeProviderPrivate()
+ : QObjectPrivate()
+{
+}
+
+QQuickTableSectionSizeProviderPrivate::~QQuickTableSectionSizeProviderPrivate()
+{
+
+}
#include "moc_qquicktableview_p.cpp"
QT_END_NAMESPACE
diff --git a/src/quick/items/qquicktableview_p.h b/src/quick/items/qquicktableview_p.h
index f32c71b983..a5f727d7a4 100644
--- a/src/quick/items/qquicktableview_p.h
+++ b/src/quick/items/qquicktableview_p.h
@@ -79,6 +79,8 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTableView : public QQuickFlickable
Q_PROPERTY(bool reuseItems READ reuseItems WRITE setReuseItems NOTIFY reuseItemsChanged)
Q_PROPERTY(qreal contentWidth READ contentWidth WRITE setContentWidth NOTIFY contentWidthChanged)
Q_PROPERTY(qreal contentHeight READ contentHeight WRITE setContentHeight NOTIFY contentHeightChanged)
+ Q_PROPERTY(QQuickTableView *syncView READ syncView WRITE setSyncView NOTIFY syncViewChanged REVISION 14)
+ Q_PROPERTY(Qt::Orientations syncDirection READ syncDirection WRITE setSyncDirection NOTIFY syncDirectionChanged REVISION 14)
public:
QQuickTableView(QQuickItem *parent = nullptr);
@@ -93,10 +95,10 @@ public:
void setColumnSpacing(qreal spacing);
QJSValue rowHeightProvider() const;
- void setRowHeightProvider(QJSValue provider);
+ void setRowHeightProvider(const QJSValue &provider);
QJSValue columnWidthProvider() const;
- void setColumnWidthProvider(QJSValue provider);
+ void setColumnWidthProvider(const QJSValue &provider);
virtual QVariant model() const;
virtual void setModel(const QVariant &newModel);
@@ -110,6 +112,12 @@ public:
void setContentWidth(qreal width);
void setContentHeight(qreal height);
+ QQuickTableView *syncView() const;
+ void setSyncView(QQuickTableView *view);
+
+ Qt::Orientations syncDirection() const;
+ void setSyncDirection(Qt::Orientations direction);
+
Q_INVOKABLE void forceLayout();
static QQuickTableViewAttached *qmlAttachedProperties(QObject *);
@@ -124,6 +132,8 @@ Q_SIGNALS:
void modelChanged();
void delegateChanged();
void reuseItemsChanged();
+ Q_REVISION(14) void syncViewChanged();
+ Q_REVISION(14) void syncDirectionChanged();
protected:
void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
@@ -137,6 +147,11 @@ private:
Q_DISABLE_COPY(QQuickTableView)
Q_DECLARE_PRIVATE(QQuickTableView)
+ qreal minXExtent() const override;
+ qreal maxXExtent() const override;
+ qreal minYExtent() const override;
+ qreal maxYExtent() const override;
+
Q_PRIVATE_SLOT(d_func(), void _q_componentFinalized())
};
diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h
index f2fef0d774..b66ac66dec 100644
--- a/src/quick/items/qquicktableview_p_p.h
+++ b/src/quick/items/qquicktableview_p_p.h
@@ -54,9 +54,9 @@
#include "qquicktableview_p.h"
#include <QtCore/qtimer.h>
-#include <QtQml/private/qqmltableinstancemodel_p.h>
+#include <QtQmlModels/private/qqmltableinstancemodel_p.h>
#include <QtQml/private/qqmlincubator_p.h>
-#include <QtQml/private/qqmlchangeset_p.h>
+#include <QtQmlModels/private/qqmlchangeset_p.h>
#include <QtQml/qqmlinfo.h>
#include <QtQuick/private/qquickflickable_p_p.h>
@@ -70,6 +70,25 @@ static const qreal kDefaultRowHeight = 50;
static const qreal kDefaultColumnWidth = 50;
class FxTableItem;
+class QQuickTableSectionSizeProviderPrivate;
+
+class Q_QUICK_PRIVATE_EXPORT QQuickTableSectionSizeProvider : public QObject {
+ Q_OBJECT
+
+public:
+ QQuickTableSectionSizeProvider(QObject *parent=nullptr);
+ void setSize(int section, qreal size);
+ qreal size(int section);
+ bool resetSize(int section);
+ void resetAll();
+
+Q_SIGNALS:
+ void sizeChanged();
+
+private:
+ Q_DISABLE_COPY(QQuickTableSectionSizeProvider)
+ Q_DECLARE_PRIVATE(QQuickTableSectionSizeProvider)
+};
class Q_QML_AUTOTEST_EXPORT QQuickTableViewPrivate : public QQuickFlickablePrivate
{
@@ -188,10 +207,11 @@ public:
enum class RebuildOption {
None = 0,
- ViewportOnly = 0x1,
- CalculateNewTopLeftRow = 0x2,
- CalculateNewTopLeftColumn = 0x4,
- All = 0x8,
+ LayoutOnly = 0x1,
+ ViewportOnly = 0x2,
+ CalculateNewTopLeftRow = 0x4,
+ CalculateNewTopLeftColumn = 0x8,
+ All = 0x10,
};
Q_DECLARE_FLAGS(RebuildOptions, RebuildOption)
@@ -231,6 +251,9 @@ public:
QRectF loadedTableOuterRect;
QRectF loadedTableInnerRect;
+ QPointF origin = QPointF(0, 0);
+ QSizeF endExtent = QSizeF(0, 0);
+
QRectF viewportRect = QRectF(0, 0, -1, -1);
QSize tableSize;
@@ -246,13 +269,18 @@ public:
QQmlTableInstanceModel::ReusableFlag reusableFlag = QQmlTableInstanceModel::Reusable;
bool blockItemCreatedCallback = false;
- bool columnRowPositionsInvalid = false;
bool layoutWarningIssued = false;
bool polishing = false;
- bool rebuildScheduled = true;
+ bool syncVertically = false;
+ bool syncHorizontally = false;
+ bool inSetLocalViewportPos = false;
+ bool inSyncViewportPosRecursive = false;
+ bool inUpdateContentSize = false;
QJSValue rowHeightProvider;
QJSValue columnWidthProvider;
+ QQuickTableSectionSizeProvider rowHeights;
+ QQuickTableSectionSizeProvider columnWidths;
EdgeRange cachedNextVisibleEdgeIndex[4];
EdgeRange cachedColumnWidth;
@@ -272,6 +300,11 @@ public:
QSizeF averageEdgeSize;
+ QPointer<QQuickTableView> assignedSyncView;
+ QPointer<QQuickTableView> syncView;
+ QList<QPointer<QQuickTableView> > syncChildren;
+ Qt::Orientations assignedSyncDirection = Qt::Horizontal | Qt::Vertical;
+
const static QPoint kLeft;
const static QPoint kRight;
const static QPoint kUp;
@@ -304,7 +337,10 @@ public:
inline int leftColumn() const { return loadedColumns.firstKey(); }
inline int rightColumn() const { return loadedColumns.lastKey(); }
- void relayoutTable();
+ QQuickTableView *rootSyncView() const;
+
+ bool updateTableRecursive();
+ bool updateTable();
void relayoutTableItems();
void layoutVerticalEdge(Qt::Edge tableEdge);
@@ -317,7 +353,7 @@ public:
void updateAverageEdgeSize();
void forceLayout();
- void enforceTableAtOrigin();
+ void updateExtents();
void syncLoadedTableRectFromLoadedTable();
void syncLoadedTableFromLoadRequest();
@@ -342,7 +378,6 @@ public:
void releaseLoadedItems(QQmlTableInstanceModel::ReusableFlag reusableFlag);
void unloadItem(const QPoint &cell);
- void loadInitialTopLeftItem(const QPoint &cell, const QPointF &pos);
void loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMode incubationMode);
void unloadEdge(Qt::Edge edge);
void loadAndUnloadVisibleEdges();
@@ -351,13 +386,11 @@ public:
void processRebuildTable();
bool moveToNextRebuildState();
- QPoint calculateNewTopLeft();
void calculateTopLeft(QPoint &topLeft, QPointF &topLeftPos);
void beginRebuildTable();
void layoutAfterLoadingInitialTable();
void scheduleRebuildTable(QQuickTableViewPrivate::RebuildOptions options);
- void invalidateColumnRowPositions();
int resolveImportVersion();
void createWrapperModel();
@@ -372,6 +405,7 @@ public:
inline void syncDelegate();
inline void syncModel();
inline void syncRebuildOptions();
+ inline void syncSyncView();
void connectToModel();
void disconnectFromModel();
@@ -385,6 +419,11 @@ public:
void layoutChangedCallback(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint);
void modelResetCallback();
+ void scheduleRebuildIfFastFlick();
+ void setLocalViewportX(qreal contentX);
+ void setLocalViewportY(qreal contentY);
+ void syncViewportPosRecursive();
+
void _q_componentFinalized();
void registerCallbackWhenBindingsAreEvaluated();
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index 6d343a91ce..ae849aeb4b 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -1653,13 +1653,17 @@ void QQuickText::setText(const QString &n)
if (d->text == n)
return;
- d->richText = d->format == RichText;
+ d->markdownText = d->format == MarkdownText;
+ d->richText = d->format == RichText || d->markdownText;
d->styledText = d->format == StyledText || (d->format == AutoText && Qt::mightBeRichText(n));
d->text = n;
if (isComponentComplete()) {
if (d->richText) {
d->ensureDoc();
- d->extra->doc->setText(n);
+ if (d->markdownText)
+ d->extra->doc->setMarkdownText(n);
+ else
+ d->extra->doc->setText(n);
d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
} else {
d->clearFormats();
@@ -2146,7 +2150,8 @@ void QQuickText::setTextFormat(TextFormat format)
return;
d->format = format;
bool wasRich = d->richText;
- d->richText = format == RichText;
+ d->markdownText = format == MarkdownText;
+ d->richText = format == RichText || d->markdownText;
d->styledText = format == StyledText || (format == AutoText && Qt::mightBeRichText(d->text));
if (isComponentComplete()) {
@@ -2687,7 +2692,10 @@ void QQuickText::componentComplete()
if (d->updateOnComponentComplete) {
if (d->richText) {
d->ensureDoc();
- d->extra->doc->setText(d->text);
+ if (d->markdownText)
+ d->extra->doc->setMarkdownText(d->text);
+ else
+ d->extra->doc->setText(d->text);
d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
} else {
d->rightToLeftText = d->text.isRightToLeft();
diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h
index 1af60051fb..45f387cb12 100644
--- a/src/quick/items/qquicktext_p.h
+++ b/src/quick/items/qquicktext_p.h
@@ -121,6 +121,7 @@ public:
Q_ENUM(TextStyle)
enum TextFormat { PlainText = Qt::PlainText,
RichText = Qt::RichText,
+ MarkdownText = Qt::MarkdownText,
AutoText = Qt::AutoText,
StyledText = 4 };
Q_ENUM(TextFormat)
diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h
index efa45e0958..c01998b100 100644
--- a/src/quick/items/qquicktext_p_p.h
+++ b/src/quick/items/qquicktext_p_p.h
@@ -161,6 +161,7 @@ public:
bool updateOnComponentComplete:1;
bool richText:1;
bool styledText:1;
+ bool markdownText:1;
bool widthExceeded:1;
bool heightExceeded:1;
bool internalWidthUpdate:1;
diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp
index 5c55f4f43f..3c392be751 100644
--- a/src/quick/items/qquicktextcontrol.cpp
+++ b/src/quick/items/qquicktextcontrol.cpp
@@ -113,6 +113,7 @@ QQuickTextControlPrivate::QQuickTextControlPrivate()
wordSelectionEnabled(false),
hasImState(false),
cursorRectangleChanged(false),
+ hoveredMarker(false),
lastSelectionStart(-1),
lastSelectionEnd(-1)
{}
@@ -321,6 +322,9 @@ void QQuickTextControlPrivate::setContent(Qt::TextFormat format, const QString &
formatCursor.select(QTextCursor::Document);
formatCursor.setCharFormat(charFormatForInsertion);
formatCursor.endEditBlock();
+ } else if (format == Qt::MarkdownText) {
+ doc->setBaseUrl(doc->baseUrl().adjusted(QUrl::RemoveFilename));
+ doc->setMarkdown(text);
} else {
#if QT_CONFIG(texthtmlparser)
doc->setHtml(text);
@@ -799,6 +803,12 @@ void QQuickTextControl::setPlainText(const QString &text)
d->setContent(Qt::PlainText, text);
}
+void QQuickTextControl::setMarkdownText(const QString &text)
+{
+ Q_D(QQuickTextControl);
+ d->setContent(Qt::MarkdownText, text);
+}
+
void QQuickTextControl::setHtml(const QString &text)
{
Q_D(QQuickTextControl);
@@ -1025,6 +1035,8 @@ void QQuickTextControlPrivate::mousePressEvent(QMouseEvent *e, const QPointF &po
cursor.clearSelection();
}
}
+ if (interactionFlags & Qt::TextEditable)
+ blockWithMarkerUnderMousePress = q->blockWithMarkerAt(pos);
if (e->button() & Qt::MiddleButton) {
return;
} else if (!(e->button() & Qt::LeftButton)) {
@@ -1196,6 +1208,16 @@ void QQuickTextControlPrivate::mouseReleaseEvent(QMouseEvent *e, const QPointF &
q->updateCursorRectangle(true);
}
+ if ((interactionFlags & Qt::TextEditable) && (e->button() & Qt::LeftButton) && blockWithMarkerUnderMousePress.isValid()) {
+ QTextBlock block = q->blockWithMarkerAt(pos);
+ if (block == blockWithMarkerUnderMousePress) {
+ auto fmt = block.blockFormat();
+ fmt.setMarker(fmt.marker() == QTextBlockFormat::MarkerType::Unchecked ?
+ QTextBlockFormat::MarkerType::Checked : QTextBlockFormat::MarkerType::Unchecked);
+ cursor.setBlockFormat(fmt);
+ }
+ }
+
if (interactionFlags & Qt::LinksAccessibleByMouse) {
if (!(e->button() & Qt::LeftButton))
return;
@@ -1375,7 +1397,7 @@ QVariant QQuickTextControl::inputMethodQuery(Qt::InputMethodQuery property) cons
return inputMethodQuery(property, QVariant());
}
-QVariant QQuickTextControl::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const
+QVariant QQuickTextControl::inputMethodQuery(Qt::InputMethodQuery property, const QVariant &argument) const
{
Q_D(const QQuickTextControl);
QTextBlock block = d->cursor.block();
@@ -1478,8 +1500,15 @@ void QQuickTextControlPrivate::hoverEvent(QHoverEvent *e, const QPointF &pos)
if (hoveredLink != link) {
hoveredLink = link;
emit q->linkHovered(link);
+ qCDebug(DBG_HOVER_TRACE) << q << e->type() << pos << "hoveredLink" << hoveredLink;
+ } else {
+ QTextBlock block = q->blockWithMarkerAt(pos);
+ if (block.isValid() != hoveredMarker)
+ emit q->markerHovered(block.isValid());
+ hoveredMarker = block.isValid();
+ if (hoveredMarker)
+ qCDebug(DBG_HOVER_TRACE) << q << e->type() << pos << "hovered marker" << int(block.blockFormat().marker()) << block.text();
}
- qCDebug(DBG_HOVER_TRACE) << q << e->type() << pos << "hoveredLink" << hoveredLink;
}
bool QQuickTextControl::hasImState() const
@@ -1555,6 +1584,12 @@ QString QQuickTextControl::anchorAt(const QPointF &pos) const
return d->doc->documentLayout()->anchorAt(pos);
}
+QTextBlock QQuickTextControl::blockWithMarkerAt(const QPointF &pos) const
+{
+ Q_D(const QQuickTextControl);
+ return d->doc->documentLayout()->blockWithMarkerAt(pos);
+}
+
void QQuickTextControl::setAcceptRichText(bool accept)
{
Q_D(QQuickTextControl);
@@ -1784,6 +1819,13 @@ QString QQuickTextControl::toHtml() const
}
#endif
+#if QT_CONFIG(textmarkdownwriter)
+QString QQuickTextControl::toMarkdown() const
+{
+ return document()->toMarkdown();
+}
+#endif
+
bool QQuickTextControl::cursorOn() const
{
Q_D(const QQuickTextControl);
diff --git a/src/quick/items/qquicktextcontrol_p.h b/src/quick/items/qquicktextcontrol_p.h
index c99736a874..41b8ed7821 100644
--- a/src/quick/items/qquicktextcontrol_p.h
+++ b/src/quick/items/qquicktextcontrol_p.h
@@ -93,6 +93,9 @@ public:
#if QT_CONFIG(texthtmlparser)
QString toHtml() const;
#endif
+#if QT_CONFIG(textmarkdownwriter)
+ QString toMarkdown() const;
+#endif
bool hasImState() const;
bool overwriteMode() const;
@@ -107,6 +110,7 @@ public:
QString hoveredLink() const;
QString anchorAt(const QPointF &pos) const;
+ QTextBlock blockWithMarkerAt(const QPointF &pos) const;
void setCursorWidth(int width);
@@ -128,6 +132,7 @@ public:
public Q_SLOTS:
void setPlainText(const QString &text);
+ void setMarkdownText(const QString &text);
void setHtml(const QString &text);
#if QT_CONFIG(clipboard)
@@ -160,6 +165,8 @@ Q_SIGNALS:
void cursorRectangleChanged();
void linkActivated(const QString &link);
void linkHovered(const QString &link);
+ void markerClicked();
+ void markerHovered(bool marker);
public:
virtual void processEvent(QEvent *e, const QMatrix &matrix);
@@ -167,7 +174,7 @@ public:
#if QT_CONFIG(im)
virtual QVariant inputMethodQuery(Qt::InputMethodQuery property) const;
- Q_INVOKABLE QVariant inputMethodQuery(Qt::InputMethodQuery query, QVariant argument) const;
+ Q_INVOKABLE QVariant inputMethodQuery(Qt::InputMethodQuery query, const QVariant &argument) const;
#endif
virtual QMimeData *createMimeDataFromSelection() const;
diff --git a/src/quick/items/qquicktextcontrol_p_p.h b/src/quick/items/qquicktextcontrol_p_p.h
index 431bf1b6c4..d52200b49d 100644
--- a/src/quick/items/qquicktextcontrol_p_p.h
+++ b/src/quick/items/qquicktextcontrol_p_p.h
@@ -54,6 +54,7 @@
#include "QtGui/qtextdocumentfragment.h"
#include "QtGui/qtextcursor.h"
#include "QtGui/qtextformat.h"
+#include "QtGui/qtextobject.h"
#include "QtGui/qabstracttextdocumentlayout.h"
#include "QtCore/qbasictimer.h"
#include "QtCore/qpointer.h"
@@ -139,6 +140,7 @@ public:
QString anchorOnMousePress;
QString linkToCopy;
QString hoveredLink;
+ QTextBlock blockWithMarkerUnderMousePress;
QBasicTimer cursorBlinkTimer;
ulong timestampAtLastDoubleClick = 0; // will only be set at a double click
@@ -163,6 +165,7 @@ public:
bool wordSelectionEnabled : 1;
bool hasImState : 1;
bool cursorRectangleChanged : 1;
+ bool hoveredMarker: 1;
int lastSelectionStart;
int lastSelectionEnd;
diff --git a/src/quick/items/qquicktextdocument.cpp b/src/quick/items/qquicktextdocument.cpp
index 06ac5804c4..021bbca0f6 100644
--- a/src/quick/items/qquicktextdocument.cpp
+++ b/src/quick/items/qquicktextdocument.cpp
@@ -237,6 +237,14 @@ void QQuickTextDocumentWithImageResources::setText(const QString &text)
#endif
}
+#if QT_CONFIG(textmarkdownreader)
+void QQuickTextDocumentWithImageResources::setMarkdownText(const QString &text)
+{
+ clearResources();
+ QTextDocument::setMarkdown(text);
+}
+#endif
+
QSet<QUrl> QQuickTextDocumentWithImageResources::errors;
QT_END_NAMESPACE
diff --git a/src/quick/items/qquicktextdocument_p.h b/src/quick/items/qquicktextdocument_p.h
index 1218b12b89..9c5152d442 100644
--- a/src/quick/items/qquicktextdocument_p.h
+++ b/src/quick/items/qquicktextdocument_p.h
@@ -73,6 +73,9 @@ public:
virtual ~QQuickTextDocumentWithImageResources();
void setText(const QString &);
+#if QT_CONFIG(textmarkdownreader)
+ void setMarkdownText(const QString &);
+#endif
int resourcesLoading() const { return outstanding; }
QSizeF intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format) override;
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index bdfbb979dd..7d34cc3f56 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -194,6 +194,11 @@ QString QQuickTextEdit::text() const
d->text = d->control->toHtml();
else
#endif
+#if QT_CONFIG(textmarkdownwriter)
+ if (d->markdownText)
+ d->text = d->control->toMarkdown();
+ else
+#endif
d->text = d->control->toPlainText();
d->textCached = true;
}
@@ -407,6 +412,7 @@ void QQuickTextEdit::setText(const QString &text)
d->document->clearResources();
d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text));
+ d->markdownText = d->format == MarkdownText;
if (!isComponentComplete()) {
d->text = text;
} else if (d->richText) {
@@ -415,6 +421,8 @@ void QQuickTextEdit::setText(const QString &text)
#else
d->control->setPlainText(text);
#endif
+ } else if (d->markdownText) {
+ d->control->setMarkdownText(text);
} else {
d->control->setPlainText(text);
}
@@ -486,6 +494,7 @@ void QQuickTextEdit::setTextFormat(TextFormat format)
bool wasRich = d->richText;
d->richText = format == RichText || (format == AutoText && (wasRich || Qt::mightBeRichText(text())));
+ d->markdownText = format == MarkdownText;
#if QT_CONFIG(texthtmlparser)
if (isComponentComplete()) {
@@ -1463,8 +1472,12 @@ void QQuickTextEdit::componentComplete()
d->control->setHtml(d->text);
else
#endif
- if (!d->text.isEmpty())
- d->control->setPlainText(d->text);
+ if (!d->text.isEmpty()) {
+ if (d->markdownText)
+ d->control->setMarkdownText(d->text);
+ else
+ d->control->setPlainText(d->text);
+ }
if (d->dirty) {
d->determineHorizontalAlignment();
@@ -2309,6 +2322,7 @@ void QQuickTextEditPrivate::init()
QObject::connect(document, &QQuickTextDocumentWithImageResources::contentsChange, q, &QQuickTextEdit::q_contentsChange);
QObject::connect(document->documentLayout(), &QAbstractTextDocumentLayout::updateBlock, q, &QQuickTextEdit::invalidateBlock);
QObject::connect(control, &QQuickTextControl::linkHovered, q, &QQuickTextEdit::q_linkHovered);
+ QObject::connect(control, &QQuickTextControl::markerHovered, q, &QQuickTextEdit::q_markerHovered);
document->setDefaultFont(font);
document->setDocumentMargin(textMargin);
@@ -2600,6 +2614,19 @@ void QQuickTextEdit::q_linkHovered(const QString &link)
#endif
}
+void QQuickTextEdit::q_markerHovered(bool hovered)
+{
+ Q_D(QQuickTextEdit);
+#if QT_CONFIG(cursor)
+ if (!hovered) {
+ setCursor(d->cursorToRestoreAfterHover);
+ } else if (cursor().shape() != Qt::PointingHandCursor) {
+ d->cursorToRestoreAfterHover = cursor().shape();
+ setCursor(Qt::PointingHandCursor);
+ }
+#endif
+}
+
void QQuickTextEdit::q_updateAlignment()
{
Q_D(QQuickTextEdit);
diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h
index 259a614d6b..2d1b6c7f9c 100644
--- a/src/quick/items/qquicktextedit_p.h
+++ b/src/quick/items/qquicktextedit_p.h
@@ -134,7 +134,8 @@ public:
enum TextFormat {
PlainText = Qt::PlainText,
RichText = Qt::RichText,
- AutoText = Qt::AutoText
+ AutoText = Qt::AutoText,
+ MarkdownText = Qt::MarkdownText
};
Q_ENUM(TextFormat)
@@ -375,6 +376,7 @@ private Q_SLOTS:
void invalidateBlock(const QTextBlock &block);
void updateCursor();
void q_linkHovered(const QString &link);
+ void q_markerHovered(bool hovered);
void q_updateAlignment();
void updateSize();
void triggerPreprocess();
diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h
index 389ce3175c..be555d44fd 100644
--- a/src/quick/items/qquicktextedit_p_p.h
+++ b/src/quick/items/qquicktextedit_p_p.h
@@ -127,7 +127,7 @@ public:
, focusOnPress(true), persistentSelection(false), requireImplicitWidth(false)
, selectByMouse(false), canPaste(false), canPasteValid(false), hAlignImplicit(true)
, textCached(true), inLayout(false), selectByKeyboard(false), selectByKeyboardSet(false)
- , hadSelection(false)
+ , hadSelection(false), markdownText(false)
{
}
@@ -225,6 +225,7 @@ public:
bool selectByKeyboard:1;
bool selectByKeyboardSet:1;
bool hadSelection : 1;
+ bool markdownText : 1;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index 182889b14e..a83b9beaa5 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -1009,9 +1009,10 @@ void QQuickTextInput::setAutoScroll(bool b)
an acceptable or intermediate state. The accepted signal will only be sent
if the text is in an acceptable state when enter is pressed.
- Currently supported validators are IntValidator, DoubleValidator and
- RegExpValidator. An example of using validators is shown below, which allows
- input of integers between 11 and 31 into the text input:
+ Currently supported validators are IntValidator, DoubleValidator,
+ RegExpValidator and RegularExpressionValidator. An example of using
+ validators is shown below, which allows input of integers between 11 and 31
+ into the text input:
\code
import QtQuick 2.0
@@ -1957,7 +1958,7 @@ QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
return inputMethodQuery(property, QVariant());
}
-QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const
+QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property, const QVariant &argument) const
{
Q_D(const QQuickTextInput);
switch (property) {
diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h
index c46a2f8128..c10b06d02a 100644
--- a/src/quick/items/qquicktextinput_p.h
+++ b/src/quick/items/qquicktextinput_p.h
@@ -93,7 +93,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextInput : public QQuickImplicitSizeItem
Q_PROPERTY(EchoMode echoMode READ echoMode WRITE setEchoMode NOTIFY echoModeChanged)
Q_PROPERTY(bool activeFocusOnPress READ focusOnPress WRITE setFocusOnPress NOTIFY activeFocusOnPressChanged)
Q_PROPERTY(QString passwordCharacter READ passwordCharacter WRITE setPasswordCharacter NOTIFY passwordCharacterChanged)
- Q_PROPERTY(int passwordMaskDelay READ passwordMaskDelay WRITE setPasswordMaskDelay RESET resetPasswordMaskDelay NOTIFY passwordMaskDelayChanged REVISION 3)
+ Q_PROPERTY(int passwordMaskDelay READ passwordMaskDelay WRITE setPasswordMaskDelay RESET resetPasswordMaskDelay NOTIFY passwordMaskDelayChanged REVISION 4)
Q_PROPERTY(QString displayText READ displayText NOTIFY displayTextChanged)
Q_PROPERTY(QString preeditText READ preeditText NOTIFY preeditTextChanged REVISION 7)
Q_PROPERTY(bool autoScroll READ autoScroll WRITE setAutoScroll NOTIFY autoScrollChanged)
@@ -268,7 +268,7 @@ public:
#if QT_CONFIG(im)
QVariant inputMethodQuery(Qt::InputMethodQuery property) const override;
- Q_REVISION(3) Q_INVOKABLE QVariant inputMethodQuery(Qt::InputMethodQuery query, QVariant argument) const;
+ Q_REVISION(4) Q_INVOKABLE QVariant inputMethodQuery(Qt::InputMethodQuery query, const QVariant &argument) const;
#endif
QRectF boundingRect() const override;
@@ -336,7 +336,7 @@ Q_SIGNALS:
void inputMaskChanged(const QString &inputMask);
void echoModeChanged(QQuickTextInput::EchoMode echoMode);
void passwordCharacterChanged();
- Q_REVISION(3) void passwordMaskDelayChanged(int delay);
+ Q_REVISION(4) void passwordMaskDelayChanged(int delay);
void displayTextChanged();
Q_REVISION(7) void preeditTextChanged();
void activeFocusOnPressChanged(bool activeFocusOnPress);
@@ -399,7 +399,7 @@ public Q_SLOTS:
void redo();
void insert(int position, const QString &text);
void remove(int start, int end);
- Q_REVISION(3) void ensureVisible(int position);
+ Q_REVISION(4) void ensureVisible(int position);
Q_REVISION(7) void clear();
private Q_SLOTS:
diff --git a/src/quick/items/qquicktextnode.cpp b/src/quick/items/qquicktextnode.cpp
index 0dd12207b7..9110a0664f 100644
--- a/src/quick/items/qquicktextnode.cpp
+++ b/src/quick/items/qquicktextnode.cpp
@@ -160,7 +160,7 @@ void QQuickTextNode::addRectangleNode(const QRectF &rect, const QColor &color)
void QQuickTextNode::addImage(const QRectF &rect, const QImage &image)
{
QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext();
- QSGInternalImageNode *node = sg->sceneGraphContext()->createInternalImageNode();
+ QSGInternalImageNode *node = sg->sceneGraphContext()->createInternalImageNode(sg);
QSGTexture *texture = sg->createTexture(image);
if (m_ownerElement->smooth())
texture->setFiltering(QSGTexture::Linear);
diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp
index 1119a3d5b4..a0ae4e5f97 100644
--- a/src/quick/items/qquicktextnodeengine.cpp
+++ b/src/quick/items/qquicktextnodeengine.cpp
@@ -638,7 +638,7 @@ void QQuickTextNodeEngine::addBorder(const QRectF &rect, qreal border,
QTextFrameFormat::BorderStyle borderStyle,
const QBrush &borderBrush)
{
- QColor color = borderBrush.color();
+ const QColor &color = borderBrush.color();
// Currently we don't support other styles than solid
Q_UNUSED(borderStyle);
@@ -1011,6 +1011,17 @@ void QQuickTextNodeEngine::addTextBlock(QTextDocument *textDocument, const QText
break;
};
+ switch (block.blockFormat().marker()) {
+ case QTextBlockFormat::MarkerType::Checked:
+ listItemBullet = QChar(0x2612); // Checked checkbox
+ break;
+ case QTextBlockFormat::MarkerType::Unchecked:
+ listItemBullet = QChar(0x2610); // Unchecked checkbox
+ break;
+ case QTextBlockFormat::MarkerType::NoMarker:
+ break;
+ }
+
QSizeF size(fontMetrics.horizontalAdvance(listItemBullet), fontMetrics.height());
qreal xoff = fontMetrics.horizontalAdvance(QLatin1Char(' '));
if (block.textDirection() == Qt::LeftToRight)
diff --git a/src/quick/items/qquickview.cpp b/src/quick/items/qquickview.cpp
index c411da9519..17fc16d44b 100644
--- a/src/quick/items/qquickview.cpp
+++ b/src/quick/items/qquickview.cpp
@@ -44,8 +44,6 @@
#include "qquickitem_p.h"
#include "qquickitemchangelistener_p.h"
-#include <private/qqmlmemoryprofiler_p.h>
-
#include <QtQml/qqmlengine.h>
#include <private/qqmlengine_p.h>
#include <private/qv4qobjectwrapper_p.h>
@@ -101,7 +99,6 @@ void QQuickViewPrivate::execute()
component = nullptr;
}
if (!source.isEmpty()) {
- QML_MEMORY_SCOPE_URL(engine.data()->baseUrl().resolved(source));
component = new QQmlComponent(engine.data(), source, q);
if (!component->isLoading()) {
q->continueExecute();
@@ -243,6 +240,22 @@ void QQuickView::setSource(const QUrl& url)
}
/*!
+ Sets the initial properties with which the QML component gets initialized after
+ calling \l QQuickView::setSource.
+
+
+ Note that you can only use this function to initialize toplevel properties.
+
+ \sa QQmlComponent::createWithInitialProperties
+ \since 5.14
+*/
+void QQuickView::setInitialProperties(const QVariantMap &initialProperties)
+{
+ Q_D(QQuickView);
+ d->initialProperties = initialProperties;
+}
+
+/*!
\internal
Set the source \a url, \a component and content \a item (root of the QML object hierarchy) directly.
@@ -474,7 +487,7 @@ void QQuickView::continueExecute()
return;
}
- QObject *obj = d->component->create();
+ QObject *obj = d->initialProperties.empty() ? d->component->create() : d->component->createWithInitialProperties(d->initialProperties);
if (d->component->isError()) {
const QList<QQmlError> errorList = d->component->errors();
diff --git a/src/quick/items/qquickview.h b/src/quick/items/qquickview.h
index ecae25e90b..4122fcac79 100644
--- a/src/quick/items/qquickview.h
+++ b/src/quick/items/qquickview.h
@@ -88,6 +88,7 @@ public:
public Q_SLOTS:
void setSource(const QUrl&);
+ void setInitialProperties(const QVariantMap &initialProperties);
void setContent(const QUrl& url, QQmlComponent *component, QObject *item);
Q_SIGNALS:
diff --git a/src/quick/items/qquickview_p.h b/src/quick/items/qquickview_p.h
index 3f284c0519..b1ab8d8e8c 100644
--- a/src/quick/items/qquickview_p.h
+++ b/src/quick/items/qquickview_p.h
@@ -108,6 +108,8 @@ public:
QQuickView::ResizeMode resizeMode;
QSize initialSize;
QElapsedTimer frameTimer;
+
+ QVariantMap initialProperties;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 2316a6c6af..8fb023cf61 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -45,13 +45,16 @@
#include "qquickitem_p.h"
#include "qquickevents_p_p.h"
+#if QT_CONFIG(quick_draganddrop)
#include <private/qquickdrag_p.h>
+#endif
#include <private/qquickhoverhandler_p.h>
#include <private/qquickpointerhandler_p.h>
#include <QtQuick/private/qsgrenderer_p.h>
-#include <QtQuick/private/qsgtexture_p.h>
+#include <QtQuick/private/qsgplaintexture_p.h>
#include <private/qsgrenderloop_p.h>
+#include <private/qsgrhisupport_p.h>
#include <private/qquickrendercontrol_p.h>
#include <private/qquickanimatorcontroller_p.h>
#include <private/qquickprofiler_p.h>
@@ -73,7 +76,6 @@
#include <QtQuick/private/qquickpixmapcache_p.h>
-#include <private/qqmlmemoryprofiler_p.h>
#include <private/qqmldebugserviceinterfaces_p.h>
#include <private/qqmldebugconnector_p.h>
#if QT_CONFIG(opengl)
@@ -84,6 +86,8 @@
#include <private/qdebug_p.h>
#endif
+#include <QtGui/private/qrhi_p.h>
+
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(DBG_TOUCH, "qt.quick.touch")
@@ -98,6 +102,7 @@ Q_LOGGING_CATEGORY(DBG_DIRTY, "qt.quick.dirty")
Q_LOGGING_CATEGORY(lcTransient, "qt.quick.window.transient")
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
+extern Q_GUI_EXPORT bool qt_sendShortcutOverrideEvent(QObject *o, ulong timestamp, int k, Qt::KeyboardModifiers mods, const QString &text = QString(), bool autorep = false, ushort count = 1);
bool QQuickWindowPrivate::defaultAlphaBuffer = false;
@@ -418,9 +423,15 @@ void forceUpdate(QQuickItem *item)
void QQuickWindowPrivate::syncSceneGraph()
{
- QML_MEMORY_SCOPE_STRING("SceneGraph");
Q_Q(QQuickWindow);
+ // Calculate the dpr the same way renderSceneGraph() will.
+ qreal devicePixelRatio = q->effectiveDevicePixelRatio();
+ if (renderTargetId && !QQuickRenderControl::renderWindowFor(q))
+ devicePixelRatio = 1;
+
+ context->prepareSync(devicePixelRatio);
+
animationController->beforeNodeSync();
emit q->beforeSynchronizing();
@@ -451,13 +462,40 @@ void QQuickWindowPrivate::syncSceneGraph()
runAndClearJobs(&afterSynchronizingJobs);
}
-void QQuickWindowPrivate::renderSceneGraph(const QSize &size)
+void QQuickWindowPrivate::emitBeforeRenderPassRecording(void *ud)
+{
+ QQuickWindow *w = reinterpret_cast<QQuickWindow *>(ud);
+ emit w->beforeRenderPassRecording();
+}
+
+void QQuickWindowPrivate::emitAfterRenderPassRecording(void *ud)
+{
+ QQuickWindow *w = reinterpret_cast<QQuickWindow *>(ud);
+ emit w->afterRenderPassRecording();
+}
+
+void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfaceSize)
{
- QML_MEMORY_SCOPE_STRING("SceneGraph");
Q_Q(QQuickWindow);
if (!renderer)
return;
+ if (rhi) {
+ // ### no offscreen ("renderTargetId") support yet
+ context->beginNextRhiFrame(renderer,
+ swapchain->currentFrameRenderTarget(),
+ rpDescForSwapchain,
+ swapchain->currentFrameCommandBuffer(),
+ emitBeforeRenderPassRecording,
+ emitAfterRenderPassRecording,
+ q);
+ } else {
+ context->beginNextFrame(renderer,
+ emitBeforeRenderPassRecording,
+ emitAfterRenderPassRecording,
+ q);
+ }
+
animationController->advance();
emit q->beforeRendering();
runAndClearJobs(&beforeRenderingJobs);
@@ -477,17 +515,45 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size)
renderer->setDevicePixelRatio(1);
}
} else {
- QRect rect(QPoint(0, 0), devicePixelRatio * size);
+ 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(QRect(QPoint(0, 0), size));
+ const bool flipY = rhi ? !rhi->isYUpInNDC() : false;
+ QSGAbstractRenderer::MatrixTransformFlags matrixFlags = 0;
+ if (flipY)
+ matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY;
+ renderer->setProjectionMatrixToRect(QRectF(QPoint(0, 0), logicalSize), matrixFlags);
renderer->setDevicePixelRatio(devicePixelRatio);
}
- context->renderNextFrame(renderer, fboId);
+ if (rhi)
+ context->renderNextRhiFrame(renderer);
+ else
+ context->renderNextFrame(renderer, fboId);
}
emit q->afterRendering();
runAndClearJobs(&afterRenderingJobs);
+
+ if (rhi)
+ context->endNextRhiFrame(renderer);
+ else
+ context->endNextFrame(renderer);
+
+ if (renderer->hasCustomRenderModeWithContinuousUpdate()) {
+ // For the overdraw visualizer. This update is not urgent so avoid a
+ // direct update() call, this is only here to keep the overdraw
+ // visualization box rotating even when the scene is static.
+ QCoreApplication::postEvent(q, new QEvent(QEvent::Type(FullUpdateRequest)));
+ }
}
QQuickWindowPrivate::QQuickWindowPrivate()
@@ -496,7 +562,7 @@ QQuickWindowPrivate::QQuickWindowPrivate()
#if QT_CONFIG(cursor)
, cursorItem(nullptr)
#endif
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
, dragGrabber(nullptr)
#endif
, touchMouseId(-1)
@@ -523,8 +589,11 @@ QQuickWindowPrivate::QQuickWindowPrivate()
, renderTargetId(0)
, vaoHelper(nullptr)
, incubationController(nullptr)
+ , hasActiveSwapchain(false)
+ , hasRenderableSwapchain(false)
+ , swapchainJustBecameRenderable(false)
{
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
dragGrabber = new QQuickDragGrabber;
#endif
}
@@ -540,6 +609,7 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
{
q_ptr = c;
+
Q_Q(QQuickWindow);
contentItem = new QQuickRootItem;
@@ -577,8 +647,12 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
q->setSurfaceType(windowManager ? windowManager->windowSurfaceType() : QSurface::OpenGLSurface);
q->setFormat(sg->defaultSurfaceFormat());
+#if QT_CONFIG(vulkan)
+ if (q->surfaceType() == QSurface::VulkanSurface)
+ q->setVulkanInstance(QSGRhiSupport::vulkanInstance());
+#endif
- animationController = new QQuickAnimatorController(q);
+ animationController.reset(new QQuickAnimatorController(q));
QObject::connect(context, SIGNAL(initialized()), q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection);
QObject::connect(context, SIGNAL(invalidated()), q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection);
@@ -639,7 +713,7 @@ bool QQuickWindowPrivate::checkIfDoubleTapped(ulong newPressEventTimestamp, QPoi
if (touchMousePressTimestamp > 0) {
QPoint distanceBetweenPresses = newPressPos - touchMousePressPos;
- const int doubleTapDistance = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::TouchDoubleTapDistance).toInt();
+ const int doubleTapDistance = QGuiApplication::styleHints()->touchDoubleTapDistance();
doubleClicked = (qAbs(distanceBetweenPresses.x()) <= doubleTapDistance) && (qAbs(distanceBetweenPresses.y()) <= doubleTapDistance);
if (doubleClicked) {
@@ -1346,7 +1420,7 @@ QQuickWindow::~QQuickWindow()
}
delete d->incubationController; d->incubationController = nullptr;
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
delete d->dragGrabber; d->dragGrabber = nullptr;
#endif
QQuickRootItem *root = d->contentItem;
@@ -1578,6 +1652,7 @@ bool QQuickWindowPrivate::clearHover(ulong timestamp)
bool accepted = false;
for (QQuickItem* item : qAsConst(hoverItems)) {
accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), timestamp, true) || accepted;
+#if QT_CONFIG(cursor)
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
if (itemPrivate->hasPointerHandlers()) {
pos = q->mapFromGlobal(QCursor::pos());
@@ -1589,6 +1664,7 @@ bool QQuickWindowPrivate::clearHover(ulong timestamp)
if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h))
hh->handlePointerEvent(pointerEvent);
}
+#endif
}
hoverItems.clear();
return accepted;
@@ -1627,7 +1703,9 @@ bool QQuickWindow::event(QEvent *e)
QGuiApplication::keyboardModifiers(), 0L, accepted);
d->lastMousePosition = enter->windowPos();
enter->setAccepted(accepted);
+#if QT_CONFIG(cursor)
d->updateCursor(mapFromGlobal(QCursor::pos()));
+#endif
return delivered;
}
break;
@@ -1635,7 +1713,7 @@ bool QQuickWindow::event(QEvent *e)
d->clearHover();
d->lastMousePosition = QPointF();
break;
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
case QEvent::DragEnter:
case QEvent::DragLeave:
case QEvent::DragMove:
@@ -1717,6 +1795,13 @@ void QQuickWindowPrivate::deliverKeyEvent(QKeyEvent *e)
{
if (activeFocusItem) {
QQuickItem *item = activeFocusItem;
+
+ // In case of generated event, trigger ShortcutOverride event
+ if (e->type() == QEvent::KeyPress && e->spontaneous() == false)
+ qt_sendShortcutOverrideEvent(item, e->timestamp(),
+ e->key(), e->modifiers(), e->text(),
+ e->isAutoRepeat(), e->count());
+
e->accept();
QCoreApplication::sendEvent(item, e);
while (!e->isAccepted() && (item = item->parentItem())) {
@@ -2691,7 +2776,7 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
// If the touch was accepted (regardless by whom or in what form),
// update accepted new points.
bool isPressOrRelease = pointerEvent->isPressEvent() || pointerEvent->isReleaseEvent();
- for (auto point: qAsConst(touchEvent->touchPoints())) {
+ for (const auto &point: qAsConst(touchEvent->touchPoints())) {
if (auto pointerEventPoint = ptEvent->pointById(point.id())) {
pointerEventPoint->setAccepted();
if (isPressOrRelease)
@@ -2701,7 +2786,7 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
} else {
// But if the event was not accepted then we know this item
// will not be interested in further updates for those touchpoint IDs either.
- for (auto point: qAsConst(touchEvent->touchPoints())) {
+ for (const auto &point: qAsConst(touchEvent->touchPoints())) {
if (point.state() == Qt::TouchPointPressed) {
if (auto *tp = ptEvent->pointById(point.id())) {
if (tp->exclusiveGrabber() == item) {
@@ -2714,7 +2799,7 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
}
}
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
{
grabber->resetTarget();
@@ -2743,32 +2828,49 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e
for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
QCoreApplication::sendEvent(**grabItem, &leaveEvent);
return;
- } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
+ } else {
QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
- if (deliverDragEvent(grabber, **grabItem, moveEvent)) {
- for (++grabItem; grabItem != grabber->end();) {
- QPointF p = (**grabItem)->mapFromScene(moveEvent->pos());
- if ((**grabItem)->contains(p)) {
- QDragMoveEvent translatedEvent(
- p.toPoint(),
- moveEvent->possibleActions(),
- moveEvent->mimeData(),
- moveEvent->mouseButtons(),
- moveEvent->keyboardModifiers());
- QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
- QCoreApplication::sendEvent(**grabItem, &translatedEvent);
- ++grabItem;
- } else {
- QDragLeaveEvent leaveEvent;
- QCoreApplication::sendEvent(**grabItem, &leaveEvent);
- grabItem = grabber->release(grabItem);
- }
+
+ // Used to ensure we don't send DragEnterEvents to current drop targets,
+ // and to detect which current drop targets we have left
+ QVarLengthArray<QQuickItem*, 64> currentGrabItems;
+ for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
+ currentGrabItems.append(**grabItem);
+
+ // Look for any other potential drop targets that are higher than the current ones
+ QDragEnterEvent enterEvent(
+ moveEvent->pos(),
+ moveEvent->possibleActions(),
+ moveEvent->mimeData(),
+ moveEvent->mouseButtons(),
+ moveEvent->keyboardModifiers());
+ QQuickDropEventEx::copyActions(&enterEvent, *moveEvent);
+ event->setAccepted(deliverDragEvent(grabber, contentItem, &enterEvent, &currentGrabItems));
+
+ for (grabItem = grabber->begin(); grabItem != grabber->end(); ++grabItem) {
+ int i = currentGrabItems.indexOf(**grabItem);
+ if (i >= 0) {
+ currentGrabItems.remove(i);
+ // Still grabbed: send move event
+ QDragMoveEvent translatedEvent(
+ (**grabItem)->mapFromScene(moveEvent->pos()).toPoint(),
+ moveEvent->possibleActions(),
+ moveEvent->mimeData(),
+ moveEvent->mouseButtons(),
+ moveEvent->keyboardModifiers());
+ QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
+ QCoreApplication::sendEvent(**grabItem, &translatedEvent);
+ event->setAccepted(translatedEvent.isAccepted());
+ QQuickDropEventEx::copyActions(moveEvent, translatedEvent);
}
- return;
- } else {
- QDragLeaveEvent leaveEvent;
- QCoreApplication::sendEvent(**grabItem, &leaveEvent);
}
+
+ // Anything left in currentGrabItems is no longer a drop target and should be sent a DragLeaveEvent
+ QDragLeaveEvent leaveEvent;
+ for (QQuickItem *i : currentGrabItems)
+ QCoreApplication::sendEvent(i, &leaveEvent);
+
+ return;
}
}
if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) {
@@ -2784,9 +2886,8 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e
}
}
-bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event)
+bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event, QVarLengthArray<QQuickItem*, 64> *currentGrabItems)
{
- bool accepted = false;
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
if (!item->isVisible() || !item->isEnabled() || QQuickItemPrivate::get(item)->culled)
return false;
@@ -2805,12 +2906,24 @@ bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickIte
event->keyboardModifiers());
QQuickDropEventEx::copyActions(&enterEvent, *event);
QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
+
+ // Check children in front of this item first
for (int ii = children.count() - 1; ii >= 0; --ii) {
- if (deliverDragEvent(grabber, children.at(ii), &enterEvent))
+ if (children.at(ii)->z() < 0)
+ continue;
+ if (deliverDragEvent(grabber, children.at(ii), &enterEvent, currentGrabItems))
return true;
}
if (itemContained) {
+ // If this item is currently grabbed, don't send it another DragEnter,
+ // just grab it again if it's still contained.
+ if (currentGrabItems && currentGrabItems->contains(item)) {
+ grabber->grab(item);
+ grabber->setTarget(item);
+ return true;
+ }
+
if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
QDragMoveEvent translatedEvent(
p.toPoint(),
@@ -2827,17 +2940,26 @@ bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickIte
if (event->type() == QEvent::DragEnter) {
if (translatedEvent.isAccepted()) {
grabber->grab(item);
- accepted = true;
+ grabber->setTarget(item);
+ return true;
}
} else {
- accepted = true;
+ return true;
}
}
}
- return accepted;
+ // Check children behind this item if this item or any higher children have not accepted
+ for (int ii = children.count() - 1; ii >= 0; --ii) {
+ if (children.at(ii)->z() >= 0)
+ continue;
+ if (deliverDragEvent(grabber, children.at(ii), &enterEvent, currentGrabItems))
+ return true;
+ }
+
+ return false;
}
-#endif // draganddrop
+#endif // quick_draganddrop
#if QT_CONFIG(cursor)
void QQuickWindowPrivate::updateCursor(const QPointF &scenePos)
@@ -2942,7 +3064,7 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event
if (filteringParent->childMouseEventFilter(receiver, filteringParentTouchEvent.data())) {
qCDebug(DBG_TOUCH) << "touch event intercepted by childMouseEventFilter of " << filteringParent;
skipDelivery.append(filteringParent);
- for (auto point: qAsConst(filteringParentTouchEvent->touchPoints())) {
+ for (const auto &point: qAsConst(filteringParentTouchEvent->touchPoints())) {
QQuickEventPoint *pt = event->pointById(point.id());
pt->setAccepted();
pt->setGrabberItem(filteringParent);
@@ -3625,24 +3747,20 @@ void QQuickWindow::setTransientParent_helper(QQuickWindow *window)
/*!
Returns the OpenGL context used for rendering.
- If the scene graph is not ready, or the scene graph is not using OpenGL,
- this function will return null.
-
- \note If using a scene graph adaptation other than OpenGL this
- function will return nullptr.
+ \note If the scene graph is not ready, or the scene graph is not using
+ OpenGL (or RHI over OpenGL), this function will return null.
\sa sceneGraphInitialized(), sceneGraphInvalidated()
*/
-
QOpenGLContext *QQuickWindow::openglContext() const
{
#if QT_CONFIG(opengl)
Q_D(const QQuickWindow);
if (d->context && d->context->isValid()) {
QSGRendererInterface *rif = d->context->sceneGraphContext()->rendererInterface(d->context);
- if (rif && rif->graphicsApi() == QSGRendererInterface::OpenGL) {
- auto openglRenderContext = static_cast<const QSGDefaultRenderContext *>(d->context);
- return openglRenderContext->openglContext();
+ if (rif) {
+ return reinterpret_cast<QOpenGLContext *>(rif->getResource(const_cast<QQuickWindow *>(this),
+ QSGRendererInterface::OpenGLContextResource));
}
}
#endif
@@ -3781,6 +3899,8 @@ bool QQuickWindow::isSceneGraphInitialized() const
This function only has an effect when using the default OpenGL scene
graph adaptation.
+ \note This function has no effect when running on the RHI graphics abstraction.
+
\warning
This function can only be called from the thread doing
the rendering.
@@ -3789,6 +3909,9 @@ bool QQuickWindow::isSceneGraphInitialized() const
void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo)
{
Q_D(QQuickWindow);
+ if (d->rhi)
+ return;
+
if (d->context && QThread::currentThread() != d->context->thread()) {
qWarning("QQuickWindow::setRenderTarget: Cannot set render target from outside the rendering thread");
return;
@@ -3870,9 +3993,11 @@ QSize QQuickWindow::renderTargetSize() const
The default is to render to the surface of the window, in which
case the render target is 0.
- \note
- This function will return nullptr when not using the OpenGL scene
+ \note This function will return nullptr when not using the OpenGL scene
graph adaptation.
+
+ \note This function has no effect and returns nullptr when running on the
+ RHI graphics abstraction.
*/
QOpenGLFramebufferObject *QQuickWindow::renderTarget() const
{
@@ -3898,12 +4023,23 @@ QImage QQuickWindow::grabWindow()
Q_D(QQuickWindow);
if (!isVisible() && !d->renderControl) {
+ // backends like software and d3d12 can grab regardless of the window state
if (d->windowManager && (d->windowManager->flags() & QSGRenderLoop::SupportsGrabWithoutExpose))
return d->windowManager->grab(this);
}
-#if QT_CONFIG(opengl)
if (!isVisible() && !d->renderControl) {
+ if (d->rhi) {
+ // ### we may need a full offscreen round when non-exposed...
+
+ if (d->renderControl)
+ return d->renderControl->grab();
+ else if (d->windowManager)
+ return d->windowManager->grab(this);
+ return QImage();
+ }
+
+#if QT_CONFIG(opengl)
auto openglRenderContext = static_cast<QSGDefaultRenderContext *>(d->context);
if (!openglRenderContext->openglContext()) {
if (!handle() || !size().isValid()) {
@@ -3916,7 +4052,9 @@ QImage QQuickWindow::grabWindow()
context.setShareContext(qt_gl_global_share_context());
context.create();
context.makeCurrent(this);
- d->context->initialize(&context);
+ QSGDefaultRenderContext::InitParams rcParams;
+ rcParams.openGLContext = &context;
+ d->context->initialize(&rcParams);
d->polishItems();
d->syncSceneGraph();
@@ -3931,8 +4069,9 @@ QImage QQuickWindow::grabWindow()
return image;
}
- }
#endif
+ }
+
if (d->renderControl)
return d->renderControl->grab();
else if (d->windowManager)
@@ -4070,6 +4209,19 @@ QQmlIncubationController *QQuickWindow::incubationController() const
The OpenGL context used for rendering the scene graph will be bound
at this point.
+ When using the RHI, the signal is emitted after the preparations for the
+ frame have been done, meaning there is a command buffer in recording mode,
+ where applicable. If desired, the slot function connected to this signal
+ can query native resources like the command before via
+ QSGRendererInterface. Note however that the recording of the main render
+ pass is not yet started at this point and it is not possible to add
+ commands within that pass. Starting a pass means clearing the color, depth,
+ and stencil buffers so it is not possible to achieve an underlay type of
+ rendering by just connecting to this signal. Rather, connect to
+ beforeRenderPassRecording(). However, connecting to this signal is still
+ important if the recording of copy type of commands is desired since those
+ cannot be enqueued within a render pass.
+
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
the connection is direct (see Qt::ConnectionType).
@@ -4091,6 +4243,18 @@ QQmlIncubationController *QQuickWindow::incubationController() const
The OpenGL context used for rendering the scene graph will be bound at this point.
+ When using the RHI, the signal is emitted after scene graph has added its
+ commands to the command buffer, which is not yet submitted to the graphics
+ queue. If desired, the slot function connected to this signal can query
+ native resources, like the command buffer, before via QSGRendererInterface.
+ Note however that the render pass (or passes) are already recorded at this
+ point and it is not possible to add more commands within the scenegraph's
+ pass. Instead, use afterRenderPassRecording() for that. This signal has
+ therefore limited use and is rarely needed in an RHI-based setup. Rather,
+ it is the combination of beforeRendering() + beforeRenderPassRecording() or
+ beforeRendering() + afterRenderPassRecording() that is typically used to
+ achieve under- or overlaying of the custom rendering.
+
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
the connection is direct (see Qt::ConnectionType).
@@ -4103,14 +4267,78 @@ QQmlIncubationController *QQuickWindow::incubationController() const
*/
/*!
+ \fn void QQuickWindow::beforeRenderPassRecording()
+
+ This signal is emitted before the scenegraph starts recording commands for
+ the main render pass. (Layers have their own passes and are fully recorded
+ by the time this signal is emitted.) The render pass is already active on
+ the command buffer when the signal is emitted.
+
+ This signal is applicable when using the RHI graphics abstraction with the
+ scenegraph. It is emitted later than beforeRendering() and it guarantees
+ that not just the frame, but also the recording of the scenegraph's main
+ render pass is active. This allows inserting commands without having to
+ generate an entire, separate render pass (which would typically clear the
+ attached images). The native graphics objects can be queried via
+ QSGRendererInterface.
+
+ When not running with the RHI (and using OpenGL directly), the signal is
+ emitted after the renderer has cleared the render target. This makes it
+ possible to create applications that function identically both with and
+ without the RHI.
+
+ \note Resource updates (uploads, copies) typically cannot be enqueued from
+ within a render pass. Therefore, more complex user rendering will need to
+ connect to both beforeRendering() and this signal.
+
+ \warning This signal is emitted from the scene graph rendering thread. If your
+ slot function needs to finish before execution continues, you must make sure that
+ the connection is direct (see Qt::ConnectionType).
+
+ \since 5.14
+*/
+
+/*!
+ \fn void QQuickWindow::afterRenderPassRecording()
+
+ This signal is emitted after the scenegraph has recorded the commands for
+ its main render pass, but the pass is not yet finalized on the command
+ buffer.
+
+ This signal is applicable when using the RHI graphics abstraction with the
+ scenegraph. It is emitted earlier than afterRendering() and it guarantees
+ that not just the frame, but also the recording of the scenegraph's main
+ render pass is still active. This allows inserting commands without having
+ to generate an entire, separate render pass (which would typically clear
+ the attached images). The native graphics objects can be queried via
+ QSGRendererInterface.
+
+ When not running with the RHI (and using OpenGL directly), the signal is
+ emitted after the renderer has finished its rendering, but before
+ afterRendering(). This makes it possible to create applications that
+ function identically both with and without the RHI.
+
+ \note Resource updates (uploads, copies) typically cannot be enqueued from
+ within a render pass. Therefore, more complex user rendering will need to
+ connect to both beforeRendering() and this signal.
+
+ \warning This signal is emitted from the scene graph rendering thread. If your
+ slot function needs to finish before execution continues, you must make sure that
+ the connection is direct (see Qt::ConnectionType).
+
+ \since 5.14
+*/
+
+/*!
\fn void QQuickWindow::afterAnimating()
This signal is emitted on the gui thread before requesting the render thread to
perform the synchronization of the scene graph.
- Unlike the other similar signals, this one is emitted on the gui thread instead
- of the render thread. It can be used to synchronize external animation systems
- with the QML content.
+ Unlike the other similar signals, this one is emitted on the gui thread
+ instead of the render thread. It can be used to synchronize external
+ animation systems with the QML content. At the same time this means that
+ this signal is not suitable for triggering graphics operations.
\since 5.3
*/
@@ -4170,7 +4398,15 @@ QQmlIncubationController *QQuickWindow::incubationController() const
The color buffer is cleared by default.
- \sa beforeRendering()
+ \warning This flag is ignored completely when running with the RHI graphics
+ abstraction instead of using OpenGL directly. As explicit clear commands
+ simply do not exist in some modern APIs, the scene graph cannot offer this
+ flexibility anymore. The images associated with a render target will always
+ get cleared when a render pass starts. As a solution, an alternative to
+ disabling scene graph issued clears is provided in form of the
+ beforeRenderPassRecording() signal.
+
+ \sa beforeRendering(), beforeRenderPassRecording()
*/
void QQuickWindow::setClearBeforeRendering(bool enabled)
@@ -4274,24 +4510,129 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateText
\note This function only has an effect when using the default OpenGL scene graph
adaptation.
+ \note This function has no effect when running on the RHI graphics
+ abstraction. Use createTextureFromNativeObject() instead.
+
\sa sceneGraphInitialized(), QSGTexture
*/
QSGTexture *QQuickWindow::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const
{
#if QT_CONFIG(opengl)
- if (openglContext()) {
- QSGPlainTexture *texture = new QSGPlainTexture();
- texture->setTextureId(id);
+ Q_D(const QQuickWindow);
+ if (!d->rhi) {
+ if (openglContext()) {
+ QSGPlainTexture *texture = new QSGPlainTexture();
+ texture->setTextureId(id);
+ texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
+ texture->setOwnsTexture(options & TextureOwnsGLTexture);
+ texture->setTextureSize(size);
+ return texture;
+ }
+ } else {
+ qWarning("createTextureFromId() must not be called when running on the RHI. "
+ "Use createTextureFromNativeObject() instead.");
+ }
+#else
+ Q_UNUSED(id)
+ Q_UNUSED(size)
+ Q_UNUSED(options)
+#endif
+ return nullptr;
+}
+
+/*!
+ \enum QQuickWindow::NativeObjectType
+ \since 5.14
+
+ Specifies the type of the native object passed to functions such as
+ createTextureFromNativeObject().
+
+ \value NativeObjectTexture The native object is a 2D texture (OpenGL,
+ Direct3D 11, Metal) or image (Vulkan).
+ */
+
+/*!
+ Creates a new QSGTexture object from an existing native object.
+
+ The native object is wrapped, but not owned, by the resulting QSGTexture.
+ The caller of the function is responsible for deleting the returned
+ QSGTexture, but that will not destroy the underlying native object.
+
+ \a type specifies the type of the object. In practice the type is
+ NativeObjectTexture, indicating that the native object is a texture or
+ image of the underlying graphics API. Other types may be introduced in the
+ future.
+
+ This function is currently suitable for 2D RGBA textures only.
+
+ Unlike createTextureFromId(), this function supports both direct OpenGL
+ usage and the RHI abstracted rendering path.
+
+ \warning This function will return null if the scenegraph has not yet been
+ initialized.
+
+ Use \a options to customize the texture attributes. Only the
+ TextureHasAlphaChannel and TextureHasMipmaps are taken into account here.
+
+ \warning Unlike createTextureFromId(), this function never takes ownership
+ of the native object, and the TextureOwnsGLTexture flag is ignored.
+
+ \a size specifies the size in pixels.
+
+ \a nativeObjectPtr is a pointer to the native object handle. With OpenGL,
+ the native handle is a GLuint value, so \a nativeObjectPtr is then a
+ pointer to a GLuint. With Vulkan, the native handle is a VkImage, so \a
+ nativeObjectPtr is a pointer to a VkImage. With Direct3D 11 and Metal \a
+ nativeObjectPtr is a pointer to a ID3D11Texture2D or MTLTexture pointer.
+
+ \note Pay attention to the fact that \a nativeObjectPtr is always a pointer
+ to the native texture handle type, even if the native type itself is a
+ pointer.
+
+ \a nativeLayout is only used for APIs like Vulkan. When applicable, it must
+ specify the current image layout, such as, a VkImageLayout value.
+
+ \sa sceneGraphInitialized(), QSGTextures
+
+ \since 5.14
+ */
+QSGTexture *QQuickWindow::createTextureFromNativeObject(NativeObjectType type,
+ const void *nativeObjectPtr,
+ int nativeLayout,
+ const QSize &size,
+ CreateTextureOptions options) const
+{
+ if (type != NativeObjectTexture) {
+ qWarning("createTextureFromNativeObject: only textures are supported");
+ return nullptr;
+ }
+
+#if QT_CONFIG(opengl) /* || QT_CONFIG(vulkan) || defined(Q_OS_WIN) || defined(Q_OS_DARWIN) */
+ Q_D(const QQuickWindow);
+ if (d->rhi) {
+ QSGPlainTexture *texture = new QSGPlainTexture;
+ texture->setTextureFromNativeObject(d->rhi, type, nativeObjectPtr, nativeLayout,
+ size, options.testFlag(TextureHasMipmaps));
+ texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
+ // note that the QRhiTexture does not (and cannot) own the native object
+ texture->setOwnsTexture(true); // texture meaning the QRhiTexture here, not the native object
+ texture->setTextureSize(size);
+ return texture;
+ } else if (openglContext()) {
+ QSGPlainTexture *texture = new QSGPlainTexture;
+ texture->setTextureId(*reinterpret_cast<const uint *>(nativeObjectPtr));
texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
texture->setOwnsTexture(options & TextureOwnsGLTexture);
texture->setTextureSize(size);
return texture;
}
#else
- Q_UNUSED(id)
- Q_UNUSED(size)
- Q_UNUSED(options)
+ Q_UNUSED(nativeObjectPtr);
+ Q_UNUSED(nativeLayout);
+ Q_UNUSED(size);
+ Q_UNUSED(options);
#endif
+
return nullptr;
}
@@ -4382,15 +4723,19 @@ void QQuickWindow::setDefaultAlphaBuffer(bool useAlpha)
\note This function only has an effect when using the default OpenGL scene graph
adaptation.
- \sa QQuickWindow::beforeRendering()
+ \note This function has no effect when running on the RHI graphics
+ abstraction. With the RHI, the functions to call when enqueuing native
+ graphics commands are beginExternalCommands() and endExternalCommands().
+
+ \sa QQuickWindow::beforeRendering(), beginExternalCommands(), endExternalCommands()
*/
void QQuickWindow::resetOpenGLState()
{
- if (!openglContext())
- return;
-
Q_D(QQuickWindow);
+ if (d->rhi || !openglContext())
+ return;
+
QOpenGLContext *ctx = openglContext();
QOpenGLFunctions *gl = ctx->functions();
@@ -4437,6 +4782,132 @@ void QQuickWindow::resetOpenGLState()
QOpenGLFramebufferObject::bindDefault();
}
#endif
+
+/*!
+ \struct QQuickWindow::GraphicsStateInfo
+ \inmodule QtQuick
+ \since 5.14
+
+ \brief Describes some of the RHI's graphics state at the point of a
+ \l{QQuickWindow::beginExternalCommands()}{beginExternalCommands()} call.
+ */
+
+/*!
+ \return a reference to a GraphicsStateInfo struct describing some of the
+ RHI's internal state, in particular, the double or tripple buffering status
+ of the backend (such as, the Vulkan or Metal integrations). This is
+ relevant when the underlying graphics APIs is Vulkan or Metal, and the
+ external rendering code wishes to perform double or tripple buffering of
+ its own often-changing resources, such as, uniform buffers, in order to
+ avoid stalling the pipeline.
+ */
+const QQuickWindow::GraphicsStateInfo &QQuickWindow::graphicsStateInfo()
+{
+ Q_D(QQuickWindow);
+ if (d->rhi) {
+ d->rhiStateInfo.currentFrameSlot = d->rhi->currentFrameSlot();
+ d->rhiStateInfo.framesInFlight = d->rhi->resourceLimit(QRhi::FramesInFlight);
+ }
+ return d->rhiStateInfo;
+}
+
+/*!
+ When mixing raw graphics (OpenGL, Vulkan, Metal, etc.) commands with scene
+ graph rendering, it is necessary to call this function before recording
+ commands to the command buffer used by the scene graph to render its main
+ render pass. This is to avoid clobbering state.
+
+ In practice this function is often called from a slot connected to the
+ beforeRenderPassRecording() or afterRenderPassRecording() signals.
+
+ The function does not need to be called when recording commands to the
+ application's own command buffer (such as, a VkCommandBuffer or
+ MTLCommandBuffer + MTLRenderCommandEncoder created and managed by the
+ application, not retrieved from the scene graph). With graphics APIs where
+ no native command buffer concept is exposed (OpenGL, Direct 3D 11),
+ beginExternalCommands() and endExternalCommands() together provide a
+ replacement for resetOpenGLState().
+
+ Calling this function and endExternalCommands() is not necessary within the
+ \l{QSGRenderNode::render()}{render()} implementation of a QSGRenderNode
+ because the scene graph performs the necessary steps implicitly for render
+ nodes.
+
+ Native graphics objects (such as, graphics device, command buffer or
+ encoder) are accessible via QSGRendererInterface::getResource().
+
+ \warning Watch out for the fact that
+ QSGRendererInterface::CommandListResource may return a different object
+ between beginExternalCommands() - endExternalCommands(). This can happen
+ when the underlying implementation provides a dedicated secondary command
+ buffer for recording external graphics commands within a render pass.
+ Therefore, always query CommandListResource after calling this function. Do
+ not attempt to reuse an object from an earlier query.
+
+ \note This function has no effect when the scene graph is using OpenGL
+ directly and the RHI graphics abstraction layer is not in use. Refer to
+ resetOpenGLState() in that case.
+
+ \sa endExternalCommands()
+
+ \since 5.14
+ */
+void QQuickWindow::beginExternalCommands()
+{
+#if QT_CONFIG(opengl) /* || QT_CONFIG(vulkan) || defined(Q_OS_WIN) || defined(Q_OS_DARWIN) */
+ Q_D(QQuickWindow);
+ if (d->rhi && d->context && d->context->isValid()) {
+ QSGDefaultRenderContext *rc = static_cast<QSGDefaultRenderContext *>(d->context);
+ QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer();
+ if (cb)
+ cb->beginExternal();
+ }
+#endif
+}
+
+/*!
+ When mixing raw graphics (OpenGL, Vulkan, Metal, etc.) commands with scene
+ graph rendering, it is necessary to call this function after recording
+ commands to the command buffer used by the scene graph to render its main
+ render pass. This is to avoid clobbering state.
+
+ In practice this function is often called from a slot connected to the
+ beforeRenderPassRecording() or afterRenderPassRecording() signals.
+
+ The function does not need to be called when recording commands to the
+ application's own command buffer (such as, a VkCommandBuffer or
+ MTLCommandBuffer + MTLRenderCommandEncoder created and managed by the
+ application, not retrieved from the scene graph). With graphics APIs where
+ no native command buffer concept is exposed (OpenGL, Direct 3D 11),
+ beginExternalCommands() and endExternalCommands() together provide a
+ replacement for resetOpenGLState().
+
+ Calling this function and beginExternalCommands() is not necessary within the
+ \l{QSGRenderNode::render()}{render()} implementation of a QSGRenderNode
+ because the scene graph performs the necessary steps implicitly for render
+ nodes.
+
+ \note This function has no effect when the scene graph is using OpenGL
+ directly and the RHI graphics abstraction layer is not in use. Refer to
+ resetOpenGLState() in that case.
+
+ \sa beginExternalCommands()
+
+ \since 5.14
+ */
+void QQuickWindow::endExternalCommands()
+{
+#if QT_CONFIG(opengl) /* || QT_CONFIG(vulkan) || defined(Q_OS_WIN) || defined(Q_OS_DARWIN) */
+ Q_D(QQuickWindow);
+ if (d->rhi && d->context && d->context->isValid()) {
+ QSGDefaultRenderContext *rc = static_cast<QSGDefaultRenderContext *>(d->context);
+ QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer();
+ if (cb)
+ cb->endExternal();
+ }
+#endif
+}
+
/*!
\qmlproperty string Window::title
@@ -5022,6 +5493,10 @@ void QQuickWindow::setSceneGraphBackend(QSGRendererInterface::GraphicsApi api)
default:
break;
}
+#if QT_CONFIG(opengl) /* || QT_CONFIG(vulkan) || defined(Q_OS_WIN) || defined(Q_OS_DARWIN) */
+ if (QSGRendererInterface::isApiRhiBased(api))
+ QSGRhiSupport::configure(api);
+#endif
}
/*!
diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h
index 53fe0a4c4b..56d50cec2a 100644
--- a/src/quick/items/qquickwindow.h
+++ b/src/quick/items/qquickwindow.h
@@ -66,6 +66,7 @@ class QQuickRenderControl;
class QSGRectangleNode;
class QSGImageNode;
class QSGNinePatchNode;
+class QRhi;
class Q_QUICK_EXPORT QQuickWindow : public QWindow
{
@@ -108,6 +109,11 @@ public:
};
Q_ENUM(TextRenderType)
+ enum NativeObjectType {
+ NativeObjectTexture
+ };
+ Q_ENUM(NativeObjectType)
+
explicit QQuickWindow(QWindow *parent = nullptr);
explicit QQuickWindow(QQuickRenderControl *renderControl);
@@ -135,6 +141,13 @@ public:
#if QT_CONFIG(opengl)
void resetOpenGLState();
#endif
+ struct GraphicsStateInfo {
+ int currentFrameSlot;
+ int framesInFlight;
+ };
+ const GraphicsStateInfo &graphicsStateInfo();
+ void beginExternalCommands();
+ void endExternalCommands();
QQmlIncubationController *incubationController() const;
#if QT_CONFIG(accessibility)
@@ -145,6 +158,11 @@ public:
QSGTexture *createTextureFromImage(const QImage &image) const;
QSGTexture *createTextureFromImage(const QImage &image, CreateTextureOptions options) const;
QSGTexture *createTextureFromId(uint id, const QSize &size, CreateTextureOptions options = CreateTextureOption()) const;
+ QSGTexture *createTextureFromNativeObject(NativeObjectType type,
+ const void *nativeObjectPtr,
+ int nativeLayout,
+ const QSize &size,
+ CreateTextureOptions options = CreateTextureOption()) const;
void setClearBeforeRendering(bool enabled);
bool clearBeforeRendering() const;
@@ -198,6 +216,8 @@ Q_SIGNALS:
Q_REVISION(1) void activeFocusItemChanged();
Q_REVISION(2) void sceneGraphError(QQuickWindow::SceneGraphError error, const QString &message);
+ Q_REVISION(14) void beforeRenderPassRecording();
+ Q_REVISION(14) void afterRenderPassRecording();
public Q_SLOTS:
void update();
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index 63760a3b68..165859b5f3 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -82,6 +82,10 @@ class QQuickWindowPrivate;
class QQuickWindowRenderLoop;
class QSGRenderLoop;
class QTouchEvent;
+class QRhi;
+class QRhiSwapChain;
+class QRhiRenderBuffer;
+class QRhiRenderPassDescriptor;
//Make it easy to identify and customize the root item if needed
class QQuickRootItem : public QQuickItem
@@ -130,7 +134,7 @@ public:
#if QT_CONFIG(cursor)
QQuickItem *cursorItem;
#endif
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
QQuickDragGrabber *dragGrabber;
#endif
int touchMouseId;
@@ -186,9 +190,9 @@ public:
Qt::KeyboardModifiers modifiers, ulong timestamp, bool accepted);
bool clearHover(ulong timestamp = 0);
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
void deliverDragEvent(QQuickDragGrabber *, QEvent *);
- bool deliverDragEvent(QQuickDragGrabber *, QQuickItem *, QDragMoveEvent *);
+ bool deliverDragEvent(QQuickDragGrabber *, QQuickItem *, QDragMoveEvent *, QVarLengthArray<QQuickItem*, 64> *currentGrabItems = nullptr);
#endif
#if QT_CONFIG(cursor)
void updateCursor(const QPointF &scenePos);
@@ -215,7 +219,7 @@ public:
void polishItems();
void forcePolish();
void syncSceneGraph();
- void renderSceneGraph(const QSize &size);
+ void renderSceneGraph(const QSize &size, const QSize &surfaceSize = QSize());
bool isRenderable() const;
@@ -250,7 +254,7 @@ public:
QSGRenderLoop *windowManager;
QQuickRenderControl *renderControl;
- QQuickAnimatorController *animationController;
+ QScopedPointer<QQuickAnimatorController> animationController;
QScopedPointer<QTouchEvent> delayedTouch;
int pointerEventRecursionGuard;
@@ -317,6 +321,9 @@ public:
QString *untranslatedMessage,
bool isEs);
+ static void emitBeforeRenderPassRecording(void *ud);
+ static void emitAfterRenderPassRecording(void *ud);
+
QMutex renderJobMutex;
QList<QRunnable *> beforeSynchronizingJobs;
QList<QRunnable *> afterSynchronizingJobs;
@@ -326,6 +333,15 @@ public:
void runAndClearJobs(QList<QRunnable *> *jobs);
+ QQuickWindow::GraphicsStateInfo rhiStateInfo;
+ QRhi *rhi = nullptr;
+ QRhiSwapChain *swapchain = nullptr;
+ QRhiRenderBuffer *depthStencilForSwapchain = nullptr;
+ QRhiRenderPassDescriptor *rpDescForSwapchain = nullptr;
+ uint hasActiveSwapchain : 1;
+ uint hasRenderableSwapchain : 1;
+ uint swapchainJustBecameRenderable : 1;
+
private:
static void cleanupNodesOnShutdown(QQuickItem *);
};
diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp
index 2b109c0897..4b2b8f498d 100644
--- a/src/quick/items/qquickwindowmodule.cpp
+++ b/src/quick/items/qquickwindowmodule.cpp
@@ -205,15 +205,16 @@ void QQuickWindowModule::defineModule()
qmlRegisterRevision<QQuickWindow,1>(uri, 2, 1);//Type moved to a subclass, but also has new members
qmlRegisterRevision<QQuickWindow,2>(uri, 2, 2);
qmlRegisterType<QQuickWindowQmlImpl>(uri, 2, 1, "Window");
- qmlRegisterType<QQuickWindowQmlImpl,1>(uri, 2, 2, "Window");
- qmlRegisterType<QQuickWindowQmlImpl,2>(uri, 2, 3, "Window");
+ qmlRegisterType<QQuickWindowQmlImpl,2>(uri, 2, 2, "Window");
+ qmlRegisterType<QQuickWindowQmlImpl,3>(uri, 2, 3, "Window");
qmlRegisterUncreatableType<QQuickScreen>(uri, 2, 0, "Screen", QStringLiteral("Screen can only be used via the attached property."));
- qmlRegisterUncreatableType<QQuickScreen,1>(uri, 2, 3, "Screen", QStringLiteral("Screen can only be used via the attached property."));
- qmlRegisterUncreatableType<QQuickScreenInfo,2>(uri, 2, 3, "ScreenInfo", QStringLiteral("ScreenInfo can only be used via the attached property."));
+ qmlRegisterUncreatableType<QQuickScreen,3>(uri, 2, 3, "Screen", QStringLiteral("Screen can only be used via the attached property."));
+ qmlRegisterUncreatableType<QQuickScreenInfo,3>(uri, 2, 3, "ScreenInfo", QStringLiteral("ScreenInfo can only be used via the attached property."));
qmlRegisterUncreatableType<QQuickScreenInfo,10>(uri, 2, 10, "ScreenInfo", QStringLiteral("ScreenInfo can only be used via the attached property."));
qmlRegisterRevision<QWindow,13>(uri, 2, 13);
qmlRegisterRevision<QQuickWindow,13>(uri, 2, 13);
qmlRegisterType<QQuickWindowQmlImpl,13>(uri, 2, 13, "Window");
+ qmlRegisterRevision<QQuickWindow,14>(uri, 2, 14);
}
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickwindowmodule_p.h b/src/quick/items/qquickwindowmodule_p.h
index e7033e9b8d..1dcf1a1021 100644
--- a/src/quick/items/qquickwindowmodule_p.h
+++ b/src/quick/items/qquickwindowmodule_p.h
@@ -67,7 +67,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickWindowQmlImpl : public QQuickWindow, public Q
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
Q_PROPERTY(Visibility visibility READ visibility WRITE setVisibility NOTIFY visibilityChanged)
- Q_PROPERTY(QObject *screen READ screen WRITE setScreen NOTIFY screenChanged REVISION 2)
+ Q_PROPERTY(QObject *screen READ screen WRITE setScreen NOTIFY screenChanged REVISION 3)
public:
QQuickWindowQmlImpl(QWindow *parent = nullptr);
@@ -83,7 +83,7 @@ public:
Q_SIGNALS:
void visibleChanged(bool arg);
void visibilityChanged(QWindow::Visibility visibility);
- Q_REVISION(2) void screenChanged();
+ Q_REVISION(3) void screenChanged();
protected:
void classBegin() override;
diff --git a/src/quick/qtquick2.cpp b/src/quick/qtquick2.cpp
index 63f3b91b82..7a04c2146c 100644
--- a/src/quick/qtquick2.cpp
+++ b/src/quick/qtquick2.cpp
@@ -183,7 +183,6 @@ void QQmlQtQuick2Module::defineModule()
QQuick_initializeProviders();
QQuickUtilModule::defineModule();
- QQmlEnginePrivate::defineQtQuick2Module();
QQuickItemsModule::defineModule();
qmlRegisterUncreatableType<QQuickApplication>("QtQuick",2,0,"Application", QQuickApplication::tr("Application is an abstract class"));
diff --git a/src/quick/quick.pro b/src/quick/quick.pro
index 37d2ad1172..700f794af4 100644
--- a/src/quick/quick.pro
+++ b/src/quick/quick.pro
@@ -1,6 +1,6 @@
TARGET = QtQuick
-QT = core-private gui-private qml-private
+QT = core-private gui-private qml-private qmlmodels-private
qtConfig(qml-network): \
QT_PRIVATE += network
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
index af3b901af4..ca620965a8 100644
--- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
@@ -77,11 +77,6 @@ QSGSoftwareRenderableNode *QSGAbstractSoftwareRenderer::renderableNode(QSGNode *
return m_nodes.value(node, nullptr);
}
-const QLinkedList<QSGSoftwareRenderableNode*> &QSGAbstractSoftwareRenderer::renderableNodes() const
-{
- return m_renderableNodes;
-}
-
void QSGAbstractSoftwareRenderer::addNodeMapping(QSGNode *node, QSGSoftwareRenderableNode *renderableNode)
{
m_nodes.insert(node, renderableNode);
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
index 875569454f..e1b477ab97 100644
--- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
@@ -54,7 +54,6 @@
#include <private/qsgrenderer_p.h>
#include <QtCore/QHash>
-#include <QtCore/QLinkedList>
QT_BEGIN_NAMESPACE
@@ -88,7 +87,6 @@ protected:
QRect backgroundRect();
// only known after calling optimizeRenderList()
bool isOpaque() const { return m_isOpaque; }
- const QLinkedList<QSGSoftwareRenderableNode*> &renderableNodes() const;
private:
void nodeAdded(QSGNode *node);
@@ -99,7 +97,7 @@ private:
void nodeOpacityUpdated(QSGNode *node);
QHash<QSGNode*, QSGSoftwareRenderableNode*> m_nodes;
- QLinkedList<QSGSoftwareRenderableNode*> m_renderableNodes;
+ QVector<QSGSoftwareRenderableNode*> m_renderableNodes;
QSGSimpleRectNode *m_background;
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
index 5b5bf005d8..7b5ee66df6 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
@@ -99,8 +99,9 @@ QSGInternalRectangleNode *QSGSoftwareContext::createInternalRectangleNode()
return new QSGSoftwareInternalRectangleNode();
}
-QSGInternalImageNode *QSGSoftwareContext::createInternalImageNode()
+QSGInternalImageNode *QSGSoftwareContext::createInternalImageNode(QSGRenderContext *renderContext)
{
+ Q_UNUSED(renderContext);
return new QSGSoftwareInternalImageNode();
}
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h
index 1f14717416..84f468ce33 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h
@@ -89,7 +89,7 @@ public:
QSGRenderContext *createRenderContext() override { return new QSGSoftwareRenderContext(this); }
QSGInternalRectangleNode *createInternalRectangleNode() override;
- QSGInternalImageNode *createInternalImageNode() override;
+ QSGInternalImageNode *createInternalImageNode(QSGRenderContext *renderContext) override;
QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override;
QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) override;
QSGLayer *createLayer(QSGRenderContext *renderContext) override;
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp
index 70378d2950..683ccb60c2 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp
@@ -45,7 +45,8 @@
QT_BEGIN_NAMESPACE
QSGSoftwareLayer::QSGSoftwareLayer(QSGRenderContext *renderContext)
- : m_item(nullptr)
+ : QSGLayer(*(new QSGSoftwareLayerPrivate))
+ , m_item(nullptr)
, m_context(renderContext)
, m_renderer(nullptr)
, m_device_pixel_ratio(1)
@@ -69,6 +70,11 @@ int QSGSoftwareLayer::textureId() const
return 0;
}
+int QSGSoftwareLayerPrivate::comparisonKey() const
+{
+ return 0;
+}
+
QSize QSGSoftwareLayer::textureSize() const
{
return m_pixmap.size();
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h
index 9f5a22e66f..1859e14514 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h
@@ -53,13 +53,16 @@
#include <private/qsgadaptationlayer_p.h>
#include <private/qsgcontext_p.h>
+#include <private/qsgtexture_p.h>
QT_BEGIN_NAMESPACE
class QSGSoftwarePixmapRenderer;
+class QSGSoftwareLayerPrivate;
class QSGSoftwareLayer : public QSGLayer
{
+ Q_DECLARE_PRIVATE(QSGSoftwareLayer)
Q_OBJECT
public:
QSGSoftwareLayer(QSGRenderContext *renderContext);
@@ -117,6 +120,13 @@ private:
bool m_dirtyTexture;
};
+class QSGSoftwareLayerPrivate : public QSGTexturePrivate
+{
+ Q_DECLARE_PUBLIC(QSGSoftwareLayer)
+public:
+ int comparisonKey() const override;
+};
+
QT_END_NAMESPACE
#endif // QSGSOFTWARELAYER_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp
index 16e3a111ae..82a48d80ca 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp
@@ -38,10 +38,12 @@
****************************************************************************/
#include "qsgsoftwarepixmaptexture_p.h"
+#include <private/qsgcontext_p.h>
QT_BEGIN_NAMESPACE
QSGSoftwarePixmapTexture::QSGSoftwarePixmapTexture(const QImage &image, uint flags)
+ : QSGTexture(*(new QSGSoftwarePixmapTexturePrivate))
{
// Prevent pixmap format conversion to reduce memory consumption
// and surprises in calling code. (See QTBUG-47328)
@@ -55,11 +57,11 @@ QSGSoftwarePixmapTexture::QSGSoftwarePixmapTexture(const QImage &image, uint fla
}
QSGSoftwarePixmapTexture::QSGSoftwarePixmapTexture(const QPixmap &pixmap)
- : m_pixmap(pixmap)
+ : QSGTexture(*(new QSGSoftwarePixmapTexturePrivate)),
+ m_pixmap(pixmap)
{
}
-
int QSGSoftwarePixmapTexture::textureId() const
{
return 0;
@@ -85,6 +87,11 @@ void QSGSoftwarePixmapTexture::bind()
Q_UNREACHABLE();
}
+int QSGSoftwarePixmapTexturePrivate::comparisonKey() const
+{
+ return 0;
+}
+
QT_END_NAMESPACE
#include "moc_qsgsoftwarepixmaptexture_p.cpp"
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h
index 034fa25da9..baa62d93de 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h
@@ -52,12 +52,16 @@
//
#include <private/qsgtexture_p.h>
+#include <QtGui/QPixmap>
QT_BEGIN_NAMESPACE
+class QSGSoftwarePixmapTexturePrivate;
+
class QSGSoftwarePixmapTexture : public QSGTexture
{
Q_OBJECT
+ Q_DECLARE_PRIVATE(QSGSoftwarePixmapTexture)
public:
QSGSoftwarePixmapTexture(const QImage &image, uint flags);
QSGSoftwarePixmapTexture(const QPixmap &pixmap);
@@ -74,6 +78,13 @@ private:
QPixmap m_pixmap;
};
+class QSGSoftwarePixmapTexturePrivate : public QSGTexturePrivate
+{
+ Q_DECLARE_PUBLIC(QSGSoftwarePixmapTexture)
+public:
+ int comparisonKey() const override;
+};
+
QT_END_NAMESPACE
#endif // QSGSOFTWAREPIXMAPTEXTURE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
index 20286a03d5..141d8f3c6d 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
@@ -41,6 +41,7 @@
#include "qsgsoftwarelayer_p.h"
#include "qsgsoftwarepixmaptexture_p.h"
#include "qsgsoftwareinternalimagenode_p.h"
+#include <private/qsgplaintexture_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
index 7fb531cca3..95c7efd4cb 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
@@ -52,7 +52,7 @@
#include <qsgsimplerectnode.h>
#include <qsgsimpletexturenode.h>
#include <private/qsgrendernode_p.h>
-#include <private/qsgtexture_p.h>
+#include <private/qsgplaintexture_p.h>
#include <qmath.h>
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp
index f5a41410ee..c97dcb9326 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp
@@ -100,7 +100,7 @@ void QSGSoftwareRenderLoop::windowDestroyed(QQuickWindow *window)
rc->invalidate();
}
- delete d->animationController;
+ d->animationController.reset();
}
void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window, bool isNewExpose)
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp
index f8973af2fb..c6b463bb02 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp
@@ -69,10 +69,6 @@ const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 1);
// Passed from the RL to RT when GUI has been locked, waiting for sync.
const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2);
-// Passed by the RT to itself to trigger another render pass. This is typically
-// a result of QQuickWindow::update().
-const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 3);
-
// Passed by the RL to the RT to maybe release resource if no windows are
// rendering.
const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4);
@@ -296,7 +292,7 @@ bool QSGSoftwareRenderThread::event(QEvent *e)
QCoreApplication::processEvents();
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
if (wme->destroying)
- delete wd->animationController;
+ wd->animationController.reset();
}
if (wme->destroying)
active = false;
@@ -353,13 +349,6 @@ bool QSGSoftwareRenderThread::event(QEvent *e)
return true;
}
- case WM_RequestRepaint:
- qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestPaint");
- // When GUI posts this event, it is followed by a polishAndSync, so we
- // must not exit the event loop yet.
- pendingUpdate |= RepaintRequest;
- break;
-
default:
break;
}
@@ -855,7 +844,8 @@ void QSGSoftwareThreadedRenderLoop::handleExposure(QQuickWindow *window)
if (!w->thread->isRunning()) {
qCDebug(QSG_RASTER_LOG_RENDERLOOP, "starting render thread");
// Push a few things to the render thread.
- QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController;
+ QQuickAnimatorController *controller
+ = QQuickWindowPrivate::get(w->window)->animationController.get();
if (controller->thread() != w->thread)
controller->moveToThread(w->thread);
if (w->thread->thread() == QThread::currentThread()) {
diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp
index 796bc870de..46b2c6386c 100644
--- a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp
+++ b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp
@@ -65,7 +65,7 @@ namespace QSGCompressedAtlasTexture
{
Atlas::Atlas(const QSize &size, uint format)
- : QSGAtlasTexture::AtlasBase(size)
+ : QSGOpenGLAtlasTexture::AtlasBase(size)
, m_format(format)
{
}
@@ -88,10 +88,20 @@ Texture *Atlas::create(const QByteArray &data, int dataLength, int dataOffset, c
void Atlas::generateTexture()
{
+ int bytesPerBlock = 8;
+ switch (m_format) {
+ case QOpenGLTexture::RGBA8_ETC2_EAC:
+ case QOpenGLTexture::RGBA_DXT3:
+ case QOpenGLTexture::RGBA_DXT5:
+ bytesPerBlock = 16;
+ default:
+ break;
+ }
+
QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
funcs->glCompressedTexImage2D(GL_TEXTURE_2D, 0, m_format,
m_size.width(), m_size.height(), 0,
- (m_size.width() * m_size.height()) / 2,
+ (m_size.width() / 4 * m_size.height() / 4) * bytesPerBlock,
nullptr);
}
@@ -117,7 +127,7 @@ void Atlas::uploadPendingTexture(int i)
}
Texture::Texture(Atlas *atlas, const QRect &textureRect, const QByteArray &data, int dataLength, int dataOffset, const QSize &size)
- : QSGAtlasTexture::TextureBase(atlas, textureRect)
+ : QSGOpenGLAtlasTexture::TextureBase(atlas, textureRect)
, m_nonatlas_texture(nullptr)
, m_data(data)
, m_size(size)
diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h
index 59e935b623..78051778f5 100644
--- a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h
+++ b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h
@@ -57,7 +57,7 @@
#include <QtQuick/QSGTexture>
#include <QtQuick/private/qsgareaallocator_p.h>
-#include <QtQuick/private/qsgatlastexture_p.h>
+#include <QtQuick/private/qsgopenglatlastexture_p.h>
QT_BEGIN_NAMESPACE
@@ -67,7 +67,7 @@ namespace QSGCompressedAtlasTexture {
class Texture;
-class Atlas : public QSGAtlasTexture::AtlasBase
+class Atlas : public QSGOpenGLAtlasTexture::AtlasBase
{
public:
Atlas(const QSize &size, uint format);
@@ -84,7 +84,7 @@ private:
uint m_format;
};
-class Texture : public QSGAtlasTexture::TextureBase
+class Texture : public QSGOpenGLAtlasTexture::TextureBase
{
Q_OBJECT
public:
diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp
index d3310bc11c..1a8bddaa6e 100644
--- a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp
+++ b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp
@@ -44,13 +44,15 @@
#include <QOpenGLFunctions>
#include <QDebug>
#include <QtQuick/private/qquickwindow_p.h>
+#include <QtGui/private/qrhi_p.h>
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(QSG_LOG_TEXTUREIO, "qt.scenegraph.textureio");
QSGCompressedTexture::QSGCompressedTexture(const QTextureFileData &texData)
- : m_textureData(texData)
+ : QSGTexture(*(new QSGCompressedTexturePrivate)),
+ m_textureData(texData)
{
m_size = m_textureData.size();
m_hasAlpha = !formatIsOpaque(m_textureData.glInternalFormat());
@@ -68,6 +70,8 @@ QSGCompressedTexture::~QSGCompressedTexture()
funcs->glDeleteTextures(1, &m_textureId);
}
#endif
+
+ delete m_texture;
}
int QSGCompressedTexture::textureId() const
@@ -85,6 +89,20 @@ int QSGCompressedTexture::textureId() const
return m_textureId;
}
+int QSGCompressedTexturePrivate::comparisonKey() const
+{
+ Q_Q(const QSGCompressedTexture);
+ // not textureId() as that would create an id when not yet done - that's not wanted here
+ if (q->m_textureId)
+ return q->m_textureId;
+
+ if (q->m_texture)
+ return int(qintptr(q->m_texture));
+
+ // two textures (and so materials) with not-yet-created texture underneath are never equal
+ return int(qintptr(q));
+}
+
QSize QSGCompressedTexture::textureSize() const
{
return m_size;
@@ -145,6 +163,165 @@ void QSGCompressedTexture::bind()
#endif // QT_CONFIG(opengl)
}
+static QPair<QRhiTexture::Format, bool> toRhiCompressedFormat(uint glinternalformat)
+{
+ switch (glinternalformat) {
+ case QOpenGLTexture::RGB_DXT1:
+ return { QRhiTexture::BC1, false };
+ case QOpenGLTexture::SRGB_DXT1:
+ return { QRhiTexture::BC1, true };
+
+ case QOpenGLTexture::RGBA_DXT3:
+ return { QRhiTexture::BC3, false };
+ case QOpenGLTexture::SRGB_Alpha_DXT3:
+ return { QRhiTexture::BC3, true };
+
+ case QOpenGLTexture::RGBA_DXT5:
+ return { QRhiTexture::BC5, false };
+ case QOpenGLTexture::SRGB_Alpha_DXT5:
+ return { QRhiTexture::BC5, true };
+
+ case QOpenGLTexture::RGB8_ETC2:
+ return { QRhiTexture::ETC2_RGB8, false };
+ case QOpenGLTexture::SRGB8_ETC2:
+ return { QRhiTexture::ETC2_RGB8, true };
+
+ case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2:
+ return { QRhiTexture::ETC2_RGB8A1, false };
+ case QOpenGLTexture::SRGB8_PunchThrough_Alpha1_ETC2:
+ return { QRhiTexture::ETC2_RGB8A1, true };
+
+ case QOpenGLTexture::RGBA8_ETC2_EAC:
+ return { QRhiTexture::ETC2_RGBA8, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ETC2_EAC:
+ return { QRhiTexture::ETC2_RGBA8, true };
+
+ case QOpenGLTexture::RGBA_ASTC_4x4:
+ return { QRhiTexture::ASTC_4x4, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_4x4:
+ return { QRhiTexture::ASTC_4x4, true };
+
+ case QOpenGLTexture::RGBA_ASTC_5x4:
+ return { QRhiTexture::ASTC_5x4, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_5x4:
+ return { QRhiTexture::ASTC_5x4, true };
+
+ case QOpenGLTexture::RGBA_ASTC_5x5:
+ return { QRhiTexture::ASTC_5x5, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_5x5:
+ return { QRhiTexture::ASTC_5x5, true };
+
+ case QOpenGLTexture::RGBA_ASTC_6x5:
+ return { QRhiTexture::ASTC_6x5, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_6x5:
+ return { QRhiTexture::ASTC_6x5, true };
+
+ case QOpenGLTexture::RGBA_ASTC_6x6:
+ return { QRhiTexture::ASTC_6x6, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_6x6:
+ return { QRhiTexture::ASTC_6x6, true };
+
+ case QOpenGLTexture::RGBA_ASTC_8x5:
+ return { QRhiTexture::ASTC_8x5, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x5:
+ return { QRhiTexture::ASTC_8x5, true };
+
+ case QOpenGLTexture::RGBA_ASTC_8x6:
+ return { QRhiTexture::ASTC_8x6, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x6:
+ return { QRhiTexture::ASTC_8x6, true };
+
+ case QOpenGLTexture::RGBA_ASTC_8x8:
+ return { QRhiTexture::ASTC_8x8, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_8x8:
+ return { QRhiTexture::ASTC_8x8, true };
+
+ case QOpenGLTexture::RGBA_ASTC_10x5:
+ return { QRhiTexture::ASTC_10x5, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x5:
+ return { QRhiTexture::ASTC_10x5, true };
+
+ case QOpenGLTexture::RGBA_ASTC_10x6:
+ return { QRhiTexture::ASTC_10x6, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x6:
+ return { QRhiTexture::ASTC_10x6, true };
+
+ case QOpenGLTexture::RGBA_ASTC_10x8:
+ return { QRhiTexture::ASTC_10x8, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x8:
+ return { QRhiTexture::ASTC_10x8, true };
+
+ case QOpenGLTexture::RGBA_ASTC_10x10:
+ return { QRhiTexture::ASTC_10x10, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_10x10:
+ return { QRhiTexture::ASTC_10x10, true };
+
+ case QOpenGLTexture::RGBA_ASTC_12x10:
+ return { QRhiTexture::ASTC_12x10, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_12x10:
+ return { QRhiTexture::ASTC_12x10, true };
+
+ case QOpenGLTexture::RGBA_ASTC_12x12:
+ return { QRhiTexture::ASTC_12x12, false };
+ case QOpenGLTexture::SRGB8_Alpha8_ASTC_12x12:
+ return { QRhiTexture::ASTC_12x12, true };
+
+ default:
+ return { QRhiTexture::UnknownFormat, false };
+ }
+}
+
+QRhiTexture *QSGCompressedTexturePrivate::rhiTexture() const
+{
+ Q_Q(const QSGCompressedTexture);
+ return q->m_texture;
+}
+
+void QSGCompressedTexturePrivate::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_Q(QSGCompressedTexture);
+ if (q->m_uploaded)
+ return;
+
+ q->m_uploaded = true; // even if fails, no point in trying again
+
+ if (!q->m_textureData.isValid()) {
+ qCDebug(QSG_LOG_TEXTUREIO, "Invalid texture data for %s", q->m_textureData.logName().constData());
+ return;
+ }
+
+ const QPair<QRhiTexture::Format, bool> fmt = toRhiCompressedFormat(q->m_textureData.glInternalFormat());
+ if (fmt.first == QRhiTexture::UnknownFormat) {
+ qWarning("Unknown compressed format 0x%x", q->m_textureData.glInternalFormat());
+ return;
+ }
+
+ QRhiTexture::Flags texFlags = 0;
+ if (fmt.second)
+ texFlags |= QRhiTexture::sRGB;
+
+ if (!rhi->isTextureFormatSupported(fmt.first, texFlags)) {
+ qWarning("Unsupported compressed format 0x%x", q->m_textureData.glInternalFormat());
+ return;
+ }
+
+ if (!q->m_texture) {
+ q->m_texture = rhi->newTexture(fmt.first, q->m_size, 1, texFlags);
+ if (!q->m_texture->build()) {
+ qWarning("Failed to create QRhiTexture for compressed data");
+ delete q->m_texture;
+ q->m_texture = nullptr;
+ return;
+ }
+ }
+
+ // only upload mip level 0 since we never do mipmapping for compressed textures (for now?)
+ resourceUpdates->uploadTexture(q->m_texture, QRhiTextureUploadEntry(0, 0,
+ { q->m_textureData.data().constData() + q->m_textureData.dataOffset(), q->m_textureData.dataLength() }));
+
+ q->m_textureData = QTextureFileData(); // Release this memory, not needed anymore
+}
+
QTextureFileData QSGCompressedTexture::textureData() const
{
return m_textureData;
diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h
index c3b58a2389..b858d8ddee 100644
--- a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h
+++ b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h
@@ -52,25 +52,28 @@
//
#include <private/qtexturefiledata_p.h>
-#include <QSGTexture>
-#include <QtQuick/private/qsgcontext_p.h>
+#include <private/qsgcontext_p.h>
+#include <private/qsgtexture_p.h>
#include <QQuickTextureFactory>
#include <QOpenGLFunctions>
QT_BEGIN_NAMESPACE
+class QSGCompressedTexturePrivate;
+
class Q_QUICK_PRIVATE_EXPORT QSGCompressedTexture : public QSGTexture
{
+ Q_DECLARE_PRIVATE(QSGCompressedTexture)
Q_OBJECT
public:
QSGCompressedTexture(const QTextureFileData& texData);
virtual ~QSGCompressedTexture();
- int textureId() const override;
QSize textureSize() const override;
bool hasAlphaChannel() const override;
bool hasMipmaps() const override;
+ int textureId() const override;
void bind() override;
QTextureFileData textureData() const;
@@ -81,14 +84,24 @@ protected:
QTextureFileData m_textureData;
QSize m_size;
mutable uint m_textureId = 0;
+ QRhiTexture *m_texture = nullptr;
bool m_hasAlpha = false;
bool m_uploaded = false;
};
-namespace QSGAtlasTexture {
+namespace QSGOpenGLAtlasTexture {
class Manager;
}
+class QSGCompressedTexturePrivate : public QSGTexturePrivate
+{
+ Q_DECLARE_PUBLIC(QSGCompressedTexture)
+public:
+ int comparisonKey() const override;
+ QRhiTexture *rhiTexture() const override;
+ void updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates) override;
+};
+
class Q_QUICK_PRIVATE_EXPORT QSGCompressedTextureFactory : public QQuickTextureFactory
{
public:
@@ -101,7 +114,7 @@ protected:
QTextureFileData m_textureData;
private:
- friend class QSGAtlasTexture::Manager;
+ friend class QSGOpenGLAtlasTexture::Manager;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp b/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp
index fddac7ed71..62ed342244 100644
--- a/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp
@@ -73,6 +73,22 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \enum QSGAbstractRenderer::MatrixTransformFlag
+
+ Used with setProjectionMatrixToRect() to indicate the expectations towards
+ the generated projection matrix.
+
+ \value MatrixTransformFlipY The traditional assumption in Qt Quick is that
+ Y points up in the normalized device coordinate system. There is at least
+ one modern graphics API where this is not the case (Vulkan). This flag can
+ then be used to get a projection that is appropriate for such an API.
+
+ \sa setProjectionMatrixToRect()
+
+ \since 5.14
+ */
+
+/*!
\fn void QSGAbstractRenderer::renderScene(GLuint fboId = 0)
Render the scene to the specified \a fboId
@@ -224,6 +240,9 @@ QRect QSGAbstractRenderer::viewportRect() const
Convenience method that calls setProjectionMatrix() with an
orthographic matrix generated from \a rect.
+ \note This function assumes that the graphics API uses Y up in its
+ normalized device coordinate system.
+
\sa setProjectionMatrix(), projectionMatrix()
*/
void QSGAbstractRenderer::setProjectionMatrixToRect(const QRectF &rect)
@@ -236,6 +255,42 @@ void QSGAbstractRenderer::setProjectionMatrixToRect(const QRectF &rect)
1,
-1);
setProjectionMatrix(matrix);
+ setProjectionMatrixWithNativeNDC(matrix);
+}
+
+/*!
+ Convenience method that calls setProjectionMatrix() with an
+ orthographic matrix generated from \a rect.
+
+ Set MatrixTransformFlipY in \a flags when the graphics API uses Y down in
+ its normalized device coordinate system (for example, Vulkan).
+
+ \sa setProjectionMatrix(), projectionMatrix()
+
+ \since 5.14
+ */
+void QSGAbstractRenderer::setProjectionMatrixToRect(const QRectF &rect, MatrixTransformFlags flags)
+{
+ const bool flipY = flags.testFlag(MatrixTransformFlipY);
+ QMatrix4x4 matrix;
+ matrix.ortho(rect.x(),
+ rect.x() + rect.width(),
+ flipY ? rect.y() : rect.y() + rect.height(),
+ flipY ? rect.y() + rect.height() : rect.y(),
+ 1,
+ -1);
+ setProjectionMatrix(matrix);
+
+ if (flipY) {
+ matrix.setToIdentity();
+ matrix.ortho(rect.x(),
+ rect.x() + rect.width(),
+ rect.y() + rect.height(),
+ rect.y(),
+ 1,
+ -1);
+ }
+ setProjectionMatrixWithNativeNDC(matrix);
}
/*!
@@ -250,6 +305,15 @@ void QSGAbstractRenderer::setProjectionMatrix(const QMatrix4x4 &matrix)
}
/*!
+ \internal
+ */
+void QSGAbstractRenderer::setProjectionMatrixWithNativeNDC(const QMatrix4x4 &matrix)
+{
+ Q_D(QSGAbstractRenderer);
+ d->m_projection_matrix_native_ndc = matrix;
+}
+
+/*!
Returns the projection matrix
\sa setProjectionMatrix(), setProjectionMatrixToRect()
@@ -261,6 +325,15 @@ QMatrix4x4 QSGAbstractRenderer::projectionMatrix() const
}
/*!
+ \internal
+ */
+QMatrix4x4 QSGAbstractRenderer::projectionMatrixWithNativeNDC() const
+{
+ Q_D(const QSGAbstractRenderer);
+ return d->m_projection_matrix_native_ndc;
+}
+
+/*!
Use \a color to clear the framebuffer when clearMode() is
set to QSGAbstractRenderer::ClearColorBuffer.
diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer.h b/src/quick/scenegraph/coreapi/qsgabstractrenderer.h
index b9805f9db6..1594352dab 100644
--- a/src/quick/scenegraph/coreapi/qsgabstractrenderer.h
+++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer.h
@@ -63,6 +63,13 @@ public:
Q_DECLARE_FLAGS(ClearMode, ClearModeBit)
Q_FLAG(ClearMode)
+ enum MatrixTransformFlag
+ {
+ MatrixTransformFlipY = 0x01
+ };
+ Q_DECLARE_FLAGS(MatrixTransformFlags, MatrixTransformFlag)
+ Q_FLAG(MatrixTransformFlags)
+
~QSGAbstractRenderer() override;
void setRootNode(QSGRootNode *node);
@@ -76,8 +83,11 @@ public:
QRect viewportRect() const;
void setProjectionMatrixToRect(const QRectF &rect);
+ void setProjectionMatrixToRect(const QRectF &rect, MatrixTransformFlags flags);
void setProjectionMatrix(const QMatrix4x4 &matrix);
+ void setProjectionMatrixWithNativeNDC(const QMatrix4x4 &matrix);
QMatrix4x4 projectionMatrix() const;
+ QMatrix4x4 projectionMatrixWithNativeNDC() const;
void setClearColor(const QColor &color);
QColor clearColor() const;
diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h b/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h
index e6b17f4448..bbc4289b2c 100644
--- a/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer_p.h
@@ -78,6 +78,7 @@ public:
QRect m_viewport_rect;
QMatrix4x4 m_projection_matrix;
+ QMatrix4x4 m_projection_matrix_native_ndc;
uint m_mirrored : 1;
};
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index afe3380494..34027cbac7 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
** Contact: https://www.qt.io/licensing/
@@ -57,7 +57,10 @@
#include <private/qnumeric_p.h>
#include <private/qquickprofiler_p.h>
-#include "qsgmaterialshader_p.h"
+#include "qsgmaterialrhishader_p.h"
+
+#include "qsgopenglvisualizer_p.h"
+#include "qsgrhivisualizer_p.h"
#include <algorithm>
@@ -67,6 +70,10 @@
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_DEBUG
+Q_QUICK_PRIVATE_EXPORT bool qsg_test_and_clear_material_failure();
+#endif
+
extern QByteArray qsgShaderRewriter_insertZAttributes(const char *input, QSurfaceFormat::OpenGLContextProfile profile);
int qt_sg_envInt(const char *name, int defaultValue);
@@ -108,8 +115,8 @@ static inline int size_of_type(GLenum type)
4,
sizeof(double)
};
- Q_ASSERT(type >= GL_BYTE && type <= 0x140A); // the value of GL_DOUBLE
- return sizes[type - GL_BYTE];
+ Q_ASSERT(type >= QSGGeometry::ByteType && type <= QSGGeometry::DoubleType);
+ return sizes[type - QSGGeometry::ByteType];
}
bool qsg_sort_element_increasing_order(Element *a, Element *b) { return a->order < b->order; }
@@ -132,44 +139,179 @@ struct QMatrix4x4_Accessor
const float OPAQUE_LIMIT = 0.999f;
-ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material)
+const uint DYNAMIC_VERTEX_INDEX_BUFFER_THRESHOLD = 4;
+const int VERTEX_BUFFER_BINDING = 0;
+const int ZORDER_BUFFER_BINDING = VERTEX_BUFFER_BINDING + 1;
+
+static inline uint aligned(uint v, uint byteAlign)
+{
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+QRhiVertexInputAttribute::Format qsg_vertexInputFormat(const QSGGeometry::Attribute &a)
+{
+ switch (a.type) {
+ case QSGGeometry::FloatType:
+ if (a.tupleSize == 4)
+ return QRhiVertexInputAttribute::Float4;
+ if (a.tupleSize == 3)
+ return QRhiVertexInputAttribute::Float3;
+ if (a.tupleSize == 2)
+ return QRhiVertexInputAttribute::Float2;
+ if (a.tupleSize == 1)
+ return QRhiVertexInputAttribute::Float;
+ break;
+ case QSGGeometry::UnsignedByteType:
+ if (a.tupleSize == 4)
+ return QRhiVertexInputAttribute::UNormByte4;
+ if (a.tupleSize == 2)
+ return QRhiVertexInputAttribute::UNormByte2;
+ if (a.tupleSize == 1)
+ return QRhiVertexInputAttribute::UNormByte;
+ break;
+ default:
+ break;
+ }
+ qWarning("Unsupported attribute type 0x%x with %d components", a.type, a.tupleSize);
+ Q_UNREACHABLE();
+ return QRhiVertexInputAttribute::Float;
+}
+
+static QRhiVertexInputLayout calculateVertexInputLayout(const QSGMaterialRhiShader *s, const QSGGeometry *geometry, bool batchable)
+{
+ Q_ASSERT(geometry);
+ const QSGMaterialRhiShaderPrivate *sd = QSGMaterialRhiShaderPrivate::get(s);
+ if (!sd->vertexShader) {
+ qWarning("No vertex shader in QSGMaterialRhiShader %p", s);
+ return QRhiVertexInputLayout();
+ }
+
+ const int attrCount = geometry->attributeCount();
+ QVarLengthArray<QRhiVertexInputAttribute, 8> inputAttributes;
+ inputAttributes.reserve(attrCount + 1);
+ int offset = 0;
+ for (int i = 0; i < attrCount; ++i) {
+ const QSGGeometry::Attribute &a = geometry->attributes()[i];
+ if (!sd->vertexShader->vertexInputLocations.contains(a.position)) {
+ qWarning("Vertex input %d is present in material but not in shader. This is wrong.",
+ a.position);
+ }
+ inputAttributes.append(QRhiVertexInputAttribute(VERTEX_BUFFER_BINDING, a.position, qsg_vertexInputFormat(a), offset));
+ offset += a.tupleSize * size_of_type(a.type);
+ }
+ if (batchable) {
+ inputAttributes.append(QRhiVertexInputAttribute(ZORDER_BUFFER_BINDING, sd->vertexShader->qt_order_attrib_location,
+ QRhiVertexInputAttribute::Float, 0));
+ }
+
+ Q_ASSERT(VERTEX_BUFFER_BINDING == 0 && ZORDER_BUFFER_BINDING == 1); // not very flexible
+ QVarLengthArray<QRhiVertexInputBinding, 2> inputBindings;
+ inputBindings.append(QRhiVertexInputBinding(geometry->sizeOfVertex()));
+ if (batchable)
+ inputBindings.append(QRhiVertexInputBinding(sizeof(float)));
+
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings(inputBindings.cbegin(), inputBindings.cend());
+ inputLayout.setAttributes(inputAttributes.cbegin(), inputAttributes.cend());
+
+ return inputLayout;
+}
+
+QRhiCommandBuffer::IndexFormat qsg_indexFormat(const QSGGeometry *geometry)
+{
+ switch (geometry->indexType()) {
+ case QSGGeometry::UnsignedShortType:
+ return QRhiCommandBuffer::IndexUInt16;
+ break;
+ case QSGGeometry::UnsignedIntType:
+ return QRhiCommandBuffer::IndexUInt32;
+ break;
+ default:
+ Q_UNREACHABLE();
+ return QRhiCommandBuffer::IndexUInt16;
+ }
+}
+
+QRhiGraphicsPipeline::Topology qsg_topology(int geomDrawMode)
+{
+ QRhiGraphicsPipeline::Topology topology = QRhiGraphicsPipeline::Triangles;
+ switch (geomDrawMode) {
+ case QSGGeometry::DrawPoints:
+ topology = QRhiGraphicsPipeline::Points;
+ break;
+ case QSGGeometry::DrawLines:
+ topology = QRhiGraphicsPipeline::Lines;
+ break;
+ case QSGGeometry::DrawLineStrip:
+ topology = QRhiGraphicsPipeline::LineStrip;
+ break;
+ case QSGGeometry::DrawTriangles:
+ topology = QRhiGraphicsPipeline::Triangles;
+ break;
+ case QSGGeometry::DrawTriangleStrip:
+ topology = QRhiGraphicsPipeline::TriangleStrip;
+ break;
+ default:
+ qWarning("Primitive topology 0x%x not supported", geomDrawMode);
+ break;
+ }
+ return topology;
+}
+
+ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material, bool enableRhiShaders, const QSGGeometry *geometry)
{
QSGMaterialType *type = material->type();
Shader *shader = rewrittenShaders.value(type, 0);
if (shader)
return shader;
+ if (enableRhiShaders && !material->flags().testFlag(QSGMaterial::SupportsRhiShader)) {
+ qWarning("The material failed to provide a working QShader pack");
+ return nullptr;
+ }
+
if (QSG_LOG_TIME_COMPILATION().isDebugEnabled())
qsg_renderer_timer.start();
Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphContextFrame);
- QSGMaterialShader *s = material->createShader();
- QOpenGLContext *ctx = context->openglContext();
- QSurfaceFormat::OpenGLContextProfile profile = ctx->format().profile();
-
- QOpenGLShaderProgram *p = s->program();
- char const *const *attr = s->attributeNames();
- int i;
- for (i = 0; attr[i]; ++i) {
- if (*attr[i])
- p->bindAttributeLocation(attr[i], i);
- }
- p->bindAttributeLocation("_qt_order", i);
- context->compileShader(s, material, qsgShaderRewriter_insertZAttributes(s->vertexShader(), profile), nullptr);
- context->initializeShader(s);
- if (!p->isLinked())
- return nullptr;
-
shader = new Shader;
- shader->program = s;
- shader->pos_order = i;
- shader->id_zRange = p->uniformLocation("_qt_zRange");
- shader->lastOpacity = 0;
+ if (enableRhiShaders) {
+ material->setFlag(QSGMaterial::RhiShaderWanted, true);
+ QSGMaterialRhiShader *s = static_cast<QSGMaterialRhiShader *>(material->createShader());
+ material->setFlag(QSGMaterial::RhiShaderWanted, false);
+ context->initializeRhiShader(s, QShader::BatchableVertexShader);
+ shader->programRhi.program = s;
+ shader->programRhi.inputLayout = calculateVertexInputLayout(s, geometry, true);
+ QSGMaterialRhiShaderPrivate *sD = QSGMaterialRhiShaderPrivate::get(s);
+ shader->programRhi.shaderStages = {
+ { QRhiGraphicsShaderStage::Vertex, sD->shader(QShader::VertexStage), QShader::BatchableVertexShader },
+ { QRhiGraphicsShaderStage::Fragment, sD->shader(QShader::FragmentStage) }
+ };
+ } else {
+ QSGMaterialShader *s = material->createShader();
+ QOpenGLContext *ctx = context->openglContext();
+ QSurfaceFormat::OpenGLContextProfile profile = ctx->format().profile();
+ QOpenGLShaderProgram *p = s->program();
+ char const *const *attr = s->attributeNames();
+ int i;
+ for (i = 0; attr[i]; ++i) {
+ if (*attr[i])
+ p->bindAttributeLocation(attr[i], i);
+ }
+ p->bindAttributeLocation("_qt_order", i);
+ context->compileShader(s, material, qsgShaderRewriter_insertZAttributes(s->vertexShader(), profile), nullptr);
+ context->initializeShader(s);
+ if (!p->isLinked()) {
+ delete shader;
+ return nullptr;
+ }
+ shader->programGL.program = s;
+ shader->programGL.pos_order = i;
+ }
- Q_ASSERT(shader->pos_order >= 0);
- Q_ASSERT(shader->id_zRange >= 0);
+ shader->lastOpacity = 0;
- qCDebug(QSG_LOG_TIME_COMPILATION, "shader compiled in %dms", (int) qsg_renderer_timer.elapsed());
+ qCDebug(QSG_LOG_TIME_COMPILATION, "material shaders prepared in %dms", (int) qsg_renderer_timer.elapsed());
Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphContextFrame,
QQuickProfiler::SceneGraphContextMaterialCompile);
@@ -178,25 +320,43 @@ ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material)
return shader;
}
-ShaderManager::Shader *ShaderManager::prepareMaterialNoRewrite(QSGMaterial *material)
+ShaderManager::Shader *ShaderManager::prepareMaterialNoRewrite(QSGMaterial *material, bool enableRhiShaders, const QSGGeometry *geometry)
{
QSGMaterialType *type = material->type();
Shader *shader = stockShaders.value(type, 0);
if (shader)
return shader;
+ if (enableRhiShaders && !material->flags().testFlag(QSGMaterial::SupportsRhiShader)) {
+ qWarning("The material failed to provide a working QShader pack");
+ return nullptr;
+ }
+
if (QSG_LOG_TIME_COMPILATION().isDebugEnabled())
qsg_renderer_timer.start();
Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphContextFrame);
- QSGMaterialShader *s = static_cast<QSGMaterialShader *>(material->createShader());
- context->compileShader(s, material);
- context->initializeShader(s);
+ shader = new Shader;
+ if (enableRhiShaders) {
+ material->setFlag(QSGMaterial::RhiShaderWanted, true);
+ QSGMaterialRhiShader *s = static_cast<QSGMaterialRhiShader *>(material->createShader());
+ material->setFlag(QSGMaterial::RhiShaderWanted, false);
+ context->initializeRhiShader(s, QShader::StandardShader);
+ shader->programRhi.program = s;
+ shader->programRhi.inputLayout = calculateVertexInputLayout(s, geometry, false);
+ QSGMaterialRhiShaderPrivate *sD = QSGMaterialRhiShaderPrivate::get(s);
+ shader->programRhi.shaderStages = {
+ { QRhiGraphicsShaderStage::Vertex, sD->shader(QShader::VertexStage) },
+ { QRhiGraphicsShaderStage::Fragment, sD->shader(QShader::FragmentStage) }
+ };
+ } else {
+ QSGMaterialShader *s = material->createShader();
+ context->compileShader(s, material);
+ context->initializeShader(s);
+ shader->programGL.program = s;
+ shader->programGL.pos_order = -1;
+ }
- shader = new Shader();
- shader->program = s;
- shader->id_zRange = -1;
- shader->pos_order = -1;
shader->lastOpacity = 0;
stockShaders[type] = shader;
@@ -216,6 +376,45 @@ void ShaderManager::invalidated()
rewrittenShaders.clear();
delete blitProgram;
blitProgram = nullptr;
+
+ qDeleteAll(srbCache);
+ srbCache.clear();
+}
+
+void ShaderManager::clearCachedRendererData()
+{
+ for (ShaderManager::Shader *sms : stockShaders) {
+ QSGMaterialRhiShader *s = sms->programRhi.program;
+ if (s) {
+ QSGMaterialRhiShaderPrivate *sd = QSGMaterialRhiShaderPrivate::get(s);
+ sd->clearCachedRendererData();
+ }
+ }
+ for (ShaderManager::Shader *sms : rewrittenShaders) {
+ QSGMaterialRhiShader *s = sms->programRhi.program;
+ if (s) {
+ QSGMaterialRhiShaderPrivate *sd = QSGMaterialRhiShaderPrivate::get(s);
+ sd->clearCachedRendererData();
+ }
+ }
+}
+
+QRhiShaderResourceBindings *ShaderManager::srb(const ShaderResourceBindingList &bindings)
+{
+ auto it = srbCache.constFind(bindings);
+ if (it != srbCache.constEnd())
+ return *it;
+
+ QRhiShaderResourceBindings *srb = context->rhi()->newShaderResourceBindings();
+ srb->setBindings(bindings.cbegin(), bindings.cend());
+ if (srb->build()) {
+ srbCache.insert(bindings, srb);
+ } else {
+ qWarning("Failed to build srb");
+ delete srb;
+ srb = nullptr;
+ }
+ return srb;
}
void qsg_dumpShadowRoots(BatchRootInfo *i, int indent)
@@ -304,8 +503,8 @@ void Updater::updateStates(QSGNode *n)
qDebug(" - forceupdate");
}
- if (Q_UNLIKELY(renderer->m_visualizeMode == Renderer::VisualizeChanges))
- renderer->visualizeChangesPrepare(sn);
+ if (Q_UNLIKELY(renderer->m_visualizer->mode() == Visualizer::VisualizeChanges))
+ renderer->m_visualizer->visualizeChangesPrepare(sn);
visitNode(sn);
}
@@ -531,11 +730,12 @@ void Updater::updateRootTransforms(Node *node, Node *root, const QMatrix4x4 &com
}
}
-int qsg_positionAttribute(QSGGeometry *g) {
+int qsg_positionAttribute(QSGGeometry *g)
+{
int vaOffset = 0;
for (int a=0; a<g->attributeCount(); ++a) {
const QSGGeometry::Attribute &attr = g->attributes()[a];
- if (attr.isVertexCoordinate && attr.tupleSize == 2 && attr.type == GL_FLOAT) {
+ if (attr.isVertexCoordinate && attr.tupleSize == 2 && attr.type == QSGGeometry::FloatType) {
return vaOffset;
}
vaOffset += attr.tupleSize * size_of_type(attr.type);
@@ -773,15 +973,29 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx)
, m_currentStencilValue(0)
, m_clipMatrixId(0)
, m_currentClip(nullptr)
- , m_currentClipType(NoClip)
+ , m_currentClipType(ClipState::NoClip)
, m_vertexUploadPool(256)
, m_indexUploadPool(64)
, m_vao(nullptr)
- , m_visualizeMode(VisualizeNothing)
{
- initializeOpenGLFunctions();
+ m_rhi = m_context->rhi();
+ if (m_rhi) {
+ m_ubufAlignment = m_rhi->ubufAlignment();
+ m_uint32IndexForRhi = !m_rhi->isFeatureSupported(QRhi::NonFourAlignedEffectiveIndexBufferOffset);
+ if (qEnvironmentVariableIntValue("QSG_RHI_UINT32_INDEX"))
+ m_uint32IndexForRhi = true;
+ m_visualizer = new RhiVisualizer(this);
+ } else {
+ initializeOpenGLFunctions();
+ m_uint32IndexForRhi = false;
+ m_visualizer = new OpenGLVisualizer(this);
+ }
+
setNodeUpdater(new Updater(this));
+ // The shader manager is shared between renderers (think for example Item
+ // layers that create a new Renderer each) with the same rendercontext
+ // (i.e. QRhi or QOpenGLContext).
m_shaderManager = ctx->findChild<ShaderManager *>(QStringLiteral("__qt_ShaderManager"), Qt::FindDirectChildrenOnly);
if (!m_shaderManager) {
m_shaderManager = new ShaderManager(ctx);
@@ -810,20 +1024,29 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx)
? "static" : (m_bufferStrategy == GL_DYNAMIC_DRAW ? "dynamic" : "stream")));
}
- // If rendering with an OpenGL Core profile context, we need to create a VAO
- // to hold our vertex specification state.
- if (m_context->openglContext()->format().profile() == QSurfaceFormat::CoreProfile) {
- m_vao = new QOpenGLVertexArrayObject(this);
- m_vao->create();
- }
+ if (!m_rhi) {
+ // If rendering with an OpenGL Core profile context, we need to create a VAO
+ // to hold our vertex specification state.
+ if (m_context->openglContext()->format().profile() == QSurfaceFormat::CoreProfile) {
+ m_vao = new QOpenGLVertexArrayObject(this);
+ m_vao->create();
+ }
- bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
- m_useDepthBuffer = useDepth && ctx->openglContext()->format().depthBufferSize() > 0;
+ bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
+ m_useDepthBuffer = useDepth && ctx->openglContext()->format().depthBufferSize() > 0;
+ }
}
static void qsg_wipeBuffer(Buffer *buffer, QOpenGLFunctions *funcs)
{
- funcs->glDeleteBuffers(1, &buffer->id);
+ if (buffer->buf) {
+ //qDebug("releasing rhibuf %p", buffer->buf);
+ delete buffer->buf;
+ }
+
+ if (buffer->id)
+ funcs->glDeleteBuffers(1, &buffer->id);
+
// The free here is ok because we're in one of two situations.
// 1. We're using the upload pool in which case unmap will have set the
// data pointer to 0 and calling free on 0 is ok.
@@ -837,12 +1060,14 @@ static void qsg_wipeBatch(Batch *batch, QOpenGLFunctions *funcs, bool separateIn
qsg_wipeBuffer(&batch->vbo, funcs);
if (separateIndexBuffer)
qsg_wipeBuffer(&batch->ibo, funcs);
+ delete batch->ubuf;
+ batch->stencilClipState.reset();
delete batch;
}
Renderer::~Renderer()
{
- if (QOpenGLContext::currentContext()) {
+ if (m_rhi || QOpenGLContext::currentContext()) {
// Clean up batches and buffers
const bool separateIndexBuffer = m_context->separateIndexBuffer();
for (int i = 0; i < m_opaqueBatches.size(); ++i)
@@ -864,6 +1089,42 @@ Renderer::~Renderer()
else
m_elementAllocator.release(e);
}
+
+ destroyGraphicsResources();
+
+ delete m_visualizer;
+}
+
+void Renderer::destroyGraphicsResources()
+{
+ // If this is from the dtor, then the shader manager and its already
+ // prepared shaders will stay around for other renderers -> the cached data
+ // in the rhi shaders have to be purged as it may refer to samplers we
+ // are going to destroy.
+ m_shaderManager->clearCachedRendererData();
+
+ qDeleteAll(m_pipelines);
+ qDeleteAll(m_samplers);
+
+ m_stencilClipCommon.reset();
+
+ delete m_dummyTexture;
+
+ m_visualizer->releaseResources();
+}
+
+void Renderer::releaseCachedResources()
+{
+ m_shaderManager->invalidated();
+
+ destroyGraphicsResources();
+
+ m_pipelines.clear();
+ m_samplers.clear();
+ m_dummyTexture = nullptr;
+
+ if (m_rhi)
+ m_rhi->releaseCachedResources();
}
void Renderer::invalidateAndRecycleBatch(Batch *b)
@@ -887,7 +1148,7 @@ void Renderer::invalidateAndRecycleBatch(Batch *b)
*/
void Renderer::map(Buffer *buffer, int byteSize, bool isIndexBuf)
{
- if (!m_context->hasBrokenIndexBufferObjects() && m_visualizeMode == VisualizeNothing) {
+ if (!m_context->hasBrokenIndexBufferObjects() && m_visualizer->mode() == Visualizer::VisualizeNothing) {
// Common case, use a shared memory pool for uploading vertex data to avoid
// excessive reevaluation
QDataBuffer<char> &pool = m_context->separateIndexBuffer() && isIndexBuf
@@ -905,14 +1166,54 @@ void Renderer::map(Buffer *buffer, int byteSize, bool isIndexBuf)
void Renderer::unmap(Buffer *buffer, bool isIndexBuf)
{
- if (buffer->id == 0)
- glGenBuffers(1, &buffer->id);
- GLenum target = isIndexBuf ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER;
- glBindBuffer(target, buffer->id);
- glBufferData(target, buffer->size, buffer->data, m_bufferStrategy);
-
- if (!m_context->hasBrokenIndexBufferObjects() && m_visualizeMode == VisualizeNothing) {
- buffer->data = nullptr;
+ if (m_rhi) {
+ // Batches are pooled and reused which means the QRhiBuffer will be
+ // still valid in a recycled Batch. We only hit the newBuffer() path
+ // for brand new Batches.
+ if (!buffer->buf) {
+ buffer->buf = m_rhi->newBuffer(QRhiBuffer::Immutable,
+ isIndexBuf ? QRhiBuffer::IndexBuffer : QRhiBuffer::VertexBuffer,
+ buffer->size);
+ if (!buffer->buf->build())
+ qWarning("Failed to build vertex/index buffer of size %d", buffer->size);
+// else
+// qDebug("created rhibuf %p size %d", buffer->buf, buffer->size);
+ } else {
+ bool needsRebuild = false;
+ if (buffer->buf->size() < buffer->size) {
+ buffer->buf->setSize(buffer->size);
+ needsRebuild = true;
+ }
+ if (buffer->buf->type() != QRhiBuffer::Dynamic
+ && buffer->nonDynamicChangeCount > DYNAMIC_VERTEX_INDEX_BUFFER_THRESHOLD)
+ {
+ buffer->buf->setType(QRhiBuffer::Dynamic);
+ buffer->nonDynamicChangeCount = 0;
+ needsRebuild = true;
+ }
+ if (needsRebuild) {
+ //qDebug("rebuilding rhibuf %p size %d type Dynamic", buffer->buf, buffer->size);
+ buffer->buf->build();
+ }
+ }
+ if (buffer->buf->type() != QRhiBuffer::Dynamic) {
+ m_resourceUpdates->uploadStaticBuffer(buffer->buf,
+ QByteArray::fromRawData(buffer->data, buffer->size));
+ buffer->nonDynamicChangeCount += 1;
+ } else {
+ m_resourceUpdates->updateDynamicBuffer(buffer->buf, 0, buffer->size,
+ QByteArray::fromRawData(buffer->data, buffer->size));
+ }
+ if (m_visualizer->mode() == Visualizer::VisualizeNothing)
+ buffer->data = nullptr;
+ } else {
+ if (buffer->id == 0)
+ glGenBuffers(1, &buffer->id);
+ GLenum target = isIndexBuf ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER;
+ glBindBuffer(target, buffer->id);
+ glBufferData(target, buffer->size, buffer->data, m_bufferStrategy);
+ if (!m_context->hasBrokenIndexBufferObjects() && m_visualizer->mode() == Visualizer::VisualizeNothing)
+ buffer->data = nullptr;
}
}
@@ -1098,8 +1399,12 @@ void Renderer::nodeWasRemoved(Node *node)
m_elementsToDelete.add(e);
if (m_renderNodeElements.isEmpty()) {
- static bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
- m_useDepthBuffer = useDepth && m_context->openglContext()->format().depthBufferSize() > 0;
+ if (m_rhi) {
+ m_useDepthBuffer = true;
+ } else {
+ static bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
+ m_useDepthBuffer = useDepth && m_context->openglContext()->format().depthBufferSize() > 0;
+ }
}
}
}
@@ -1539,7 +1844,7 @@ void Renderer::prepareOpaqueBatches()
if (gni->clipList() == gnj->clipList()
&& gni->geometry()->drawingMode() == gnj->geometry()->drawingMode()
- && (gni->geometry()->drawingMode() != GL_LINES || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
+ && (gni->geometry()->drawingMode() != QSGGeometry::DrawLines || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
&& gni->geometry()->attributes() == gnj->geometry()->attributes()
&& gni->inheritedOpacity() == gnj->inheritedOpacity()
&& gni->activeMaterial()->type() == gnj->activeMaterial()->type()
@@ -1638,7 +1943,7 @@ void Renderer::prepareAlphaBatches()
if (gni->clipList() == gnj->clipList()
&& gni->geometry()->drawingMode() == gnj->geometry()->drawingMode()
- && (gni->geometry()->drawingMode() != GL_LINES || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
+ && (gni->geometry()->drawingMode() != QSGGeometry::DrawLines || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
&& gni->geometry()->attributes() == gnj->geometry()->attributes()
&& gni->inheritedOpacity() == gnj->inheritedOpacity()
&& gni->activeMaterial()->type() == gnj->activeMaterial()->type()
@@ -1666,19 +1971,20 @@ void Renderer::prepareAlphaBatches()
}
-static inline int qsg_fixIndexCount(int iCount, GLenum drawMode) {
+static inline int qsg_fixIndexCount(int iCount, int drawMode)
+{
switch (drawMode) {
- case GL_TRIANGLE_STRIP:
+ case QSGGeometry::DrawTriangleStrip:
// Merged triangle strips need to contain degenerate triangles at the beginning and end.
// One could save 2 uploaded ushorts here by ditching the padding for the front of the
// first and the end of the last, but for simplicity, we simply don't care.
// Those extra triangles will be skipped while drawing to preserve the strip's parity
// anyhow.
return iCount + 2;
- case GL_LINES:
+ case QSGGeometry::DrawLines:
// For lines we drop the last vertex if the number of vertices is uneven.
return iCount - (iCount % 2);
- case GL_TRIANGLES:
+ case QSGGeometry::DrawTriangles:
// For triangles we drop trailing vertices until the result is divisible by 3.
return iCount - (iCount % 3);
default:
@@ -1700,7 +2006,7 @@ static inline int qsg_fixIndexCount(int iCount, GLenum drawMode) {
* iBase: The starting index for this element in the batch
*/
-void Renderer::uploadMergedElement(Element *e, int vaOffset, char **vertexData, char **zData, char **indexData, quint16 *iBase, int *indexCount)
+void Renderer::uploadMergedElement(Element *e, int vaOffset, char **vertexData, char **zData, char **indexData, void *iBasePtr, int *indexCount)
{
if (Q_UNLIKELY(debug_upload())) qDebug() << " - uploading element:" << e << e->node << (void *) *vertexData << (qintptr) (*zData - *vertexData) << (qintptr) (*indexData - *vertexData);
QSGGeometry *g = e->node->geometry();
@@ -1736,39 +2042,71 @@ void Renderer::uploadMergedElement(Element *e, int vaOffset, char **vertexData,
}
int iCount = g->indexCount();
- quint16 *indices = (quint16 *) *indexData;
+ if (m_uint32IndexForRhi) {
+ // can only happen when using the rhi
+ quint32 *iBase = (quint32 *) iBasePtr;
+ quint32 *indices = (quint32 *) *indexData;
+ if (iCount == 0) {
+ iCount = vCount;
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip)
+ *indices++ = *iBase;
+ else
+ iCount = qsg_fixIndexCount(iCount, g->drawingMode());
- if (iCount == 0) {
- iCount = vCount;
- if (g->drawingMode() == GL_TRIANGLE_STRIP)
- *indices++ = *iBase;
- else
- iCount = qsg_fixIndexCount(iCount, g->drawingMode());
+ for (int i=0; i<iCount; ++i)
+ indices[i] = *iBase + i;
+ } else {
+ // source index data in QSGGeometry is always ushort (we would not merge otherwise)
+ const quint16 *srcIndices = g->indexDataAsUShort();
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip)
+ *indices++ = *iBase + srcIndices[0];
+ else
+ iCount = qsg_fixIndexCount(iCount, g->drawingMode());
- for (int i=0; i<iCount; ++i)
- indices[i] = *iBase + i;
+ for (int i=0; i<iCount; ++i)
+ indices[i] = *iBase + srcIndices[i];
+ }
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip) {
+ indices[iCount] = indices[iCount - 1];
+ iCount += 2;
+ }
+ *iBase += vCount;
} else {
- const quint16 *srcIndices = g->indexDataAsUShort();
- if (g->drawingMode() == GL_TRIANGLE_STRIP)
- *indices++ = *iBase + srcIndices[0];
- else
- iCount = qsg_fixIndexCount(iCount, g->drawingMode());
+ // normally batching is only done for ushort index data
+ quint16 *iBase = (quint16 *) iBasePtr;
+ quint16 *indices = (quint16 *) *indexData;
+ if (iCount == 0) {
+ iCount = vCount;
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip)
+ *indices++ = *iBase;
+ else
+ iCount = qsg_fixIndexCount(iCount, g->drawingMode());
- for (int i=0; i<iCount; ++i)
- indices[i] = *iBase + srcIndices[i];
- }
- if (g->drawingMode() == GL_TRIANGLE_STRIP) {
- indices[iCount] = indices[iCount - 1];
- iCount += 2;
+ for (int i=0; i<iCount; ++i)
+ indices[i] = *iBase + i;
+ } else {
+ const quint16 *srcIndices = g->indexDataAsUShort();
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip)
+ *indices++ = *iBase + srcIndices[0];
+ else
+ iCount = qsg_fixIndexCount(iCount, g->drawingMode());
+
+ for (int i=0; i<iCount; ++i)
+ indices[i] = *iBase + srcIndices[i];
+ }
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip) {
+ indices[iCount] = indices[iCount - 1];
+ iCount += 2;
+ }
+ *iBase += vCount;
}
*vertexData += vCount * vSize;
- *indexData += iCount * sizeof(quint16);
- *iBase += vCount;
+ *indexData += iCount * mergedIndexElemSize();
*indexCount += iCount;
}
-static QMatrix4x4 qsg_matrixForRoot(Node *node)
+QMatrix4x4 qsg_matrixForRoot(Node *node)
{
if (node->type() == QSGNode::TransformNodeType)
return static_cast<QSGTransformNode *>(node->sgNode)->combinedMatrix();
@@ -1779,66 +2117,67 @@ static QMatrix4x4 qsg_matrixForRoot(Node *node)
void Renderer::uploadBatch(Batch *b)
{
- // Early out if nothing has changed in this batch..
- if (!b->needsUpload) {
- if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch:" << b << "already uploaded...";
- return;
- }
-
- if (!b->first) {
- if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch:" << b << "is invalid...";
- return;
- }
-
- if (b->isRenderNode) {
- if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch: " << b << "is a render node...";
- return;
- }
+ // Early out if nothing has changed in this batch..
+ if (!b->needsUpload) {
+ if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch:" << b << "already uploaded...";
+ return;
+ }
- // Figure out if we can merge or not, if not, then just render the batch as is..
- Q_ASSERT(b->first);
- Q_ASSERT(b->first->node);
+ if (!b->first) {
+ if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch:" << b << "is invalid...";
+ return;
+ }
- QSGGeometryNode *gn = b->first->node;
- QSGGeometry *g = gn->geometry();
- QSGMaterial::Flags flags = gn->activeMaterial()->flags();
- bool canMerge = (g->drawingMode() == GL_TRIANGLES || g->drawingMode() == GL_TRIANGLE_STRIP ||
- g->drawingMode() == GL_LINES || g->drawingMode() == GL_POINTS)
- && b->positionAttribute >= 0
- && g->indexType() == GL_UNSIGNED_SHORT
- && (flags & (QSGMaterial::CustomCompileStep | QSGMaterial_FullMatrix)) == 0
- && ((flags & QSGMaterial::RequiresFullMatrixExceptTranslate) == 0 || b->isTranslateOnlyToRoot())
- && b->isSafeToBatch();
+ if (b->isRenderNode) {
+ if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch: " << b << "is a render node...";
+ return;
+ }
- b->merged = canMerge;
+ // Figure out if we can merge or not, if not, then just render the batch as is..
+ Q_ASSERT(b->first);
+ Q_ASSERT(b->first->node);
- // Figure out how much memory we need...
- b->vertexCount = 0;
- b->indexCount = 0;
- int unmergedIndexSize = 0;
- Element *e = b->first;
+ QSGGeometryNode *gn = b->first->node;
+ QSGGeometry *g = gn->geometry();
+ QSGMaterial::Flags flags = gn->activeMaterial()->flags();
+ bool canMerge = (g->drawingMode() == QSGGeometry::DrawTriangles || g->drawingMode() == QSGGeometry::DrawTriangleStrip ||
+ g->drawingMode() == QSGGeometry::DrawLines || g->drawingMode() == QSGGeometry::DrawPoints)
+ && b->positionAttribute >= 0
+ && g->indexType() == QSGGeometry::UnsignedShortType
+ && (flags & (QSGMaterial::CustomCompileStep | QSGMaterial_FullMatrix)) == 0
+ && ((flags & QSGMaterial::RequiresFullMatrixExceptTranslate) == 0 || b->isTranslateOnlyToRoot())
+ && b->isSafeToBatch();
+
+ b->merged = canMerge;
+
+ // Figure out how much memory we need...
+ b->vertexCount = 0;
+ b->indexCount = 0;
+ int unmergedIndexSize = 0;
+ Element *e = b->first;
- while (e) {
- QSGGeometry *eg = e->node->geometry();
- b->vertexCount += eg->vertexCount();
- int iCount = eg->indexCount();
- if (b->merged) {
- if (iCount == 0)
- iCount = eg->vertexCount();
- iCount = qsg_fixIndexCount(iCount, g->drawingMode());
- } else {
- unmergedIndexSize += iCount * eg->sizeOfIndex();
- }
- b->indexCount += iCount;
- e = e->nextInBatch;
+ while (e) {
+ QSGGeometry *eg = e->node->geometry();
+ b->vertexCount += eg->vertexCount();
+ int iCount = eg->indexCount();
+ if (b->merged) {
+ if (iCount == 0)
+ iCount = eg->vertexCount();
+ iCount = qsg_fixIndexCount(iCount, g->drawingMode());
+ } else {
+ const int effectiveIndexSize = m_uint32IndexForRhi ? sizeof(quint32) : eg->sizeOfIndex();
+ unmergedIndexSize += iCount * effectiveIndexSize;
}
+ b->indexCount += iCount;
+ e = e->nextInBatch;
+ }
- // Abort if there are no vertices in this batch.. We abort this late as
- // this is a broken usecase which we do not care to optimize for...
- if (b->vertexCount == 0 || (b->merged && b->indexCount == 0))
- return;
+ // Abort if there are no vertices in this batch.. We abort this late as
+ // this is a broken usecase which we do not care to optimize for...
+ if (b->vertexCount == 0 || (b->merged && b->indexCount == 0))
+ return;
- /* Allocate memory for this batch. Merged batches are divided into three separate blocks
+ /* Allocate memory for this batch. Merged batches are divided into three separate blocks
1. Vertex data for all elements, as they were in the QSGGeometry object, but
with the tranform relative to this batch's root applied. The vertex data
is otherwise unmodified.
@@ -1850,119 +2189,161 @@ void Renderer::uploadBatch(Batch *b)
primitive. These are unsigned shorts for merged and arbitrary for
non-merged.
*/
- int bufferSize = b->vertexCount * g->sizeOfVertex();
- int ibufferSize = 0;
- if (b->merged) {
- ibufferSize = b->indexCount * sizeof(quint16);
- if (m_useDepthBuffer)
- bufferSize += b->vertexCount * sizeof(float);
- } else {
- ibufferSize = unmergedIndexSize;
- }
+ int bufferSize = b->vertexCount * g->sizeOfVertex();
+ int ibufferSize = 0;
+ if (b->merged) {
+ ibufferSize = b->indexCount * mergedIndexElemSize();
+ if (m_useDepthBuffer)
+ bufferSize += b->vertexCount * sizeof(float);
+ } else {
+ ibufferSize = unmergedIndexSize;
+ }
- const bool separateIndexBuffer = m_context->separateIndexBuffer();
- if (separateIndexBuffer)
- map(&b->ibo, ibufferSize, true);
- else
- bufferSize += ibufferSize;
- map(&b->vbo, bufferSize);
+ const bool separateIndexBuffer = m_context->separateIndexBuffer();
+ if (separateIndexBuffer)
+ map(&b->ibo, ibufferSize, true);
+ else
+ bufferSize += ibufferSize;
+ map(&b->vbo, bufferSize);
- if (Q_UNLIKELY(debug_upload())) qDebug() << " - batch" << b << " first:" << b->first << " root:"
- << b->root << " merged:" << b->merged << " positionAttribute" << b->positionAttribute
- << " vbo:" << b->vbo.id << ":" << b->vbo.size;
+ if (Q_UNLIKELY(debug_upload())) qDebug() << " - batch" << b << " first:" << b->first << " root:"
+ << b->root << " merged:" << b->merged << " positionAttribute" << b->positionAttribute
+ << " vbo:" << b->vbo.id << ":" << b->vbo.size;
- if (b->merged) {
- char *vertexData = b->vbo.data;
- char *zData = vertexData + b->vertexCount * g->sizeOfVertex();
- char *indexData = separateIndexBuffer
- ? b->ibo.data
- : zData + (int(m_useDepthBuffer) * b->vertexCount * sizeof(float));
-
- quint16 iOffset = 0;
- e = b->first;
- int verticesInSet = 0;
- int indicesInSet = 0;
- b->drawSets.reset();
- int drawSetIndices = separateIndexBuffer ? 0 : indexData - vertexData;
- const auto indexBase = separateIndexBuffer ? b->ibo.data : b->vbo.data;
- b->drawSets << DrawSet(0, zData - vertexData, drawSetIndices);
- while (e) {
- verticesInSet += e->node->geometry()->vertexCount();
- if (verticesInSet > 0xffff) {
- b->drawSets.last().indexCount = indicesInSet;
- if (g->drawingMode() == GL_TRIANGLE_STRIP) {
- b->drawSets.last().indices += 1 * sizeof(quint16);
- b->drawSets.last().indexCount -= 2;
- }
- drawSetIndices = indexData - indexBase;
- b->drawSets << DrawSet(vertexData - b->vbo.data,
- zData - b->vbo.data,
- drawSetIndices);
- iOffset = 0;
- verticesInSet = e->node->geometry()->vertexCount();
- indicesInSet = 0;
+ if (b->merged) {
+ char *vertexData = b->vbo.data;
+ char *zData = vertexData + b->vertexCount * g->sizeOfVertex();
+ char *indexData = separateIndexBuffer
+ ? b->ibo.data
+ : zData + (int(m_useDepthBuffer) * b->vertexCount * sizeof(float));
+
+ quint16 iOffset16 = 0;
+ quint32 iOffset32 = 0;
+ e = b->first;
+ uint verticesInSet = 0;
+ // Start a new set already after 65534 vertices because 0xFFFF may be
+ // used for an always-on primitive restart with some apis (adapt for
+ // uint32 indices as appropriate).
+ 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;
+ b->drawSets << DrawSet(0, zData - vertexData, drawSetIndices);
+ while (e) {
+ verticesInSet += e->node->geometry()->vertexCount();
+ if (verticesInSet > verticesInSetLimit) {
+ b->drawSets.last().indexCount = indicesInSet;
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip) {
+ b->drawSets.last().indices += 1 * mergedIndexElemSize();
+ b->drawSets.last().indexCount -= 2;
}
- uploadMergedElement(e, b->positionAttribute, &vertexData, &zData, &indexData, &iOffset, &indicesInSet);
- e = e->nextInBatch;
- }
- b->drawSets.last().indexCount = indicesInSet;
- // We skip the very first and very last degenerate triangles since they aren't needed
- // and the first one would reverse the vertex ordering of the merged strips.
- if (g->drawingMode() == GL_TRIANGLE_STRIP) {
- b->drawSets.last().indices += 1 * sizeof(quint16);
- b->drawSets.last().indexCount -= 2;
+ drawSetIndices = indexData - indexBase;
+ b->drawSets << DrawSet(vertexData - b->vbo.data,
+ zData - b->vbo.data,
+ drawSetIndices);
+ iOffset16 = 0;
+ iOffset32 = 0;
+ verticesInSet = e->node->geometry()->vertexCount();
+ indicesInSet = 0;
}
- } else {
- char *vboData = b->vbo.data;
- char *iboData = separateIndexBuffer ? b->ibo.data
- : vboData + b->vertexCount * g->sizeOfVertex();
- Element *e = b->first;
- while (e) {
- QSGGeometry *g = e->node->geometry();
- int vbs = g->vertexCount() * g->sizeOfVertex();
- memcpy(vboData, g->vertexData(), vbs);
- vboData = vboData + vbs;
- if (g->indexCount()) {
+ void *iBasePtr = &iOffset16;
+ if (m_uint32IndexForRhi)
+ iBasePtr = &iOffset32;
+ uploadMergedElement(e, b->positionAttribute, &vertexData, &zData, &indexData, iBasePtr, &indicesInSet);
+ e = e->nextInBatch;
+ }
+ b->drawSets.last().indexCount = indicesInSet;
+ // We skip the very first and very last degenerate triangles since they aren't needed
+ // and the first one would reverse the vertex ordering of the merged strips.
+ if (g->drawingMode() == QSGGeometry::DrawTriangleStrip) {
+ b->drawSets.last().indices += 1 * mergedIndexElemSize();
+ b->drawSets.last().indexCount -= 2;
+ }
+ } else {
+ char *vboData = b->vbo.data;
+ char *iboData = separateIndexBuffer ? b->ibo.data
+ : vboData + b->vertexCount * g->sizeOfVertex();
+ Element *e = b->first;
+ while (e) {
+ QSGGeometry *g = e->node->geometry();
+ int vbs = g->vertexCount() * g->sizeOfVertex();
+ memcpy(vboData, g->vertexData(), vbs);
+ vboData = vboData + vbs;
+ const int indexCount = g->indexCount();
+ if (indexCount) {
+ if (!m_rhi) {
int ibs = g->indexCount() * g->sizeOfIndex();
memcpy(iboData, g->indexData(), ibs);
iboData += ibs;
+ } else {
+ const int effectiveIndexSize = m_uint32IndexForRhi ? sizeof(quint32) : g->sizeOfIndex();
+ const int ibs = indexCount * effectiveIndexSize;
+ if (g->sizeOfIndex() == effectiveIndexSize) {
+ memcpy(iboData, g->indexData(), ibs);
+ } else {
+ if (g->sizeOfIndex() == sizeof(quint16) && effectiveIndexSize == sizeof(quint32)) {
+ quint16 *src = g->indexDataAsUShort();
+ quint32 *dst = (quint32 *) iboData;
+ for (int i = 0; i < indexCount; ++i)
+ dst[i] = src[i];
+ } else {
+ Q_ASSERT_X(false, "uploadBatch (unmerged)", "uint index with ushort effective index - cannot happen");
+ }
+ }
+ iboData += ibs;
}
- e = e->nextInBatch;
}
+ e = e->nextInBatch;
}
+ }
#ifndef QT_NO_DEBUG_OUTPUT
- if (Q_UNLIKELY(debug_upload())) {
- const char *vd = b->vbo.data;
- qDebug() << " -- Vertex Data, count:" << b->vertexCount << " - " << g->sizeOfVertex() << "bytes/vertex";
- for (int i=0; i<b->vertexCount; ++i) {
- QDebug dump = qDebug().nospace();
- dump << " --- " << i << ": ";
- int offset = 0;
- for (int a=0; a<g->attributeCount(); ++a) {
- const QSGGeometry::Attribute &attr = g->attributes()[a];
- dump << attr.position << ":(" << attr.tupleSize << ",";
- if (attr.type == GL_FLOAT) {
- dump << "float ";
- if (attr.isVertexCoordinate)
- dump << "* ";
- for (int t=0; t<attr.tupleSize; ++t)
- dump << *(const float *)(vd + offset + t * sizeof(float)) << " ";
- } else if (attr.type == GL_UNSIGNED_BYTE) {
- dump << "ubyte ";
- for (int t=0; t<attr.tupleSize; ++t)
- dump << *(const unsigned char *)(vd + offset + t * sizeof(unsigned char)) << " ";
- }
- dump << ") ";
- offset += attr.tupleSize * size_of_type(attr.type);
+ if (Q_UNLIKELY(debug_upload())) {
+ const char *vd = b->vbo.data;
+ qDebug() << " -- Vertex Data, count:" << b->vertexCount << " - " << g->sizeOfVertex() << "bytes/vertex";
+ for (int i=0; i<b->vertexCount; ++i) {
+ QDebug dump = qDebug().nospace();
+ dump << " --- " << i << ": ";
+ int offset = 0;
+ for (int a=0; a<g->attributeCount(); ++a) {
+ const QSGGeometry::Attribute &attr = g->attributes()[a];
+ dump << attr.position << ":(" << attr.tupleSize << ",";
+ if (attr.type == QSGGeometry::FloatType) {
+ dump << "float ";
+ if (attr.isVertexCoordinate)
+ dump << "* ";
+ for (int t=0; t<attr.tupleSize; ++t)
+ dump << *(const float *)(vd + offset + t * sizeof(float)) << " ";
+ } else if (attr.type == QSGGeometry::UnsignedByteType) {
+ dump << "ubyte ";
+ for (int t=0; t<attr.tupleSize; ++t)
+ dump << *(const unsigned char *)(vd + offset + t * sizeof(unsigned char)) << " ";
}
- if (b->merged && m_useDepthBuffer) {
- float zorder = ((float*)(b->vbo.data + b->vertexCount * g->sizeOfVertex()))[i];
- dump << " Z:(" << zorder << ")";
- }
- vd += g->sizeOfVertex();
+ dump << ") ";
+ offset += attr.tupleSize * size_of_type(attr.type);
+ }
+ if (b->merged && m_useDepthBuffer) {
+ float zorder = ((float*)(b->vbo.data + b->vertexCount * g->sizeOfVertex()))[i];
+ dump << " Z:(" << zorder << ")";
}
+ vd += g->sizeOfVertex();
+ }
- if (!b->drawSets.isEmpty()) {
+ if (!b->drawSets.isEmpty()) {
+ if (m_uint32IndexForRhi) {
+ const quint32 *id = (const quint32 *)(separateIndexBuffer
+ ? b->ibo.data
+ : b->vbo.data + b->drawSets.at(0).indices);
+ {
+ QDebug iDump = qDebug();
+ iDump << " -- Index Data, count:" << b->indexCount;
+ for (int i=0; i<b->indexCount; ++i) {
+ if ((i % 24) == 0)
+ iDump << endl << " --- ";
+ iDump << id[i];
+ }
+ }
+ } else {
const quint16 *id = (const quint16 *)(separateIndexBuffer
? b->ibo.data
: b->vbo.data + b->drawSets.at(0).indices);
@@ -1971,29 +2352,30 @@ void Renderer::uploadBatch(Batch *b)
iDump << " -- Index Data, count:" << b->indexCount;
for (int i=0; i<b->indexCount; ++i) {
if ((i % 24) == 0)
- iDump << endl << " --- ";
+ iDump << endl << " --- ";
iDump << id[i];
}
}
+ }
- for (int i=0; i<b->drawSets.size(); ++i) {
- const DrawSet &s = b->drawSets.at(i);
- qDebug() << " -- DrawSet: indexCount:" << s.indexCount << " vertices:" << s.vertices << " z:" << s.zorders << " indices:" << s.indices;
- }
+ for (int i=0; i<b->drawSets.size(); ++i) {
+ const DrawSet &s = b->drawSets.at(i);
+ qDebug() << " -- DrawSet: indexCount:" << s.indexCount << " vertices:" << s.vertices << " z:" << s.zorders << " indices:" << s.indices;
}
}
+ }
#endif // QT_NO_DEBUG_OUTPUT
- unmap(&b->vbo);
- if (separateIndexBuffer)
- unmap(&b->ibo, true);
+ unmap(&b->vbo);
+ if (separateIndexBuffer)
+ unmap(&b->ibo, true);
- if (Q_UNLIKELY(debug_upload())) qDebug() << " --- vertex/index buffers unmapped, batch upload completed...";
+ if (Q_UNLIKELY(debug_upload())) qDebug() << " --- vertex/index buffers unmapped, batch upload completed...";
- b->needsUpload = false;
+ b->needsUpload = false;
- if (Q_UNLIKELY(debug_render()))
- b->uploadedThisFrame = true;
+ if (Q_UNLIKELY(debug_render()))
+ b->uploadedThisFrame = true;
}
/*!
@@ -2002,15 +2384,15 @@ void Renderer::uploadBatch(Batch *b)
* If the clip is a pixel aligned rectangle, this function will use glScissor instead
* of stencil.
*/
-Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
+ClipState::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
{
if (!clip) {
glDisable(GL_STENCIL_TEST);
glDisable(GL_SCISSOR_TEST);
- return NoClip;
+ return ClipState::NoClip;
}
- ClipType clipType = NoClip;
+ ClipState::ClipType clipType = ClipState::NoClip;
GLuint vbo = 0;
int vboSize = 0;
@@ -2067,17 +2449,17 @@ Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
GLint ix2 = qRound((fx2 + 1) * deviceRect.width() * qreal(0.5));
GLint iy2 = qRound((fy2 + 1) * deviceRect.height() * qreal(0.5));
- if (!(clipType & ScissorClip)) {
+ if (!(clipType & ClipState::ScissorClip)) {
m_currentScissorRect = QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
glEnable(GL_SCISSOR_TEST);
- clipType |= ScissorClip;
+ clipType |= ClipState::ScissorClip;
} else {
m_currentScissorRect &= QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
}
glScissor(m_currentScissorRect.x(), m_currentScissorRect.y(),
m_currentScissorRect.width(), m_currentScissorRect.height());
} else {
- if (!(clipType & StencilClip)) {
+ if (!(clipType & ClipState::StencilClip)) {
if (!m_clipProgram.isLinked()) {
QSGShaderSourceBuilder::initializeProgramFromFiles(
&m_clipProgram,
@@ -2097,7 +2479,7 @@ Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
m_clipProgram.bind();
m_clipProgram.enableAttributeArray(0);
- clipType |= StencilClip;
+ clipType |= ClipState::StencilClip;
}
glStencilFunc(GL_EQUAL, m_currentStencilValue, 0xff); // stencil test, ref, test mask
@@ -2148,7 +2530,7 @@ Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
if (vbo)
glDeleteBuffers(1, &vbo);
- if (clipType & StencilClip) {
+ if (clipType & ClipState::StencilClip) {
m_clipProgram.disableAttributeArray(0);
glStencilFunc(GL_EQUAL, m_currentStencilValue, 0xff); // stencil test, ref, test mask
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // stencil fail, z fail, z pass
@@ -2160,7 +2542,7 @@ Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
return clipType;
}
-void Renderer::updateClip(const QSGClipNode *clipList, const Batch *batch)
+void Renderer::updateClip(const QSGClipNode *clipList, const Batch *batch) // legacy (GL-only)
{
if (clipList != m_currentClip && Q_LIKELY(!debug_noclip())) {
m_currentClip = clipList;
@@ -2174,7 +2556,7 @@ void Renderer::updateClip(const QSGClipNode *clipList, const Batch *batch)
m_currentClipType = updateStencilClip(m_currentClip);
if (batch->isOpaque) {
glEnable(GL_DEPTH_TEST);
- if (m_currentClipType & StencilClip)
+ if (m_currentClipType & ClipState::StencilClip)
glDepthMask(true);
}
}
@@ -2185,13 +2567,14 @@ void Renderer::updateClip(const QSGClipNode *clipList, const Batch *batch)
* which vertex attribute arrays need to be enabled and not. Then update the current
* Shader and current QSGMaterialShader.
*/
-void Renderer::setActiveShader(QSGMaterialShader *program, ShaderManager::Shader *shader)
+void Renderer::setActiveShader(QSGMaterialShader *program, ShaderManager::Shader *shader) // legacy (GL-only)
{
+ Q_ASSERT(!m_rhi);
const char * const *c = m_currentProgram ? m_currentProgram->attributeNames() : nullptr;
const char * const *n = program ? program->attributeNames() : nullptr;
- int cza = m_currentShader ? m_currentShader->pos_order : -1;
- int nza = shader ? shader->pos_order : -1;
+ int cza = m_currentShader ? m_currentShader->programGL.pos_order : -1;
+ int nza = shader ? shader->programGL.pos_order : -1;
int i = 0;
while (c || n) {
@@ -2233,7 +2616,373 @@ void Renderer::setActiveShader(QSGMaterialShader *program, ShaderManager::Shader
}
}
-void Renderer::renderMergedBatch(const Batch *batch)
+void Renderer::applyClipStateToGraphicsState() // RHI only
+{
+ m_gstate.usesScissor = (m_currentClipState.type & ClipState::ScissorClip);
+ m_gstate.stencilTest = (m_currentClipState.type & ClipState::StencilClip);
+}
+
+QRhiGraphicsPipeline *Renderer::buildStencilPipeline(const Batch *batch, bool firstStencilClipInBatch)
+{
+ QRhiGraphicsPipeline *ps = m_rhi->newGraphicsPipeline();
+ ps->setFlags(QRhiGraphicsPipeline::UsesStencilRef);
+ QRhiGraphicsPipeline::TargetBlend blend;
+ blend.colorWrite = 0;
+ ps->setTargetBlends({ blend });
+ ps->setSampleCount(renderTarget()->sampleCount());
+ ps->setStencilTest(true);
+ QRhiGraphicsPipeline::StencilOpState stencilOp;
+ if (firstStencilClipInBatch) {
+ stencilOp.compareOp = QRhiGraphicsPipeline::Always;
+ stencilOp.failOp = QRhiGraphicsPipeline::Keep;
+ stencilOp.depthFailOp = QRhiGraphicsPipeline::Keep;
+ stencilOp.passOp = QRhiGraphicsPipeline::Replace;
+ } else {
+ stencilOp.compareOp = QRhiGraphicsPipeline::Equal;
+ stencilOp.failOp = QRhiGraphicsPipeline::Keep;
+ stencilOp.depthFailOp = QRhiGraphicsPipeline::Keep;
+ stencilOp.passOp = QRhiGraphicsPipeline::IncrementAndClamp;
+ }
+ ps->setStencilFront(stencilOp);
+ ps->setStencilBack(stencilOp);
+
+ ps->setTopology(m_stencilClipCommon.topology);
+
+ ps->setShaderStages({ QRhiGraphicsShaderStage(QRhiGraphicsShaderStage::Vertex, m_stencilClipCommon.vs),
+ QRhiGraphicsShaderStage(QRhiGraphicsShaderStage::Fragment, m_stencilClipCommon.fs) });
+ ps->setVertexInputLayout(m_stencilClipCommon.inputLayout);
+ ps->setShaderResourceBindings(batch->stencilClipState.srb); // use something, it just needs to be layout-compatible
+ ps->setRenderPassDescriptor(renderPassDescriptor());
+
+ if (!ps->build()) {
+ qWarning("Failed to build stencil clip pipeline");
+ delete ps;
+ return nullptr;
+ }
+
+ return ps;
+}
+
+void Renderer::updateClipState(const QSGClipNode *clipList, Batch *batch) // RHI only
+{
+ // Note: No use of the clip-related speparate m_current* vars is allowed
+ // here. All stored in batch->clipState instead. To collect state during
+ // the prepare steps, m_currentClipState is used. It should not be used in
+ // the render steps afterwards.
+
+ // The stenciling logic is slightly different from the legacy GL path as we
+ // cannot just randomly clear the stencil buffer. We now put all clip
+ // shapes into the stencil buffer for all batches in the frame. This means
+ // that the number of total clips in a scene is reduced (since the stencil
+ // value cannot exceed 255) but we do not need any clears inbetween.
+
+ Q_ASSERT(m_rhi);
+ batch->stencilClipState.updateStencilBuffer = false;
+ if (clipList == m_currentClipState.clipList || Q_UNLIKELY(debug_noclip())) {
+ applyClipStateToGraphicsState();
+ batch->clipState = m_currentClipState;
+ return;
+ }
+
+ ClipState::ClipType clipType = ClipState::NoClip;
+ QRect scissorRect;
+ QVarLengthArray<const QSGClipNode *, 4> stencilClipNodes;
+ const QSGClipNode *clip = clipList;
+
+ batch->stencilClipState.drawCalls.reset();
+ int totalVSize = 0;
+ int totalISize = 0;
+ int totalUSize = 0;
+ const int StencilClipUbufSize = 64;
+
+ while (clip) {
+ QMatrix4x4 m = m_current_projection_matrix_native_ndc;
+ if (clip->matrix())
+ m *= *clip->matrix();
+
+ bool isRectangleWithNoPerspective = clip->isRectangular()
+ && qFuzzyIsNull(m(3, 0)) && qFuzzyIsNull(m(3, 1));
+ bool noRotate = qFuzzyIsNull(m(0, 1)) && qFuzzyIsNull(m(1, 0));
+ bool isRotate90 = qFuzzyIsNull(m(0, 0)) && qFuzzyIsNull(m(1, 1));
+
+ if (isRectangleWithNoPerspective && (noRotate || isRotate90)) {
+ QRectF bbox = clip->clipRect();
+ qreal invW = 1 / m(3, 3);
+ qreal fx1, fy1, fx2, fy2;
+ if (noRotate) {
+ fx1 = (bbox.left() * m(0, 0) + m(0, 3)) * invW;
+ fy1 = (bbox.bottom() * m(1, 1) + m(1, 3)) * invW;
+ fx2 = (bbox.right() * m(0, 0) + m(0, 3)) * invW;
+ fy2 = (bbox.top() * m(1, 1) + m(1, 3)) * invW;
+ } else {
+ Q_ASSERT(isRotate90);
+ fx1 = (bbox.bottom() * m(0, 1) + m(0, 3)) * invW;
+ fy1 = (bbox.left() * m(1, 0) + m(1, 3)) * invW;
+ fx2 = (bbox.top() * m(0, 1) + m(0, 3)) * invW;
+ fy2 = (bbox.right() * m(1, 0) + m(1, 3)) * invW;
+ }
+
+ if (fx1 > fx2)
+ qSwap(fx1, fx2);
+ if (fy1 > fy2)
+ qSwap(fy1, fy2);
+
+ QRect deviceRect = this->deviceRect();
+
+ GLint ix1 = qRound((fx1 + 1) * deviceRect.width() * qreal(0.5));
+ GLint iy1 = qRound((fy1 + 1) * deviceRect.height() * qreal(0.5));
+ GLint ix2 = qRound((fx2 + 1) * deviceRect.width() * qreal(0.5));
+ GLint iy2 = qRound((fy2 + 1) * deviceRect.height() * qreal(0.5));
+
+ if (!(clipType & ClipState::ScissorClip)) {
+ clipType |= ClipState::ScissorClip;
+ scissorRect = QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
+ } else {
+ scissorRect &= QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
+ }
+ } else {
+ clipType |= ClipState::StencilClip;
+
+ const QSGGeometry *g = clip->geometry();
+ Q_ASSERT(g->attributeCount() > 0);
+
+ const int vertexByteSize = g->sizeOfVertex() * g->vertexCount();
+ // the 4 byte alignment may not actually be needed here
+ totalVSize = aligned(totalVSize, 4) + vertexByteSize;
+ if (g->indexCount()) {
+ const int indexByteSize = g->sizeOfIndex() * g->indexCount();
+ // so no need to worry about NonFourAlignedEffectiveIndexBufferOffset
+ totalISize = aligned(totalISize, 4) + indexByteSize;
+ }
+ // ubuf start offsets must be aligned (typically to 256 bytes)
+ totalUSize = aligned(totalUSize, m_ubufAlignment) + StencilClipUbufSize;
+
+ stencilClipNodes.append(clip);
+ }
+
+ clip = clip->clipList();
+ }
+
+ if (clipType & ClipState::StencilClip) {
+ bool rebuildVBuf = false;
+ if (!batch->stencilClipState.vbuf) {
+ batch->stencilClipState.vbuf = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, totalVSize);
+ rebuildVBuf = true;
+ } else if (batch->stencilClipState.vbuf->size() < totalVSize) {
+ batch->stencilClipState.vbuf->setSize(totalVSize);
+ rebuildVBuf = true;
+ }
+ if (rebuildVBuf) {
+ if (!batch->stencilClipState.vbuf->build()) {
+ qWarning("Failed to build stencil clip vertex buffer");
+ delete batch->stencilClipState.vbuf;
+ batch->stencilClipState.vbuf = nullptr;
+ return;
+ }
+ }
+
+ if (totalISize) {
+ bool rebuildIBuf = false;
+ if (!batch->stencilClipState.ibuf) {
+ batch->stencilClipState.ibuf = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::IndexBuffer, totalISize);
+ rebuildIBuf = true;
+ } else if (batch->stencilClipState.ibuf->size() < totalISize) {
+ batch->stencilClipState.ibuf->setSize(totalISize);
+ rebuildIBuf = true;
+ }
+ if (rebuildIBuf) {
+ if (!batch->stencilClipState.ibuf->build()) {
+ qWarning("Failed to build stencil clip index buffer");
+ delete batch->stencilClipState.ibuf;
+ batch->stencilClipState.ibuf = nullptr;
+ return;
+ }
+ }
+ }
+
+ bool rebuildUBuf = false;
+ if (!batch->stencilClipState.ubuf) {
+ batch->stencilClipState.ubuf = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, totalUSize);
+ rebuildUBuf = true;
+ } else if (batch->stencilClipState.ubuf->size() < totalUSize) {
+ batch->stencilClipState.ubuf->setSize(totalUSize);
+ rebuildUBuf = true;
+ }
+ if (rebuildUBuf) {
+ if (!batch->stencilClipState.ubuf->build()) {
+ qWarning("Failed to build stencil clip uniform buffer");
+ delete batch->stencilClipState.ubuf;
+ batch->stencilClipState.ubuf = nullptr;
+ return;
+ }
+ }
+
+ if (!batch->stencilClipState.srb) {
+ batch->stencilClipState.srb = m_rhi->newShaderResourceBindings();
+ const QRhiShaderResourceBinding ubufBinding = QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(
+ 0, QRhiShaderResourceBinding::VertexStage, batch->stencilClipState.ubuf, StencilClipUbufSize);
+ batch->stencilClipState.srb->setBindings({ ubufBinding });
+ if (!batch->stencilClipState.srb->build()) {
+ qWarning("Failed to build stencil clip srb");
+ delete batch->stencilClipState.srb;
+ batch->stencilClipState.srb = nullptr;
+ return;
+ }
+ }
+
+ int vOffset = 0;
+ int iOffset = 0;
+ int uOffset = 0;
+ for (const QSGClipNode *clip : stencilClipNodes) {
+ const QSGGeometry *g = clip->geometry();
+ const QSGGeometry::Attribute *a = g->attributes();
+ StencilClipState::StencilDrawCall drawCall;
+ const bool firstStencilClipInBatch = batch->stencilClipState.drawCalls.isEmpty();
+
+ if (firstStencilClipInBatch) {
+ m_stencilClipCommon.inputLayout.setBindings({ QRhiVertexInputBinding(g->sizeOfVertex()) });
+ m_stencilClipCommon.inputLayout.setAttributes({ QRhiVertexInputAttribute(0, 0, qsg_vertexInputFormat(*a), 0) });
+ m_stencilClipCommon.topology = qsg_topology(g->drawingMode());
+ }
+#ifndef QT_NO_DEBUG
+ else {
+ if (qsg_topology(g->drawingMode()) != m_stencilClipCommon.topology)
+ qWarning("updateClipState: Clip list entries have different primitive topologies, this is not currently supported.");
+ if (qsg_vertexInputFormat(*a) != m_stencilClipCommon.inputLayout.cbeginAttributes()->format())
+ qWarning("updateClipState: Clip list entries have different vertex input layouts, this is must not happen.");
+ }
+#endif
+
+ drawCall.vbufOffset = aligned(vOffset, 4);
+ const int vertexByteSize = g->sizeOfVertex() * g->vertexCount();
+ vOffset = drawCall.vbufOffset + vertexByteSize;
+
+ int indexByteSize = 0;
+ if (g->indexCount()) {
+ drawCall.ibufOffset = aligned(iOffset, 4);
+ indexByteSize = g->sizeOfIndex() * g->indexCount();
+ iOffset = drawCall.ibufOffset + indexByteSize;
+ }
+
+ drawCall.ubufOffset = aligned(uOffset, m_ubufAlignment);
+ uOffset = drawCall.ubufOffset + StencilClipUbufSize;
+
+ QMatrix4x4 matrixYUpNDC = m_current_projection_matrix;
+ if (clip->matrix())
+ matrixYUpNDC *= *clip->matrix();
+
+ m_resourceUpdates->updateDynamicBuffer(batch->stencilClipState.ubuf, drawCall.ubufOffset, 64, matrixYUpNDC.constData());
+ m_resourceUpdates->updateDynamicBuffer(batch->stencilClipState.vbuf, drawCall.vbufOffset, vertexByteSize, g->vertexData());
+ if (indexByteSize)
+ m_resourceUpdates->updateDynamicBuffer(batch->stencilClipState.ibuf, drawCall.ibufOffset, indexByteSize, g->indexData());
+
+ // stencil ref goes 1, 1, 2, 3, 4, ..., N for the clips in the first batch,
+ // then N+1, N+1, N+2, N+3, ... for the next batch,
+ // and so on.
+ // Note the different stencilOp for the first and the subsequent clips.
+ drawCall.stencilRef = firstStencilClipInBatch ? m_currentClipState.stencilRef + 1 : m_currentClipState.stencilRef;
+ m_currentClipState.stencilRef += 1;
+
+ drawCall.vertexCount = g->vertexCount();
+ drawCall.indexCount = g->indexCount();
+ drawCall.indexFormat = qsg_indexFormat(g);
+ batch->stencilClipState.drawCalls.add(drawCall);
+ }
+
+ if (!m_stencilClipCommon.vs.isValid())
+ m_stencilClipCommon.vs = QSGMaterialRhiShaderPrivate::loadShader(QLatin1String(":/qt-project.org/scenegraph/shaders_ng/stencilclip.vert.qsb"));
+
+ if (!m_stencilClipCommon.fs.isValid())
+ m_stencilClipCommon.fs = QSGMaterialRhiShaderPrivate::loadShader(QLatin1String(":/qt-project.org/scenegraph/shaders_ng/stencilclip.frag.qsb"));
+
+ if (!m_stencilClipCommon.replacePs)
+ m_stencilClipCommon.replacePs = buildStencilPipeline(batch, true);
+
+ if (!m_stencilClipCommon.incrPs)
+ m_stencilClipCommon.incrPs = buildStencilPipeline(batch, false);
+
+ batch->stencilClipState.updateStencilBuffer = true;
+ }
+
+ m_currentClipState.clipList = clipList;
+ m_currentClipState.type = clipType;
+ m_currentClipState.scissor = QRhiScissor(scissorRect.x(), scissorRect.y(),
+ scissorRect.width(), scissorRect.height());
+
+ applyClipStateToGraphicsState();
+ batch->clipState = m_currentClipState;
+}
+
+void Renderer::enqueueStencilDraw(const Batch *batch) // RHI only
+{
+ // cliptype stencil + updateStencilBuffer==false means the batch uses
+ // stenciling but relies on the stencil data generated by a previous batch
+ // (due to the having the same clip node). Do not enqueue draw calls for
+ // stencil in this case as the stencil buffer is already up-to-date.
+ if (!batch->stencilClipState.updateStencilBuffer)
+ return;
+
+ QRhiCommandBuffer *cb = commandBuffer();
+ const int count = batch->stencilClipState.drawCalls.size();
+ for (int i = 0; i < count; ++i) {
+ const StencilClipState::StencilDrawCall &drawCall(batch->stencilClipState.drawCalls.at(i));
+ QRhiShaderResourceBindings *srb = batch->stencilClipState.srb;
+ QRhiCommandBuffer::DynamicOffset ubufOffset(0, drawCall.ubufOffset);
+ if (i == 0) {
+ cb->setGraphicsPipeline(m_stencilClipCommon.replacePs);
+ cb->setViewport(m_pstate.viewport);
+ } else if (i == 1) {
+ cb->setGraphicsPipeline(m_stencilClipCommon.incrPs);
+ cb->setViewport(m_pstate.viewport);
+ }
+ // else incrPs is already bound
+ cb->setShaderResources(srb, 1, &ubufOffset);
+ cb->setStencilRef(drawCall.stencilRef);
+ const QRhiCommandBuffer::VertexInput vbufBinding(batch->stencilClipState.vbuf, drawCall.vbufOffset);
+ if (drawCall.indexCount) {
+ cb->setVertexInput(0, 1, &vbufBinding,
+ batch->stencilClipState.ibuf, drawCall.ibufOffset, drawCall.indexFormat);
+ cb->drawIndexed(drawCall.indexCount);
+ } else {
+ cb->setVertexInput(0, 1, &vbufBinding);
+ cb->draw(drawCall.vertexCount);
+ }
+ }
+}
+
+void Renderer::setActiveRhiShader(QSGMaterialRhiShader *program, ShaderManager::Shader *shader) // RHI only
+{
+ Q_ASSERT(m_rhi);
+ m_currentRhiProgram = program;
+ m_currentShader = shader;
+ m_currentMaterial = nullptr;
+}
+
+void Renderer::updateLineWidth(QSGGeometry *g) // legacy (GL-only)
+{
+ if (g->drawingMode() == GL_LINE_STRIP || g->drawingMode() == GL_LINE_LOOP || g->drawingMode() == GL_LINES)
+ glLineWidth(g->lineWidth());
+#if !defined(QT_OPENGL_ES_2)
+ else {
+ QOpenGLContext *ctx = m_context->openglContext();
+ if (!ctx->isOpenGLES() && g->drawingMode() == GL_POINTS) {
+ QOpenGLFunctions_1_0 *gl1funcs = nullptr;
+ QOpenGLFunctions_3_2_Core *gl3funcs = nullptr;
+ if (ctx->format().profile() == QSurfaceFormat::CoreProfile)
+ gl3funcs = ctx->versionFunctions<QOpenGLFunctions_3_2_Core>();
+ else
+ gl1funcs = ctx->versionFunctions<QOpenGLFunctions_1_0>();
+ Q_ASSERT(gl1funcs || gl3funcs);
+ if (gl1funcs)
+ gl1funcs->glPointSize(g->lineWidth());
+ else
+ gl3funcs->glPointSize(g->lineWidth());
+ }
+ }
+#endif
+}
+
+void Renderer::renderMergedBatch(const Batch *batch) // legacy (GL-only)
{
if (batch->vertexCount == 0 || batch->indexCount == 0)
return;
@@ -2289,13 +3038,14 @@ void Renderer::renderMergedBatch(const Batch *batch)
QSGMaterial *material = gn->activeMaterial();
- ShaderManager::Shader *sms = m_useDepthBuffer ? m_shaderManager->prepareMaterial(material) : m_shaderManager->prepareMaterialNoRewrite(material);
+ ShaderManager::Shader *sms = m_useDepthBuffer ? m_shaderManager->prepareMaterial(material)
+ : m_shaderManager->prepareMaterialNoRewrite(material);
if (!sms)
return;
- QSGMaterialShader *program = sms->program;
+ Q_ASSERT(sms->programGL.program);
if (m_currentShader != sms)
- setActiveShader(program, sms);
+ setActiveShader(sms->programGL.program, sms);
m_current_opacity = gn->inheritedOpacity();
if (!qFuzzyCompare(sms->lastOpacity, float(m_current_opacity))) {
@@ -2303,7 +3053,7 @@ void Renderer::renderMergedBatch(const Batch *batch)
sms->lastOpacity = m_current_opacity;
}
- program->updateState(state(dirty), material, m_currentMaterial);
+ sms->programGL.program->updateState(state(dirty), material, m_currentMaterial);
#ifndef QT_NO_DEBUG
if (qsg_test_and_clear_material_failure()) {
@@ -2320,9 +3070,9 @@ void Renderer::renderMergedBatch(const Batch *batch)
m_currentMaterial = material;
- QSGGeometry* g = gn->geometry();
+ QSGGeometry *g = gn->geometry();
updateLineWidth(g);
- char const *const *attrNames = program->attributeNames();
+ char const *const *attrNames = sms->programGL.program->attributeNames();
for (int i=0; i<batch->drawSets.size(); ++i) {
const DrawSet &draw = batch->drawSets.at(i);
int offset = 0;
@@ -2335,13 +3085,13 @@ void Renderer::renderMergedBatch(const Batch *batch)
offset += a.tupleSize * size_of_type(a.type);
}
if (m_useDepthBuffer)
- glVertexAttribPointer(sms->pos_order, 1, GL_FLOAT, false, 0, (void *) (qintptr) (draw.zorders));
+ glVertexAttribPointer(sms->programGL.pos_order, 1, GL_FLOAT, false, 0, (void *) (qintptr) (draw.zorders));
glDrawElements(g->drawingMode(), draw.indexCount, GL_UNSIGNED_SHORT, (void *) (qintptr) (indexBase + draw.indices));
}
}
-void Renderer::renderUnmergedBatch(const Batch *batch)
+void Renderer::renderUnmergedBatch(const Batch *batch) // legacy (GL-only)
{
if (batch->vertexCount == 0)
return;
@@ -2371,7 +3121,7 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
glBindBuffer(GL_ARRAY_BUFFER, batch->vbo.id);
char *indexBase = nullptr;
- const auto separateIndexBuffer = m_context->separateIndexBuffer();
+ const bool separateIndexBuffer = m_context->separateIndexBuffer();
const Buffer *indexBuf = separateIndexBuffer ? &batch->ibo : &batch->vbo;
if (batch->indexCount) {
if (m_context->hasBrokenIndexBufferObjects()) {
@@ -2389,10 +3139,10 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
ShaderManager::Shader *sms = m_shaderManager->prepareMaterialNoRewrite(material);
if (!sms)
return;
- QSGMaterialShader *program = sms->program;
- if (sms != m_currentShader)
- setActiveShader(program, sms);
+ Q_ASSERT(sms->programGL.program);
+ if (m_currentShader != sms)
+ setActiveShader(sms->programGL.program, sms);
m_current_opacity = gn->inheritedOpacity();
if (sms->lastOpacity != m_current_opacity) {
@@ -2419,7 +3169,7 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
m_current_projection_matrix(2, 3) = 1.0f - e->order * m_zRange;
}
- program->updateState(state(dirty), material, m_currentMaterial);
+ sms->programGL.program->updateState(state(dirty), material, m_currentMaterial);
#ifndef QT_NO_DEBUG
if (qsg_test_and_clear_material_failure()) {
@@ -2435,8 +3185,8 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
// are all identical (compare==0) since they are in the same batch.
m_currentMaterial = material;
- QSGGeometry* g = gn->geometry();
- char const *const *attrNames = program->attributeNames();
+ QSGGeometry *g = gn->geometry();
+ char const *const *attrNames = sms->programGL.program->attributeNames();
int offset = 0;
for (int j = 0; attrNames[j]; ++j) {
if (!*attrNames[j])
@@ -2463,101 +3213,933 @@ void Renderer::renderUnmergedBatch(const Batch *batch)
}
}
-void Renderer::updateLineWidth(QSGGeometry *g)
+static inline bool needsBlendConstant(QRhiGraphicsPipeline::BlendFactor f)
{
- if (g->drawingMode() == GL_LINE_STRIP || g->drawingMode() == GL_LINE_LOOP || g->drawingMode() == GL_LINES)
- glLineWidth(g->lineWidth());
-#if !defined(QT_OPENGL_ES_2)
- else {
- QOpenGLContext *ctx = m_context->openglContext();
- if (!ctx->isOpenGLES() && g->drawingMode() == GL_POINTS) {
- QOpenGLFunctions_1_0 *gl1funcs = nullptr;
- QOpenGLFunctions_3_2_Core *gl3funcs = nullptr;
- if (ctx->format().profile() == QSurfaceFormat::CoreProfile)
- gl3funcs = ctx->versionFunctions<QOpenGLFunctions_3_2_Core>();
- else
- gl1funcs = ctx->versionFunctions<QOpenGLFunctions_1_0>();
- Q_ASSERT(gl1funcs || gl3funcs);
- if (gl1funcs)
- gl1funcs->glPointSize(g->lineWidth());
- else
- gl3funcs->glPointSize(g->lineWidth());
+ return f == QRhiGraphicsPipeline::ConstantColor
+ || f == QRhiGraphicsPipeline::OneMinusConstantColor
+ || f == QRhiGraphicsPipeline::ConstantAlpha
+ || f == QRhiGraphicsPipeline::OneMinusConstantAlpha;
+}
+
+// With QRhi renderBatches() is split to two steps: prepare and render.
+//
+// Prepare goes through the batches and elements, and set up a graphics
+// pipeline, srb, uniform buffer, calculates clipping, based on m_gstate, the
+// material (shaders), and the batches. This step does not touch the command
+// buffer or renderpass-related state (m_pstate).
+//
+// The render step then starts a renderpass, and goes through all
+// batches/elements again and records setGraphicsPipeline, drawIndexed, etc. on
+// the command buffer. The prepare step's accumulated global state like
+// m_gstate must not be used here. Rather, all data needed for rendering is
+// available from Batch/Element at this stage. Bookkeeping of state in the
+// renderpass is done via m_pstate.
+
+bool Renderer::ensurePipelineState(Element *e, const ShaderManager::Shader *sms) // RHI only, [prepare step]
+{
+ // In unmerged batches the srbs in the elements are all compatible layout-wise.
+ const GraphicsPipelineStateKey k { m_gstate, sms, renderPassDescriptor(), e->srb };
+
+ // Note: dynamic state (viewport rect, scissor rect, stencil ref, blend
+ // constant) is never a part of GraphicsState/QRhiGraphicsPipeline.
+
+ // See if there is an existing, matching pipeline state object.
+ auto it = m_pipelines.constFind(k);
+ if (it != m_pipelines.constEnd()) {
+ e->ps = *it;
+ return true;
+ }
+
+ // Build a new one. This is potentially expensive.
+ QRhiGraphicsPipeline *ps = m_rhi->newGraphicsPipeline();
+ ps->setShaderStages(sms->programRhi.shaderStages.cbegin(), sms->programRhi.shaderStages.cend());
+ ps->setVertexInputLayout(sms->programRhi.inputLayout);
+ ps->setShaderResourceBindings(e->srb);
+ ps->setRenderPassDescriptor(renderPassDescriptor());
+
+ QRhiGraphicsPipeline::Flags flags = 0;
+ if (needsBlendConstant(m_gstate.srcColor) || needsBlendConstant(m_gstate.dstColor))
+ flags |= QRhiGraphicsPipeline::UsesBlendConstants;
+ if (m_gstate.usesScissor)
+ flags |= QRhiGraphicsPipeline::UsesScissor;
+ if (m_gstate.stencilTest)
+ flags |= QRhiGraphicsPipeline::UsesStencilRef;
+
+ ps->setFlags(flags);
+ ps->setTopology(qsg_topology(m_gstate.drawMode));
+ ps->setCullMode(m_gstate.cullMode);
+
+ QRhiGraphicsPipeline::TargetBlend blend;
+ blend.colorWrite = m_gstate.colorWrite;
+ blend.enable = m_gstate.blending;
+ blend.srcColor = m_gstate.srcColor;
+ blend.dstColor = m_gstate.dstColor;
+ ps->setTargetBlends({ blend });
+
+ ps->setDepthTest(m_gstate.depthTest);
+ ps->setDepthWrite(m_gstate.depthWrite);
+ ps->setDepthOp(m_gstate.depthFunc);
+
+ if (m_gstate.stencilTest) {
+ ps->setStencilTest(true);
+ QRhiGraphicsPipeline::StencilOpState stencilOp;
+ stencilOp.compareOp = QRhiGraphicsPipeline::Equal;
+ stencilOp.failOp = QRhiGraphicsPipeline::Keep;
+ stencilOp.depthFailOp = QRhiGraphicsPipeline::Keep;
+ stencilOp.passOp = QRhiGraphicsPipeline::Keep;
+ ps->setStencilFront(stencilOp);
+ ps->setStencilBack(stencilOp);
+ }
+
+ ps->setSampleCount(m_gstate.sampleCount);
+
+ ps->setLineWidth(m_gstate.lineWidth);
+
+ //qDebug("building new ps %p", ps);
+ if (!ps->build()) {
+ qWarning("Failed to build graphics pipeline state");
+ delete ps;
+ return false;
+ }
+
+ m_pipelines.insert(k, ps);
+ e->ps = ps;
+ return true;
+}
+
+static QRhiSampler *newSampler(QRhi *rhi, const QSGSamplerDescription &desc)
+{
+ QRhiSampler::Filter magFilter;
+ QRhiSampler::Filter minFilter;
+ QRhiSampler::Filter mipmapMode;
+ QRhiSampler::AddressMode u;
+ QRhiSampler::AddressMode v;
+
+ switch (desc.filtering) {
+ case QSGTexture::None:
+ Q_FALLTHROUGH();
+ case QSGTexture::Nearest:
+ magFilter = minFilter = QRhiSampler::Nearest;
+ break;
+ case QSGTexture::Linear:
+ magFilter = minFilter = QRhiSampler::Linear;
+ break;
+ default:
+ Q_UNREACHABLE();
+ magFilter = minFilter = QRhiSampler::Nearest;
+ break;
+ }
+
+ switch (desc.mipmapFiltering) {
+ case QSGTexture::None:
+ mipmapMode = QRhiSampler::None;
+ break;
+ case QSGTexture::Nearest:
+ mipmapMode = QRhiSampler::Nearest;
+ break;
+ case QSGTexture::Linear:
+ mipmapMode = QRhiSampler::Linear;
+ break;
+ default:
+ Q_UNREACHABLE();
+ mipmapMode = QRhiSampler::None;
+ break;
+ }
+
+ switch (desc.horizontalWrap) {
+ case QSGTexture::Repeat:
+ u = QRhiSampler::Repeat;
+ break;
+ case QSGTexture::ClampToEdge:
+ u = QRhiSampler::ClampToEdge;
+ break;
+ case QSGTexture::MirroredRepeat:
+ u = QRhiSampler::Mirror;
+ break;
+ default:
+ Q_UNREACHABLE();
+ u = QRhiSampler::ClampToEdge;
+ break;
+ }
+
+ switch (desc.verticalWrap) {
+ case QSGTexture::Repeat:
+ v = QRhiSampler::Repeat;
+ break;
+ case QSGTexture::ClampToEdge:
+ v = QRhiSampler::ClampToEdge;
+ break;
+ case QSGTexture::MirroredRepeat:
+ v = QRhiSampler::Mirror;
+ break;
+ default:
+ Q_UNREACHABLE();
+ v = QRhiSampler::ClampToEdge;
+ break;
+ }
+
+ return rhi->newSampler(magFilter, minFilter, mipmapMode, u, v);
+}
+
+QRhiTexture *Renderer::dummyTexture()
+{
+ if (!m_dummyTexture) {
+ m_dummyTexture = m_rhi->newTexture(QRhiTexture::RGBA8, QSize(64, 64));
+ if (m_dummyTexture->build()) {
+ if (m_resourceUpdates) {
+ QImage img(m_dummyTexture->pixelSize(), QImage::Format_RGBA8888_Premultiplied);
+ img.fill(0);
+ m_resourceUpdates->uploadTexture(m_dummyTexture, img);
+ }
+ }
+ }
+ return m_dummyTexture;
+}
+
+static void rendererToMaterialGraphicsState(QSGMaterialRhiShader::GraphicsPipelineState *dst,
+ GraphicsState *src)
+{
+ dst->blendEnable = src->blending;
+
+ // the enum values should match, sanity check it
+ Q_ASSERT(int(QSGMaterialRhiShader::GraphicsPipelineState::OneMinusSrc1Alpha) == int(QRhiGraphicsPipeline::OneMinusSrc1Alpha));
+ Q_ASSERT(int(QSGMaterialRhiShader::GraphicsPipelineState::A) == int(QRhiGraphicsPipeline::A));
+ Q_ASSERT(int(QSGMaterialRhiShader::GraphicsPipelineState::CullBack) == int(QRhiGraphicsPipeline::Back));
+
+ dst->srcColor = QSGMaterialRhiShader::GraphicsPipelineState::BlendFactor(src->srcColor);
+ dst->dstColor = QSGMaterialRhiShader::GraphicsPipelineState::BlendFactor(src->dstColor);
+
+ dst->colorWrite = QSGMaterialRhiShader::GraphicsPipelineState::ColorMask(int(src->colorWrite));
+
+ dst->cullMode = QSGMaterialRhiShader::GraphicsPipelineState::CullMode(src->cullMode);
+}
+
+static void materialToRendererGraphicsState(GraphicsState *dst,
+ QSGMaterialRhiShader::GraphicsPipelineState *src)
+{
+ dst->blending = src->blendEnable;
+ dst->srcColor = QRhiGraphicsPipeline::BlendFactor(src->srcColor);
+ dst->dstColor = QRhiGraphicsPipeline::BlendFactor(src->dstColor);
+ dst->colorWrite = QRhiGraphicsPipeline::ColorMask(int(src->colorWrite));
+ dst->cullMode = QRhiGraphicsPipeline::CullMode(src->cullMode);
+}
+
+void Renderer::updateMaterialDynamicData(ShaderManager::Shader *sms,
+ QSGMaterialRhiShader::RenderState &renderState,
+ QSGMaterial *material,
+ ShaderManager::ShaderResourceBindingList *bindings,
+ const Batch *batch,
+ int ubufOffset,
+ int ubufRegionSize) // RHI only, [prepare step]
+{
+ m_current_resource_update_batch = m_resourceUpdates;
+
+ QSGMaterialRhiShader *shader = sms->programRhi.program;
+ QSGMaterialRhiShaderPrivate *pd = QSGMaterialRhiShaderPrivate::get(shader);
+ if (pd->ubufBinding >= 0) {
+ m_current_uniform_data = &pd->masterUniformData;
+ const bool changed = shader->updateUniformData(renderState, material, m_currentMaterial);
+ m_current_uniform_data = nullptr;
+
+ if (changed || !batch->ubufDataValid)
+ m_resourceUpdates->updateDynamicBuffer(batch->ubuf, ubufOffset, ubufRegionSize, pd->masterUniformData.constData());
+
+ bindings->append(QRhiShaderResourceBinding::uniformBuffer(pd->ubufBinding,
+ pd->ubufStages,
+ batch->ubuf,
+ ubufOffset,
+ ubufRegionSize));
+ }
+
+ for (int binding = 0; binding < QSGMaterialRhiShaderPrivate::MAX_SHADER_RESOURCE_BINDINGS; ++binding) {
+ const QRhiShaderResourceBinding::StageFlags stages = pd->combinedImageSamplerBindings[binding];
+ if (!stages)
+ continue;
+
+ QSGTexture *prevTex = pd->textureBindingTable[binding];
+ QSGTexture *t = prevTex;
+
+ shader->updateSampledImage(renderState, binding, &t, material, m_currentMaterial);
+ if (!t) {
+ qWarning("No QSGTexture provided from updateSampledImage(). This is wrong.");
+ continue;
+ }
+
+ QSGTexturePrivate *td = QSGTexturePrivate::get(t);
+ // prevTex may be invalid at this point, avoid dereferencing it
+ if (t != prevTex || td->hasDirtySamplerOptions()) {
+ // The QSGTexture, and so the sampler parameters, may have changed.
+ // The rhiTexture is not relevant here.
+ td->resetDirtySamplerOptions();
+ pd->textureBindingTable[binding] = t; // does not own
+ pd->samplerBindingTable[binding] = nullptr;
+ if (t->anisotropyLevel() != QSGTexture::AnisotropyNone) // ###
+ qWarning("QSGTexture anisotropy levels are not currently supported");
+
+ const QSGSamplerDescription samplerDesc = QSGSamplerDescription::fromTexture(t);
+ QRhiSampler *sampler = nullptr;
+ auto it = m_samplers.constFind(samplerDesc);
+ if (it != m_samplers.constEnd()) {
+ sampler = *it;
+ Q_ASSERT(sampler);
+ } else {
+ sampler = newSampler(m_rhi, samplerDesc);
+ if (!sampler->build()) {
+ qWarning("Failed to build sampler");
+ delete sampler;
+ continue;
+ }
+ m_samplers.insert(samplerDesc, sampler);
+ }
+ pd->samplerBindingTable[binding] = sampler; // does not own
+ }
+
+ if (pd->textureBindingTable[binding] && pd->samplerBindingTable[binding]) {
+ QRhiTexture *texture = QSGTexturePrivate::get(pd->textureBindingTable[binding])->rhiTexture();
+ // texture may be null if the update above failed for any reason,
+ // or if the QSGTexture chose to return null intentionally. This is
+ // valid and we still need to provide something to the shader.
+ if (!texture)
+ texture = dummyTexture();
+ QRhiSampler *sampler = pd->samplerBindingTable[binding];
+ bindings->append(QRhiShaderResourceBinding::sampledTexture(binding,
+ stages,
+ texture,
+ sampler));
}
}
+
+#ifndef QT_NO_DEBUG
+ if (bindings->isEmpty())
+ qWarning("No shader resources for material %p, this is odd.", material);
#endif
}
-void Renderer::renderBatches()
+void Renderer::updateMaterialStaticData(ShaderManager::Shader *sms,
+ QSGMaterialRhiShader::RenderState &renderState,
+ QSGMaterial *material,
+ Batch *batch,
+ bool *gstateChanged) // RHI only, [prepare step]
+{
+ QSGMaterialRhiShader *shader = sms->programRhi.program;
+ *gstateChanged = false;
+ if (shader->flags().testFlag(QSGMaterialRhiShader::UpdatesGraphicsPipelineState)) {
+ // generate the public mini-state from m_gstate, invoke the material,
+ // write the changes, if any, back to m_gstate, together with a way to
+ // roll those back.
+ QSGMaterialRhiShader::GraphicsPipelineState shaderPs;
+ rendererToMaterialGraphicsState(&shaderPs, &m_gstate);
+ const bool changed = shader->updateGraphicsPipelineState(renderState, &shaderPs, material, m_currentMaterial);
+ if (changed) {
+ m_gstateStack.push(m_gstate);
+ materialToRendererGraphicsState(&m_gstate, &shaderPs);
+ if (needsBlendConstant(m_gstate.srcColor) || needsBlendConstant(m_gstate.dstColor))
+ batch->blendConstant = shaderPs.blendConstant;
+ *gstateChanged = true;
+ }
+ }
+}
+
+bool Renderer::prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *renderBatch) // split prepare-render (RHI only)
{
+ if (batch->vertexCount == 0 || batch->indexCount == 0)
+ return false;
+
+ Element *e = batch->first;
+ Q_ASSERT(e);
+
+#ifndef QT_NO_DEBUG_OUTPUT
if (Q_UNLIKELY(debug_render())) {
- qDebug().nospace() << "Rendering:" << endl
- << " -> Opaque: " << qsg_countNodesInBatches(m_opaqueBatches) << " nodes in " << m_opaqueBatches.size() << " batches..." << endl
- << " -> Alpha: " << qsg_countNodesInBatches(m_alphaBatches) << " nodes in " << m_alphaBatches.size() << " batches...";
+ QDebug debug = qDebug();
+ debug << " -"
+ << batch
+ << (batch->uploadedThisFrame ? "[ upload]" : "[retained]")
+ << (e->node->clipList() ? "[ clip]" : "[noclip]")
+ << (batch->isOpaque ? "[opaque]" : "[ alpha]")
+ << "[ merged]"
+ << " Nodes:" << QString::fromLatin1("%1").arg(qsg_countNodesInBatch(batch), 4).toLatin1().constData()
+ << " Vertices:" << QString::fromLatin1("%1").arg(batch->vertexCount, 5).toLatin1().constData()
+ << " Indices:" << QString::fromLatin1("%1").arg(batch->indexCount, 5).toLatin1().constData()
+ << " root:" << batch->root;
+ if (batch->drawSets.size() > 1)
+ debug << "sets:" << batch->drawSets.size();
+ if (!batch->isOpaque)
+ debug << "opacity:" << e->node->inheritedOpacity();
+ batch->uploadedThisFrame = false;
}
+#endif
- QRect r = viewportRect();
- glViewport(r.x(), deviceRect().bottom() - r.bottom(), r.width(), r.height());
- glClearColor(clearColor().redF(), clearColor().greenF(), clearColor().blueF(), clearColor().alphaF());
+ QSGGeometryNode *gn = e->node;
- if (m_useDepthBuffer) {
- glClearDepthf(1); // calls glClearDepth() under the hood for desktop OpenGL
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_LESS);
- glDepthMask(true);
- glDisable(GL_BLEND);
+ // We always have dirty matrix as all batches are at a unique z range.
+ QSGMaterialShader::RenderState::DirtyStates dirty = QSGMaterialShader::RenderState::DirtyMatrix;
+ if (batch->root)
+ m_current_model_view_matrix = qsg_matrixForRoot(batch->root);
+ else
+ m_current_model_view_matrix.setToIdentity();
+ m_current_determinant = m_current_model_view_matrix.determinant();
+ m_current_projection_matrix = projectionMatrix();
+ m_current_projection_matrix_native_ndc = projectionMatrixWithNativeNDC();
+
+ QSGMaterial *material = gn->activeMaterial();
+ updateClipState(gn->clipList(), batch);
+
+ const QSGGeometry *g = gn->geometry();
+ ShaderManager::Shader *sms = m_useDepthBuffer ? m_shaderManager->prepareMaterial(material, true, g)
+ : m_shaderManager->prepareMaterialNoRewrite(material, true, g);
+ if (!sms)
+ return false;
+
+ Q_ASSERT(sms->programRhi.program);
+ if (m_currentShader != sms)
+ setActiveRhiShader(sms->programRhi.program, sms);
+
+ m_current_opacity = gn->inheritedOpacity();
+ if (!qFuzzyCompare(sms->lastOpacity, float(m_current_opacity))) {
+ dirty |= QSGMaterialShader::RenderState::DirtyOpacity;
+ sms->lastOpacity = m_current_opacity;
+ }
+
+ QSGMaterialRhiShaderPrivate *pd = QSGMaterialRhiShaderPrivate::get(sms->programRhi.program);
+ const int ubufSize = pd->masterUniformData.size();
+ if (pd->ubufBinding >= 0) {
+ bool ubufRebuild = false;
+ if (!batch->ubuf) {
+ batch->ubuf = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufSize);
+ ubufRebuild = true;
+ } else {
+ if (batch->ubuf->size() < ubufSize) {
+ batch->ubuf->setSize(ubufSize);
+ ubufRebuild = true;
+ }
+ }
+ if (ubufRebuild) {
+ batch->ubufDataValid = false;
+ if (!batch->ubuf->build()) {
+ qWarning("Failed to build uniform buffer of size %d bytes", ubufSize);
+ delete batch->ubuf;
+ batch->ubuf = nullptr;
+ return false;
+ }
+ }
+ }
+
+ QSGMaterialRhiShader::RenderState renderState = rhiState(QSGMaterialRhiShader::RenderState::DirtyStates(int(dirty)));
+
+ bool pendingGStatePop = false;
+ updateMaterialStaticData(sms, renderState, material, batch, &pendingGStatePop);
+
+ ShaderManager::ShaderResourceBindingList bindings;
+ updateMaterialDynamicData(sms, renderState, material, &bindings, batch, 0, ubufSize);
+
+#ifndef QT_NO_DEBUG
+ if (qsg_test_and_clear_material_failure()) {
+ qDebug("QSGMaterial::updateState triggered an error (merged), batch will be skipped:");
+ Element *ee = e;
+ while (ee) {
+ qDebug() << " -" << ee->node;
+ ee = ee->nextInBatch;
+ }
+ QSGNodeDumper::dump(rootNode());
+ qFatal("Aborting: scene graph is invalid...");
+ }
+#endif
+
+ e->srb = m_shaderManager->srb(bindings);
+
+ m_gstate.drawMode = QSGGeometry::DrawingMode(g->drawingMode());
+ m_gstate.lineWidth = g->lineWidth();
+
+ const bool hasPipeline = ensurePipelineState(e, sms);
+
+ if (pendingGStatePop)
+ m_gstate = m_gstateStack.pop();
+
+ if (!hasPipeline)
+ return false;
+
+ batch->ubufDataValid = true;
+
+ m_currentMaterial = material;
+
+ renderBatch->batch = batch;
+ renderBatch->sms = sms;
+
+ return true;
+}
+
+void Renderer::checkLineWidth(QSGGeometry *g)
+{
+ if (g->drawingMode() == QSGGeometry::DrawLines || g->drawingMode() == QSGGeometry::DrawLineLoop
+ || g->drawingMode() == QSGGeometry::DrawLineStrip)
+ {
+ if (g->lineWidth() != 1.0f) {
+ static bool checkedWideLineSupport = false;
+ if (!checkedWideLineSupport) {
+ checkedWideLineSupport = true;
+ if (!m_rhi->isFeatureSupported(QRhi::WideLines))
+ qWarning("Line widths other than 1 are not supported by the graphics API");
+ }
+ }
+ } else if (g->drawingMode() == QSGGeometry::DrawPoints) {
+ if (g->lineWidth() != 1.0f) {
+ static bool warnedPointSize = false;
+ if (!warnedPointSize) {
+ warnedPointSize = true;
+ qWarning("Point size is not controllable by QSGGeometry. "
+ "Set gl_PointSize from the vertex shader instead.");
+ }
+ }
+ }
+}
+
+void Renderer::renderMergedBatch(PreparedRenderBatch *renderBatch) // split prepare-render (RHI only)
+{
+ const Batch *batch = renderBatch->batch;
+ Element *e = batch->first;
+ QSGGeometryNode *gn = e->node;
+ QSGGeometry *g = gn->geometry();
+ checkLineWidth(g);
+
+ if (batch->clipState.type & ClipState::StencilClip)
+ enqueueStencilDraw(batch);
+
+ QRhiCommandBuffer *cb = commandBuffer();
+ setGraphicsPipeline(cb, batch, e);
+
+ for (int i = 0, ie = batch->drawSets.size(); i != ie; ++i) {
+ const DrawSet &draw = batch->drawSets.at(i);
+ const QRhiCommandBuffer::VertexInput vbufBindings[] = {
+ { batch->vbo.buf, quint32(draw.vertices) },
+ { batch->vbo.buf, quint32(draw.zorders) }
+ };
+ cb->setVertexInput(VERTEX_BUFFER_BINDING, 2, vbufBindings,
+ batch->ibo.buf, draw.indices,
+ m_uint32IndexForRhi ? QRhiCommandBuffer::IndexUInt32 : QRhiCommandBuffer::IndexUInt16);
+ cb->drawIndexed(draw.indexCount);
+ }
+}
+
+bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *renderBatch) // split prepare-render (RHI only)
+{
+ if (batch->vertexCount == 0)
+ return false;
+
+ Element *e = batch->first;
+ Q_ASSERT(e);
+
+ if (Q_UNLIKELY(debug_render())) {
+ qDebug() << " -"
+ << batch
+ << (batch->uploadedThisFrame ? "[ upload]" : "[retained]")
+ << (e->node->clipList() ? "[ clip]" : "[noclip]")
+ << (batch->isOpaque ? "[opaque]" : "[ alpha]")
+ << "[unmerged]"
+ << " Nodes:" << QString::fromLatin1("%1").arg(qsg_countNodesInBatch(batch), 4).toLatin1().constData()
+ << " Vertices:" << QString::fromLatin1("%1").arg(batch->vertexCount, 5).toLatin1().constData()
+ << " Indices:" << QString::fromLatin1("%1").arg(batch->indexCount, 5).toLatin1().constData()
+ << " root:" << batch->root;
+
+ batch->uploadedThisFrame = false;
+ }
+
+ m_current_projection_matrix = projectionMatrix();
+ m_current_projection_matrix_native_ndc = projectionMatrixWithNativeNDC();
+
+ QSGGeometryNode *gn = e->node;
+ updateClipState(gn->clipList(), batch);
+
+ // We always have dirty matrix as all batches are at a unique z range.
+ QSGMaterialShader::RenderState::DirtyStates dirty = QSGMaterialShader::RenderState::DirtyMatrix;
+
+ // The vertex attributes are assumed to be the same for all elements in the
+ // unmerged batch since the material (and so the shaders) is the same.
+ QSGGeometry *g = gn->geometry();
+ QSGMaterial *material = gn->activeMaterial();
+ ShaderManager::Shader *sms = m_shaderManager->prepareMaterialNoRewrite(material, m_rhi, g);
+ if (!sms)
+ return false;
+
+ Q_ASSERT(sms->programRhi.program);
+ if (m_currentShader != sms)
+ setActiveRhiShader(sms->programRhi.program, sms);
+
+ m_current_opacity = gn->inheritedOpacity();
+ if (sms->lastOpacity != m_current_opacity) {
+ dirty |= QSGMaterialShader::RenderState::DirtyOpacity;
+ sms->lastOpacity = m_current_opacity;
+ }
+
+ QMatrix4x4 rootMatrix = batch->root ? qsg_matrixForRoot(batch->root) : QMatrix4x4();
+
+ QSGMaterialRhiShaderPrivate *pd = QSGMaterialRhiShaderPrivate::get(sms->programRhi.program);
+ const int ubufSize = pd->masterUniformData.size();
+ if (pd->ubufBinding >= 0) {
+ int totalUBufSize = 0;
+ while (e) {
+ totalUBufSize += aligned(ubufSize, m_ubufAlignment);
+ e = e->nextInBatch;
+ }
+ bool ubufRebuild = false;
+ if (!batch->ubuf) {
+ batch->ubuf = m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, totalUBufSize);
+ ubufRebuild = true;
+ } else {
+ if (batch->ubuf->size() < totalUBufSize) {
+ batch->ubuf->setSize(totalUBufSize);
+ ubufRebuild = true;
+ }
+ }
+ if (ubufRebuild) {
+ batch->ubufDataValid = false;
+ if (!batch->ubuf->build()) {
+ qWarning("Failed to build uniform buffer of size %d bytes", totalUBufSize);
+ delete batch->ubuf;
+ batch->ubuf = nullptr;
+ return false;
+ }
+ }
+ }
+
+ QSGMaterialRhiShader::RenderState renderState = rhiState(QSGMaterialRhiShader::RenderState::DirtyStates(int(dirty)));
+ bool pendingGStatePop = false;
+ updateMaterialStaticData(sms, renderState,
+ material, batch, &pendingGStatePop);
+
+ int ubufOffset = 0;
+ QRhiGraphicsPipeline *ps = nullptr;
+ e = batch->first;
+ while (e) {
+ gn = e->node;
+
+ m_current_model_view_matrix = rootMatrix * *gn->matrix();
+ m_current_determinant = m_current_model_view_matrix.determinant();
+
+ m_current_projection_matrix = projectionMatrix();
+ m_current_projection_matrix_native_ndc = projectionMatrixWithNativeNDC();
+ if (m_useDepthBuffer) {
+ m_current_projection_matrix(2, 2) = m_zRange;
+ m_current_projection_matrix(2, 3) = 1.0f - e->order * m_zRange;
+ }
+
+ QSGMaterialRhiShader::RenderState renderState = rhiState(QSGMaterialRhiShader::RenderState::DirtyStates(int(dirty)));
+ ShaderManager::ShaderResourceBindingList bindings;
+ updateMaterialDynamicData(sms, renderState,
+ material, &bindings, batch, ubufOffset, ubufSize);
+
+#ifndef QT_NO_DEBUG
+ if (qsg_test_and_clear_material_failure()) {
+ qDebug("QSGMaterial::updateState() triggered an error (unmerged), batch will be skipped:");
+ qDebug() << " - offending node is" << e->node;
+ QSGNodeDumper::dump(rootNode());
+ qFatal("Aborting: scene graph is invalid...");
+ return false;
+ }
+#endif
+
+ e->srb = m_shaderManager->srb(bindings);
+
+ ubufOffset += aligned(ubufSize, m_ubufAlignment);
+
+ const QSGGeometry::DrawingMode prevDrawMode = m_gstate.drawMode;
+ const float prevLineWidth = m_gstate.lineWidth;
+ m_gstate.drawMode = QSGGeometry::DrawingMode(g->drawingMode());
+ m_gstate.lineWidth = g->lineWidth();
+
+ // Do not bother even looking up the ps if the topology has not changed
+ // since everything else is the same for all elements in the batch.
+ // (except if the material modified blend state)
+ if (!ps || m_gstate.drawMode != prevDrawMode || m_gstate.lineWidth != prevLineWidth || pendingGStatePop) {
+ if (!ensurePipelineState(e, sms)) {
+ if (pendingGStatePop)
+ m_gstate = m_gstateStack.pop();
+ return false;
+ }
+ ps = e->ps;
+ } else {
+ e->ps = ps;
+ }
+
+ // We don't need to bother with asking each node for its material as they
+ // are all identical (compare==0) since they are in the same batch.
+ m_currentMaterial = material;
+
+ // We only need to push this on the very first iteration...
+ dirty &= ~QSGMaterialShader::RenderState::DirtyOpacity;
+
+ e = e->nextInBatch;
+ }
+
+ if (pendingGStatePop)
+ m_gstate = m_gstateStack.pop();
+
+ batch->ubufDataValid = true;
+
+ renderBatch->batch = batch;
+ renderBatch->sms = sms;
+
+ return true;
+}
+
+void Renderer::renderUnmergedBatch(PreparedRenderBatch *renderBatch) // split prepare-render (RHI only)
+{
+ const Batch *batch = renderBatch->batch;
+ Element *e = batch->first;
+ QSGGeometryNode *gn = e->node;
+
+ if (batch->clipState.type & ClipState::StencilClip)
+ enqueueStencilDraw(batch);
+
+ int vOffset = 0;
+ int iOffset = 0;
+ QRhiCommandBuffer *cb = commandBuffer();
+
+ while (e) {
+ gn = e->node;
+ QSGGeometry *g = gn->geometry();
+ checkLineWidth(g);
+ const int effectiveIndexSize = m_uint32IndexForRhi ? sizeof(quint32) : g->sizeOfIndex();
+
+ setGraphicsPipeline(cb, batch, e);
+
+ const QRhiCommandBuffer::VertexInput vbufBinding(batch->vbo.buf, vOffset);
+ if (g->indexCount()) {
+ cb->setVertexInput(VERTEX_BUFFER_BINDING, 1, &vbufBinding,
+ batch->ibo.buf, iOffset,
+ effectiveIndexSize == sizeof(quint32) ? QRhiCommandBuffer::IndexUInt32
+ : QRhiCommandBuffer::IndexUInt16);
+ cb->drawIndexed(g->indexCount());
+ } else {
+ cb->setVertexInput(VERTEX_BUFFER_BINDING, 1, &vbufBinding);
+ cb->draw(g->vertexCount());
+ }
+
+ vOffset += g->sizeOfVertex() * g->vertexCount();
+ iOffset += g->indexCount() * effectiveIndexSize;
+
+ e = e->nextInBatch;
+ }
+}
+
+void Renderer::setGraphicsPipeline(QRhiCommandBuffer *cb, const Batch *batch, Element *e) // RHI only, [render step]
+{
+ cb->setGraphicsPipeline(e->ps);
+
+ if (!m_pstate.viewportSet) {
+ m_pstate.viewportSet = true;
+ cb->setViewport(m_pstate.viewport);
+ }
+ if (batch->clipState.type & ClipState::ScissorClip) {
+ Q_ASSERT(e->ps->flags().testFlag(QRhiGraphicsPipeline::UsesScissor));
+ m_pstate.scissorSet = true;
+ cb->setScissor(batch->clipState.scissor);
} else {
- glDisable(GL_DEPTH_TEST);
- glDepthMask(false);
+ Q_ASSERT(!e->ps->flags().testFlag(QRhiGraphicsPipeline::UsesScissor));
+ // Regardless of the ps not using scissor, the scissor may need to be
+ // reset, depending on the backend. So set the viewport again, which in
+ // turn also sets the scissor on backends where a scissor rect is
+ // always-on (Vulkan).
+ if (m_pstate.scissorSet) {
+ m_pstate.scissorSet = false;
+ cb->setViewport(m_pstate.viewport);
+ }
}
- glDisable(GL_CULL_FACE);
- glColorMask(true, true, true, true);
- glDisable(GL_SCISSOR_TEST);
- glDisable(GL_STENCIL_TEST);
+ if (batch->clipState.type & ClipState::StencilClip) {
+ Q_ASSERT(e->ps->flags().testFlag(QRhiGraphicsPipeline::UsesStencilRef));
+ cb->setStencilRef(batch->clipState.stencilRef);
+ }
+ if (e->ps->flags().testFlag(QRhiGraphicsPipeline::UsesBlendConstants))
+ cb->setBlendConstants(batch->blendConstant);
- bindable()->clear(clearMode());
+ cb->setShaderResources(e->srb);
+}
+
+void Renderer::renderBatches()
+{
+ if (Q_UNLIKELY(debug_render())) {
+ qDebug().nospace() << "Rendering:" << endl
+ << " -> Opaque: " << qsg_countNodesInBatches(m_opaqueBatches) << " nodes in " << m_opaqueBatches.size() << " batches..." << endl
+ << " -> Alpha: " << qsg_countNodesInBatches(m_alphaBatches) << " nodes in " << m_alphaBatches.size() << " batches...";
+ }
m_current_opacity = 1;
m_currentMaterial = nullptr;
m_currentShader = nullptr;
m_currentProgram = nullptr;
+ m_currentRhiProgram = nullptr;
m_currentClip = nullptr;
+ m_currentClipState.reset();
+
+ const QRect viewport = viewportRect();
bool renderOpaque = !debug_noopaque();
bool renderAlpha = !debug_noalpha();
- if (Q_LIKELY(renderOpaque)) {
- for (int i=0; i<m_opaqueBatches.size(); ++i) {
- Batch *b = m_opaqueBatches.at(i);
- if (b->merged)
- renderMergedBatch(b);
- else
- renderUnmergedBatch(b);
+ if (!m_rhi) {
+ // legacy, GL-only path
+
+ glViewport(viewport.x(), deviceRect().bottom() - viewport.bottom(), viewport.width(), viewport.height());
+ glClearColor(clearColor().redF(), clearColor().greenF(), clearColor().blueF(), clearColor().alphaF());
+
+ if (m_useDepthBuffer) {
+ glClearDepthf(1); // calls glClearDepth() under the hood for desktop OpenGL
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glDepthMask(true);
+ glDisable(GL_BLEND);
+ } else {
+ glDisable(GL_DEPTH_TEST);
+ glDepthMask(false);
}
- }
+ glDisable(GL_CULL_FACE);
+ glColorMask(true, true, true, true);
+ glDisable(GL_SCISSOR_TEST);
+ glDisable(GL_STENCIL_TEST);
+
+ bindable()->clear(clearMode());
- glEnable(GL_BLEND);
- if (m_useDepthBuffer)
- glDepthMask(false);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ if (m_renderPassRecordingCallbacks.start)
+ m_renderPassRecordingCallbacks.start(m_renderPassRecordingCallbacks.userData);
- if (Q_LIKELY(renderAlpha)) {
- for (int i=0; i<m_alphaBatches.size(); ++i) {
- Batch *b = m_alphaBatches.at(i);
- if (b->merged)
- renderMergedBatch(b);
- else if (b->isRenderNode)
- renderRenderNode(b);
+ if (Q_LIKELY(renderOpaque)) {
+ for (int i=0; i<m_opaqueBatches.size(); ++i) {
+ Batch *b = m_opaqueBatches.at(i);
+ if (b->merged)
+ renderMergedBatch(b);
+ else
+ renderUnmergedBatch(b);
+ }
+ }
+
+ glEnable(GL_BLEND);
+ if (m_useDepthBuffer)
+ glDepthMask(false);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ if (Q_LIKELY(renderAlpha)) {
+ for (int i=0; i<m_alphaBatches.size(); ++i) {
+ Batch *b = m_alphaBatches.at(i);
+ if (b->merged)
+ renderMergedBatch(b);
+ else if (b->isRenderNode)
+ renderRenderNode(b);
+ else
+ renderUnmergedBatch(b);
+ }
+ }
+
+ if (m_currentShader)
+ setActiveShader(nullptr, nullptr);
+
+ updateStencilClip(nullptr);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glDepthMask(true);
+
+ if (m_renderPassRecordingCallbacks.end)
+ m_renderPassRecordingCallbacks.end(m_renderPassRecordingCallbacks.userData);
+
+ } else {
+ // RHI path
+
+ m_pstate.viewport = QRhiViewport(viewport.x(), deviceRect().bottom() - viewport.bottom(), viewport.width(), viewport.height());
+ m_pstate.clearColor = clearColor();
+ m_pstate.dsClear = QRhiDepthStencilClearValue(1.0f, 0);
+ m_pstate.viewportSet = false;
+ m_pstate.scissorSet = false;
+
+ m_gstate.depthTest = true;
+ m_gstate.depthWrite = true;
+ m_gstate.depthFunc = QRhiGraphicsPipeline::Less;
+ m_gstate.blending = false;
+
+ m_gstate.cullMode = QRhiGraphicsPipeline::None;
+ m_gstate.colorWrite = QRhiGraphicsPipeline::R
+ | QRhiGraphicsPipeline::G
+ | QRhiGraphicsPipeline::B
+ | QRhiGraphicsPipeline::A;
+ m_gstate.usesScissor = false;
+ m_gstate.stencilTest = false;
+
+ m_gstate.sampleCount = renderTarget()->sampleCount();
+
+ QVarLengthArray<PreparedRenderBatch, 64> opaqueRenderBatches;
+ if (Q_LIKELY(renderOpaque)) {
+ for (int i = 0, ie = m_opaqueBatches.size(); i != ie; ++i) {
+ Batch *b = m_opaqueBatches.at(i);
+ PreparedRenderBatch renderBatch;
+ bool ok;
+ if (b->merged)
+ ok = prepareRenderMergedBatch(b, &renderBatch);
+ else
+ ok = prepareRenderUnmergedBatch(b, &renderBatch);
+ if (ok)
+ opaqueRenderBatches.append(renderBatch);
+ }
+ }
+
+ m_gstate.blending = true;
+ // factors never change, always set for premultiplied alpha based blending
+
+ // depth test stays enabled but no need to write out depth from the
+ // transparent (back-to-front) pass
+ m_gstate.depthWrite = false;
+
+ QVarLengthArray<PreparedRenderBatch, 64> alphaRenderBatches;
+ if (Q_LIKELY(renderAlpha)) {
+ for (int i = 0, ie = m_alphaBatches.size(); i != ie; ++i) {
+ Batch *b = m_alphaBatches.at(i);
+ PreparedRenderBatch renderBatch;
+ bool ok;
+ if (b->merged)
+ ok = prepareRenderMergedBatch(b, &renderBatch);
+ else if (b->isRenderNode)
+ ok = prepareRhiRenderNode(b, &renderBatch);
+ else
+ ok = prepareRenderUnmergedBatch(b, &renderBatch);
+ if (ok)
+ alphaRenderBatches.append(renderBatch);
+ }
+ }
+
+ if (m_visualizer->mode() != Visualizer::VisualizeNothing)
+ m_visualizer->prepareVisualize();
+
+ QRhiCommandBuffer *cb = commandBuffer();
+ cb->beginPass(renderTarget(), m_pstate.clearColor, m_pstate.dsClear, m_resourceUpdates);
+ m_resourceUpdates = nullptr;
+
+ if (m_renderPassRecordingCallbacks.start)
+ m_renderPassRecordingCallbacks.start(m_renderPassRecordingCallbacks.userData);
+
+ for (int i = 0, ie = opaqueRenderBatches.count(); i != ie; ++i) {
+ PreparedRenderBatch *renderBatch = &opaqueRenderBatches[i];
+ if (renderBatch->batch->merged)
+ renderMergedBatch(renderBatch);
else
- renderUnmergedBatch(b);
+ renderUnmergedBatch(renderBatch);
}
- }
- if (m_currentShader)
- setActiveShader(nullptr, nullptr);
- updateStencilClip(nullptr);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glDepthMask(true);
+ for (int i = 0, ie = alphaRenderBatches.count(); i != ie; ++i) {
+ PreparedRenderBatch *renderBatch = &alphaRenderBatches[i];
+ if (renderBatch->batch->merged)
+ renderMergedBatch(renderBatch);
+ else if (renderBatch->batch->isRenderNode)
+ renderRhiRenderNode(renderBatch->batch);
+ else
+ renderUnmergedBatch(renderBatch);
+ }
+
+ if (m_currentShader)
+ setActiveRhiShader(nullptr, nullptr);
+
+ if (m_renderPassRecordingCallbacks.end)
+ m_renderPassRecordingCallbacks.end(m_renderPassRecordingCallbacks.userData);
+
+ if (m_visualizer->mode() == Visualizer::VisualizeNothing)
+ cb->endPass();
+ }
}
void Renderer::deleteRemovedElements()
@@ -2588,8 +4170,6 @@ void Renderer::deleteRemovedElements()
void Renderer::render()
{
- Q_ASSERT(m_context->openglContext() == QOpenGLContext::currentContext());
-
if (Q_UNLIKELY(debug_dump())) {
qDebug("\n");
QSGNodeDumper::dump(rootNode());
@@ -2622,8 +4202,13 @@ void Renderer::render()
timer.start();
}
- if (m_vao)
- m_vao->bind();
+ if (!m_rhi) {
+ Q_ASSERT(m_context->openglContext() == QOpenGLContext::currentContext());
+ if (m_vao)
+ m_vao->bind();
+ } else {
+ m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ }
if (m_rebuild & (BuildRenderLists | BuildRenderListsForTaggedRoots)) {
bool complete = (m_rebuild & BuildRenderLists) != 0;
@@ -2747,11 +4332,21 @@ void Renderer::render()
m_renderOrderRebuildLower = -1;
m_renderOrderRebuildUpper = -1;
- if (m_visualizeMode != VisualizeNothing)
- visualize();
+ if (m_visualizer->mode() != Visualizer::VisualizeNothing)
+ m_visualizer->visualize();
- if (m_vao)
- m_vao->release();
+ if (!m_rhi) {
+ if (m_vao)
+ m_vao->release();
+ } else {
+ if (m_visualizer->mode() != Visualizer::VisualizeNothing)
+ commandBuffer()->endPass();
+
+ if (m_resourceUpdates) {
+ m_resourceUpdates->release();
+ m_resourceUpdates = nullptr;
+ }
+ }
}
struct RenderNodeState : public QSGRenderNode::RenderState
@@ -2770,7 +4365,7 @@ struct RenderNodeState : public QSGRenderNode::RenderState
bool m_stencilEnabled;
};
-void Renderer::renderRenderNode(Batch *batch)
+void Renderer::renderRenderNode(Batch *batch) // legacy (GL-only)
{
if (Q_UNLIKELY(debug_render()))
qDebug() << " -" << batch << "rendernode";
@@ -2801,8 +4396,8 @@ void Renderer::renderRenderNode(Batch *batch)
RenderNodeState state;
state.m_projectionMatrix = &pm;
- state.m_scissorEnabled = m_currentClipType & ScissorClip;
- state.m_stencilEnabled = m_currentClipType & StencilClip;
+ state.m_scissorEnabled = m_currentClipType & ClipState::ScissorClip;
+ state.m_stencilEnabled = m_currentClipType & ClipState::StencilClip;
state.m_scissorRect = m_currentScissorRect;
state.m_stencilValue = m_currentStencilValue;
@@ -2863,7 +4458,7 @@ void Renderer::renderRenderNode(Batch *batch)
if (changes & (QSGRenderNode::StencilState | QSGRenderNode::ScissorState)) {
glDisable(GL_SCISSOR_TEST);
m_currentClip = nullptr;
- m_currentClipType = NoClip;
+ m_currentClipType = ClipState::NoClip;
}
if (changes & QSGRenderNode::DepthState)
@@ -2886,320 +4481,210 @@ void Renderer::renderRenderNode(Batch *batch)
glBindFramebuffer(GL_FRAMEBUFFER, prevFbo);
}
-void Renderer::releaseCachedResources()
+bool Renderer::prepareRhiRenderNode(Batch *batch, PreparedRenderBatch *renderBatch) // split prepare-render (RHI only)
{
- m_shaderManager->invalidated();
-}
+ if (Q_UNLIKELY(debug_render()))
+ qDebug() << " -" << batch << "rendernode";
-class VisualizeShader : public QOpenGLShaderProgram
-{
-public:
- int color;
- int matrix;
- int rotation;
- int pattern;
- int projection;
-};
+ Q_ASSERT(batch->first->isRenderNode);
+ RenderNodeElement *e = static_cast<RenderNodeElement *>(batch->first);
-void Renderer::visualizeDrawGeometry(const QSGGeometry *g)
-{
- if (g->attributeCount() < 1)
- return;
- const QSGGeometry::Attribute *a = g->attributes();
- glVertexAttribPointer(0, a->tupleSize, a->type, false, g->sizeOfVertex(), g->vertexData());
- if (g->indexCount())
- glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
- else
- glDrawArrays(g->drawingMode(), 0, g->vertexCount());
+ setActiveRhiShader(nullptr, nullptr);
-}
+ QSGNode *clip = e->renderNode->parent();
+ QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(e->renderNode);
+ rd->m_clip_list = nullptr;
+ while (clip != rootNode()) {
+ if (clip->type() == QSGNode::ClipNodeType) {
+ rd->m_clip_list = static_cast<QSGClipNode *>(clip);
+ break;
+ }
+ clip = clip->parent();
+ }
-void Renderer::visualizeBatch(Batch *b)
-{
- VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram);
+ updateClipState(rd->m_clip_list, batch);
- if (b->positionAttribute != 0)
- return;
+ renderBatch->batch = batch;
+ renderBatch->sms = nullptr;
- QSGGeometryNode *gn = b->first->node;
- QSGGeometry *g = gn->geometry();
- const QSGGeometry::Attribute &a = g->attributes()[b->positionAttribute];
+ return true;
+}
- glBindBuffer(GL_ARRAY_BUFFER, b->vbo.id);
+void Renderer::renderRhiRenderNode(const Batch *batch) // split prepare-render (RHI only)
+{
+ if (batch->clipState.type & ClipState::StencilClip)
+ enqueueStencilDraw(batch);
- QMatrix4x4 matrix(m_current_projection_matrix);
- if (b->root)
- matrix = matrix * qsg_matrixForRoot(b->root);
+ RenderNodeElement *e = static_cast<RenderNodeElement *>(batch->first);
+ QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(e->renderNode);
- shader->setUniformValue(shader->pattern, float(b->merged ? 0 : 1));
+ QMatrix4x4 pm = projectionMatrix();
+ if (m_useDepthBuffer) {
+ pm(2, 2) = m_zRange;
+ pm(2, 3) = 1.0f - e->order * m_zRange;
+ }
- QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 1.0, 1.0);
- float cr = color.redF();
- float cg = color.greenF();
- float cb = color.blueF();
- shader->setUniformValue(shader->color, cr, cg, cb, 1.0);
+ RenderNodeState state;
+ state.m_projectionMatrix = &pm;
+ const std::array<int, 4> scissor = batch->clipState.scissor.scissor();
+ state.m_scissorRect = QRect(scissor[0], scissor[1], scissor[2], scissor[3]);
+ state.m_stencilValue = batch->clipState.stencilRef;
+ state.m_scissorEnabled = batch->clipState.type & ClipState::ScissorClip;
+ state.m_stencilEnabled = batch->clipState.type & ClipState::StencilClip;
- if (b->merged) {
- shader->setUniformValue(shader->matrix, matrix);
- const auto &dataStart = m_context->separateIndexBuffer() ? b->ibo.data : b->vbo.data;
- for (int ds=0; ds<b->drawSets.size(); ++ds) {
- const DrawSet &set = b->drawSets.at(ds);
- glVertexAttribPointer(a.position, 2, a.type, false, g->sizeOfVertex(), (void *) (qintptr) (set.vertices));
- glDrawElements(g->drawingMode(), set.indexCount, GL_UNSIGNED_SHORT,
- (void *)(qintptr)(dataStart + set.indices));
+ QSGNode *xform = e->renderNode->parent();
+ QMatrix4x4 matrix;
+ QSGNode *root = rootNode();
+ if (e->root) {
+ matrix = qsg_matrixForRoot(e->root);
+ root = e->root->sgNode;
+ }
+ while (xform != root) {
+ if (xform->type() == QSGNode::TransformNodeType) {
+ matrix = matrix * static_cast<QSGTransformNode *>(xform)->combinedMatrix();
+ break;
}
- } else {
- Element *e = b->first;
- int offset = 0;
- while (e) {
- gn = e->node;
- g = gn->geometry();
- shader->setUniformValue(shader->matrix, matrix * *gn->matrix());
- glVertexAttribPointer(a.position, a.tupleSize, a.type, false, g->sizeOfVertex(), (void *) (qintptr) offset);
- if (g->indexCount())
- glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
- else
- glDrawArrays(g->drawingMode(), 0, g->vertexCount());
- offset += g->sizeOfVertex() * g->vertexCount();
- e = e->nextInBatch;
+ xform = xform->parent();
+ }
+ rd->m_matrix = &matrix;
+
+ QSGNode *opacity = e->renderNode->parent();
+ rd->m_opacity = 1.0;
+ while (opacity != rootNode()) {
+ if (opacity->type() == QSGNode::OpacityNodeType) {
+ rd->m_opacity = static_cast<QSGOpacityNode *>(opacity)->combinedOpacity();
+ break;
}
+ opacity = opacity->parent();
}
-}
+ const QSGRenderNode::StateFlags changes = e->renderNode->changedStates();
+ QRhiCommandBuffer *cb = commandBuffer();
+ cb->beginExternal();
+ e->renderNode->render(&state);
+ cb->endExternal();
+ rd->m_matrix = nullptr;
+ rd->m_clip_list = nullptr;
-void Renderer::visualizeClipping(QSGNode *node)
-{
- if (node->type() == QSGNode::ClipNodeType) {
- VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram);
- QSGClipNode *clipNode = static_cast<QSGClipNode *>(node);
- QMatrix4x4 matrix = m_current_projection_matrix;
- if (clipNode->matrix())
- matrix = matrix * *clipNode->matrix();
- shader->setUniformValue(shader->matrix, matrix);
- visualizeDrawGeometry(clipNode->geometry());
+ if ((changes & QSGRenderNode::ViewportState)
+ || (changes & QSGRenderNode::ScissorState))
+ {
+ // Reset both flags if either is reported as changed, since with the rhi
+ // it could be setViewport() that will record the resetting of the scissor.
+ m_pstate.viewportSet = false;
+ m_pstate.scissorSet = false;
}
- QSGNODE_TRAVERSE(node) {
- visualizeClipping(child);
- }
+ // Do not bother with RenderTargetState. Where applicable, endExternal()
+ // ensures the correct target is rebound. For others (like Vulkan) it makes
+ // no sense since render() could not possibly do that on our command buffer
+ // which is in renderpass recording state.
}
-#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \
- | QSGNode::DirtyOpacity \
- | QSGNode::DirtyMatrix \
- | QSGNode::DirtyNodeRemoved)
-
-void Renderer::visualizeChangesPrepare(Node *n, uint parentChanges)
+void Renderer::setCustomRenderMode(const QByteArray &mode)
{
- uint childDirty = (parentChanges | n->dirtyState) & QSGNODE_DIRTY_PARENT;
- uint selfDirty = n->dirtyState | parentChanges;
- if (n->type() == QSGNode::GeometryNodeType && selfDirty != 0)
- m_visualizeChanceSet.insert(n, selfDirty);
- SHADOWNODE_TRAVERSE(n) {
- visualizeChangesPrepare(child, childDirty);
- }
+ if (mode.isEmpty())
+ m_visualizer->setMode(Visualizer::VisualizeNothing);
+ else if (mode == "clip")
+ m_visualizer->setMode(Visualizer::VisualizeClipping);
+ else if (mode == "overdraw")
+ m_visualizer->setMode(Visualizer::VisualizeOverdraw);
+ else if (mode == "batches")
+ m_visualizer->setMode(Visualizer::VisualizeBatches);
+ else if (mode == "changes")
+ m_visualizer->setMode(Visualizer::VisualizeChanges);
}
-void Renderer::visualizeChanges(Node *n)
+bool Renderer::hasCustomRenderModeWithContinuousUpdate() const
{
-
- if (n->type() == QSGNode::GeometryNodeType && n->element()->batch && m_visualizeChanceSet.contains(n)) {
- uint dirty = m_visualizeChanceSet.value(n);
- bool tinted = (dirty & QSGNODE_DIRTY_PARENT) != 0;
-
- VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram);
- QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 0.3, 1.0);
- float ca = 0.5;
- float cr = color.redF() * ca;
- float cg = color.greenF() * ca;
- float cb = color.blueF() * ca;
- shader->setUniformValue(shader->color, cr, cg, cb, ca);
- shader->setUniformValue(shader->pattern, float(tinted ? 0.5 : 0));
-
- QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode);
-
- QMatrix4x4 matrix = m_current_projection_matrix;
- if (n->element()->batch->root)
- matrix = matrix * qsg_matrixForRoot(n->element()->batch->root);
- matrix = matrix * *gn->matrix();
- shader->setUniformValue(shader->matrix, matrix);
- visualizeDrawGeometry(gn->geometry());
-
- // This is because many changes don't propegate their dirty state to the
- // parent so the node updater will not unset these states. They are
- // not used for anything so, unsetting it should have no side effects.
- n->dirtyState = nullptr;
- }
-
- SHADOWNODE_TRAVERSE(n) {
- visualizeChanges(child);
- }
+ return m_visualizer->mode() == Visualizer::VisualizeOverdraw;
}
-void Renderer::visualizeOverdraw_helper(Node *node)
+bool operator==(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW
{
- if (node->type() == QSGNode::GeometryNodeType && node->element()->batch) {
- VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram);
- QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node->sgNode);
-
- QMatrix4x4 matrix = m_current_projection_matrix;
- matrix(2, 2) = m_zRange;
- matrix(2, 3) = 1.0f - node->element()->order * m_zRange;
-
- if (node->element()->batch->root)
- matrix = matrix * qsg_matrixForRoot(node->element()->batch->root);
- matrix = matrix * *gn->matrix();
- shader->setUniformValue(shader->matrix, matrix);
-
- QColor color = node->element()->batch->isOpaque ? QColor::fromRgbF(0.3, 1.0, 0.3) : QColor::fromRgbF(1.0, 0.3, 0.3);
- float ca = 0.33f;
- shader->setUniformValue(shader->color, color.redF() * ca, color.greenF() * ca, color.blueF() * ca, ca);
-
- visualizeDrawGeometry(gn->geometry());
- }
-
- SHADOWNODE_TRAVERSE(node) {
- visualizeOverdraw_helper(child);
- }
+ return a.depthTest == b.depthTest
+ && a.depthWrite == b.depthWrite
+ && a.depthFunc == b.depthFunc
+ && a.blending == b.blending
+ && a.srcColor == b.srcColor
+ && a.dstColor == b.dstColor
+ && a.colorWrite == b.colorWrite
+ && a.cullMode == b.cullMode
+ && a.usesScissor == b.usesScissor
+ && a.stencilTest == b.stencilTest
+ && a.sampleCount == b.sampleCount
+ && a.drawMode == b.drawMode
+ && a.lineWidth == b.lineWidth;
}
-void Renderer::visualizeOverdraw()
+bool operator!=(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW
{
- VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram);
- shader->setUniformValue(shader->color, 0.5f, 0.5f, 1.0f, 1.0f);
- shader->setUniformValue(shader->projection, 1);
-
- glBlendFunc(GL_ONE, GL_ONE);
-
- static float step = 0;
- step += static_cast<float>(M_PI * 2 / 1000.);
- if (step > M_PI * 2)
- step = 0;
- float angle = 80.0 * std::sin(step);
-
- QMatrix4x4 xrot; xrot.rotate(20, 1, 0, 0);
- QMatrix4x4 zrot; zrot.rotate(angle, 0, 0, 1);
- QMatrix4x4 tx; tx.translate(0, 0, 1);
-
- QMatrix4x4 m;
-
-// m.rotate(180, 0, 1, 0);
-
- m.translate(0, 0.5, 4);
- m.scale(2, 2, 1);
-
- m.rotate(-30, 1, 0, 0);
- m.rotate(angle, 0, 1, 0);
- m.translate(0, 0, -1);
+ return !(a == b);
+}
- shader->setUniformValue(shader->rotation, m);
+uint qHash(const GraphicsState &s, uint seed) Q_DECL_NOTHROW
+{
+ // do not bother with all fields
+ return seed
+ + s.depthTest * 1000
+ + s.depthWrite * 100
+ + s.depthFunc
+ + s.blending * 10
+ + s.srcColor
+ + s.cullMode
+ + s.usesScissor
+ + s.stencilTest
+ + s.sampleCount;
+}
- float box[] = {
- // lower
- -1, 1, 0, 1, 1, 0,
- -1, 1, 0, -1, -1, 0,
- 1, 1, 0, 1, -1, 0,
- -1, -1, 0, 1, -1, 0,
+bool operator==(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) Q_DECL_NOTHROW
+{
+ return a.state == b.state
+ && a.sms->programRhi.program == b.sms->programRhi.program
+ && a.rpDesc == b.rpDesc
+ && a.layoutCompatibleSrb->isLayoutCompatible(b.layoutCompatibleSrb);
+}
- // upper
- -1, 1, 1, 1, 1, 1,
- -1, 1, 1, -1, -1, 1,
- 1, 1, 1, 1, -1, 1,
- -1, -1, 1, 1, -1, 1,
+bool operator!=(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
- // sides
- -1, -1, 0, -1, -1, 1,
- 1, -1, 0, 1, -1, 1,
- -1, 1, 0, -1, 1, 1,
- 1, 1, 0, 1, 1, 1
- };
- glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, box);
- glLineWidth(2);
- glDrawArrays(GL_LINES, 0, 24);
+uint qHash(const GraphicsPipelineStateKey &k, uint seed) Q_DECL_NOTHROW
+{
+ return qHash(k.state, seed) + qHash(k.sms->programRhi.program, seed) + qHash(k.rpDesc, seed);
+}
- visualizeOverdraw_helper(m_nodes.value(rootNode()));
+Visualizer::Visualizer(Renderer *renderer)
+ : m_renderer(renderer),
+ m_visualizeMode(VisualizeNothing)
+{
+}
- // Animate the view...
- QSurface *surface = m_context->openglContext()->surface();
- if (surface->surfaceClass() == QSurface::Window)
- if (QQuickWindow *window = qobject_cast<QQuickWindow *>(static_cast<QWindow *>(surface)))
- window->update();
+Visualizer::~Visualizer()
+{
}
-void Renderer::setCustomRenderMode(const QByteArray &mode)
+#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \
+ | QSGNode::DirtyOpacity \
+ | QSGNode::DirtyMatrix \
+ | QSGNode::DirtyNodeRemoved)
+
+void Visualizer::visualizeChangesPrepare(Node *n, uint parentChanges)
{
- if (mode.isEmpty()) m_visualizeMode = VisualizeNothing;
- else if (mode == "clip") m_visualizeMode = VisualizeClipping;
- else if (mode == "overdraw") m_visualizeMode = VisualizeOverdraw;
- else if (mode == "batches") m_visualizeMode = VisualizeBatches;
- else if (mode == "changes") m_visualizeMode = VisualizeChanges;
-}
-
-void Renderer::visualize()
-{
- if (!m_shaderManager->visualizeProgram) {
- VisualizeShader *prog = new VisualizeShader();
- QSGShaderSourceBuilder::initializeProgramFromFiles(
- prog,
- QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.vert"),
- QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.frag"));
- prog->bindAttributeLocation("v", 0);
- prog->link();
- prog->bind();
- prog->color = prog->uniformLocation("color");
- prog->pattern = prog->uniformLocation("pattern");
- prog->projection = prog->uniformLocation("projection");
- prog->matrix = prog->uniformLocation("matrix");
- prog->rotation = prog->uniformLocation("rotation");
- m_shaderManager->visualizeProgram = prog;
- } else {
- m_shaderManager->visualizeProgram->bind();
+ uint childDirty = (parentChanges | n->dirtyState) & QSGNODE_DIRTY_PARENT;
+ uint selfDirty = n->dirtyState | parentChanges;
+ if (n->type() == QSGNode::GeometryNodeType && selfDirty != 0)
+ m_visualizeChangeSet.insert(n, selfDirty);
+ SHADOWNODE_TRAVERSE(n) {
+ visualizeChangesPrepare(child, childDirty);
}
- VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram);
-
- glDisable(GL_DEPTH_TEST);
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glEnableVertexAttribArray(0);
-
- // Blacken out the actual rendered content...
- float bgOpacity = 0.8f;
- if (m_visualizeMode == VisualizeBatches)
- bgOpacity = 1.0;
- float v[] = { -1, 1, 1, 1, -1, -1, 1, -1 };
- shader->setUniformValue(shader->color, 0.0f, 0.0f, 0.0f, bgOpacity);
- shader->setUniformValue(shader->matrix, QMatrix4x4());
- shader->setUniformValue(shader->rotation, QMatrix4x4());
- shader->setUniformValue(shader->pattern, 0.0f);
- shader->setUniformValue(shader->projection, false);
- glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, v);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
- if (m_visualizeMode == VisualizeBatches) {
- srand(0); // To force random colors to be roughly the same every time..
- for (int i=0; i<m_opaqueBatches.size(); ++i) visualizeBatch(m_opaqueBatches.at(i));
- for (int i=0; i<m_alphaBatches.size(); ++i) visualizeBatch(m_alphaBatches.at(i));
- } else if (m_visualizeMode == VisualizeClipping) {
- shader->setUniformValue(shader->pattern, 0.5f);
- shader->setUniformValue(shader->color, 0.2f, 0.0f, 0.0f, 0.2f);
- visualizeClipping(rootNode());
- } else if (m_visualizeMode == VisualizeChanges) {
- visualizeChanges(m_nodes.value(rootNode()));
- m_visualizeChanceSet.clear();
- } else if (m_visualizeMode == VisualizeOverdraw) {
- visualizeOverdraw();
- }
-
- // Reset state back to defaults..
- glDisable(GL_BLEND);
- glDisableVertexAttribArray(0);
- shader->release();
}
-QT_END_NAMESPACE
+} // namespace QSGBatchRenderer
-}
+QT_END_NAMESPACE
#include "moc_qsgbatchrenderer_p.cpp"
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
index 12b48c1451..297df2232a 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
@@ -58,11 +58,14 @@
#include <private/qsgnodeupdater_p.h>
#include <private/qsgrendernode_p.h>
#include <private/qdatabuffer_p.h>
+#include <private/qsgtexture_p.h>
#include <QtCore/QBitArray>
-
+#include <QtCore/QStack>
#include <QtGui/QOpenGLFunctions>
+#include <QtGui/private/qrhi_p.h>
+
QT_BEGIN_NAMESPACE
class QOpenGLVertexArrayObject;
@@ -300,10 +303,11 @@ struct Buffer {
// Data is only valid while preparing the upload. Exception is if we are using the
// broken IBO workaround or we are using a visualization mode.
char *data;
+ QRhiBuffer *buf;
+ uint nonDynamicChangeCount;
};
struct Element {
-
Element()
: boundsComputed(false)
, boundsOutsideFloatRange(false)
@@ -334,6 +338,8 @@ struct Element {
Rect bounds; // in device coordinates
int order = 0;
+ QRhiShaderResourceBindings *srb = nullptr;
+ QRhiGraphicsPipeline *ps = nullptr;
uint boundsComputed : 1;
uint boundsOutsideFloatRange : 1;
@@ -390,6 +396,48 @@ enum BatchCompatibility
BatchIsCompatible
};
+struct ClipState
+{
+ enum ClipTypeBit
+ {
+ NoClip = 0x00,
+ ScissorClip = 0x01,
+ StencilClip = 0x02
+ };
+ Q_DECLARE_FLAGS(ClipType, ClipTypeBit)
+
+ const QSGClipNode *clipList;
+ ClipType type;
+ QRhiScissor scissor;
+ int stencilRef;
+
+ inline void reset();
+};
+
+struct StencilClipState
+{
+ StencilClipState() : drawCalls(1) { }
+
+ bool updateStencilBuffer = false;
+ QRhiShaderResourceBindings *srb = nullptr;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ibuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+
+ struct StencilDrawCall {
+ int stencilRef;
+ int vertexCount;
+ int indexCount;
+ QRhiCommandBuffer::IndexFormat indexFormat;
+ quint32 vbufOffset;
+ quint32 ibufOffset;
+ quint32 ubufOffset;
+ };
+ QDataBuffer<StencilDrawCall> drawCalls;
+
+ inline void reset();
+};
+
struct Batch
{
Batch() : drawSets(1) {}
@@ -403,6 +451,7 @@ struct Batch
// pseudo-constructor...
void init() {
+ // Only non-reusable members are reset here. See Renderer::newBatch().
first = nullptr;
root = nullptr;
vertexCount = 0;
@@ -413,6 +462,9 @@ struct Batch
positionAttribute = -1;
uploadedThisFrame = false;
isRenderNode = false;
+ ubufDataValid = false;
+ clipState.reset();
+ blendConstant = QColor();
}
Element *first;
@@ -429,16 +481,21 @@ struct Batch
uint needsUpload : 1;
uint merged : 1;
uint isRenderNode : 1;
+ uint ubufDataValid : 1;
mutable uint uploadedThisFrame : 1; // solely for debugging purposes
Buffer vbo;
Buffer ibo;
+ QRhiBuffer *ubuf;
+ ClipState clipState;
+ StencilClipState stencilClipState;
+ QColor blendConstant;
QDataBuffer<DrawSet> drawSets;
};
-// NOTE: Node is zero-allocated by the Allocator.
+// NOTE: Node is zero-initialized by the Allocator.
struct Node
{
QSGNode *sgNode;
@@ -574,28 +631,41 @@ class ShaderManager : public QObject
Q_OBJECT
public:
struct Shader {
- ~Shader() { delete program; }
- int id_zRange;
- int pos_order;
- QSGMaterialShader *program;
+ ~Shader() {
+ delete programRhi.program;
+ delete programGL.program;
+ }
+ struct {
+ QSGMaterialShader *program = nullptr;
+ int pos_order;
+ } programGL;
+ struct {
+ QSGMaterialRhiShader *program = nullptr;
+ QRhiVertexInputLayout inputLayout;
+ QVarLengthArray<QRhiGraphicsShaderStage, 2> shaderStages;
+ } programRhi;
float lastOpacity;
};
- ShaderManager(QSGDefaultRenderContext *ctx) : visualizeProgram(nullptr), blitProgram(nullptr), context(ctx) { }
+ ShaderManager(QSGDefaultRenderContext *ctx) : blitProgram(nullptr), context(ctx) { }
~ShaderManager() {
qDeleteAll(rewrittenShaders);
qDeleteAll(stockShaders);
}
+ void clearCachedRendererData();
+
+ using ShaderResourceBindingList = QVarLengthArray<QRhiShaderResourceBinding, 8>;
+
+ QRhiShaderResourceBindings *srb(const ShaderResourceBindingList &bindings);
+
public Q_SLOTS:
void invalidated();
public:
- Shader *prepareMaterial(QSGMaterial *material);
- Shader *prepareMaterialNoRewrite(QSGMaterial *material);
-
- QOpenGLShaderProgram *visualizeProgram;
+ Shader *prepareMaterial(QSGMaterial *material, bool enableRhiShaders = false, const QSGGeometry *geometry = nullptr);
+ Shader *prepareMaterialNoRewrite(QSGMaterial *material, bool enableRhiShaders = false, const QSGGeometry *geometry = nullptr);
private:
QHash<QSGMaterialType *, Shader *> rewrittenShaders;
@@ -603,14 +673,55 @@ private:
QOpenGLShaderProgram *blitProgram;
QSGDefaultRenderContext *context;
+
+ QHash<ShaderResourceBindingList, QRhiShaderResourceBindings *> srbCache;
};
-class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer, public QOpenGLFunctions
+struct GraphicsState
{
-public:
- Renderer(QSGDefaultRenderContext *);
- ~Renderer();
+ bool depthTest = false;
+ bool depthWrite = false;
+ QRhiGraphicsPipeline::CompareOp depthFunc = QRhiGraphicsPipeline::Less;
+ bool blending = false;
+ QRhiGraphicsPipeline::BlendFactor srcColor = QRhiGraphicsPipeline::One;
+ QRhiGraphicsPipeline::BlendFactor dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
+ QRhiGraphicsPipeline::ColorMask colorWrite = QRhiGraphicsPipeline::ColorMask(0xF);
+ QRhiGraphicsPipeline::CullMode cullMode = QRhiGraphicsPipeline::None;
+ bool usesScissor = false;
+ bool stencilTest = false;
+ int sampleCount = 1;
+ QSGGeometry::DrawingMode drawMode = QSGGeometry::DrawTriangles;
+ float lineWidth = 1.0f;
+};
+
+bool operator==(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW;
+bool operator!=(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW;
+uint qHash(const GraphicsState &s, uint seed = 0) Q_DECL_NOTHROW;
+
+struct GraphicsPipelineStateKey
+{
+ GraphicsState state;
+ const ShaderManager::Shader *sms;
+ const QRhiRenderPassDescriptor *rpDesc;
+ const QRhiShaderResourceBindings *layoutCompatibleSrb;
+};
+
+bool operator==(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) Q_DECL_NOTHROW;
+bool operator!=(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) Q_DECL_NOTHROW;
+uint qHash(const GraphicsPipelineStateKey &k, uint seed = 0) Q_DECL_NOTHROW;
+
+struct RenderPassState
+{
+ QRhiViewport viewport;
+ QColor clearColor;
+ QRhiDepthStencilClearValue dsClear;
+ bool viewportSet;
+ bool scissorSet;
+};
+class Visualizer
+{
+public:
enum VisualizeMode {
VisualizeNothing,
VisualizeBatches,
@@ -619,20 +730,36 @@ public:
VisualizeOverdraw
};
+ Visualizer(Renderer *renderer);
+ virtual ~Visualizer();
+
+ VisualizeMode mode() const { return m_visualizeMode; }
+ void setMode(VisualizeMode mode) { m_visualizeMode = mode; }
+
+ virtual void visualizeChangesPrepare(Node *n, uint parentChanges = 0);
+ virtual void prepareVisualize() = 0;
+ virtual void visualize() = 0;
+
+ virtual void releaseResources() = 0;
+
+protected:
+ Renderer *m_renderer;
+ VisualizeMode m_visualizeMode;
+ QHash<Node *, uint> m_visualizeChangeSet;
+};
+
+class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer, public QOpenGLFunctions
+{
+public:
+ Renderer(QSGDefaultRenderContext *);
+ ~Renderer();
+
protected:
void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override;
void render() override;
void releaseCachedResources() override;
private:
- enum ClipTypeBit
- {
- NoClip = 0x00,
- ScissorClip = 0x01,
- StencilClip = 0x02
- };
- Q_DECLARE_FLAGS(ClipType, ClipTypeBit)
-
enum RebuildFlag {
BuildRenderListsForTaggedRoots = 0x0001,
BuildRenderLists = 0x0002,
@@ -641,7 +768,10 @@ private:
};
friend class Updater;
+ friend class OpenGLVisualizer;
+ friend class RhiVisualizer;
+ void destroyGraphicsResources();
void map(Buffer *buffer, int size, bool isIndexBuf = false);
void unmap(Buffer *buffer, bool isIndexBuf = false);
@@ -658,16 +788,41 @@ private:
void invalidateBatchAndOverlappingRenderOrders(Batch *batch);
void uploadBatch(Batch *b);
- void uploadMergedElement(Element *e, int vaOffset, char **vertexData, char **zData, char **indexData, quint16 *iBase, int *indexCount);
+ void uploadMergedElement(Element *e, int vaOffset, char **vertexData, char **zData, char **indexData, void *iBasePtr, int *indexCount);
+
+ struct PreparedRenderBatch {
+ const Batch *batch;
+ ShaderManager::Shader *sms;
+ };
void renderBatches();
- void renderMergedBatch(const Batch *batch);
- void renderUnmergedBatch(const Batch *batch);
- ClipType updateStencilClip(const QSGClipNode *clip);
+ bool ensurePipelineState(Element *e, const ShaderManager::Shader *sms);
+ QRhiTexture *dummyTexture();
+ void updateMaterialDynamicData(ShaderManager::Shader *sms, QSGMaterialRhiShader::RenderState &renderState,
+ QSGMaterial *material, ShaderManager::ShaderResourceBindingList *bindings,
+ const Batch *batch, int ubufOffset, int ubufRegionSize);
+ void updateMaterialStaticData(ShaderManager::Shader *sms, QSGMaterialRhiShader::RenderState &renderState,
+ QSGMaterial *material, Batch *batch, bool *gstateChanged);
+ void checkLineWidth(QSGGeometry *g);
+ bool prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *renderBatch);
+ void renderMergedBatch(PreparedRenderBatch *renderBatch);
+ bool prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *renderBatch);
+ void renderUnmergedBatch(PreparedRenderBatch *renderBatch);
+ void setGraphicsPipeline(QRhiCommandBuffer *cb, const Batch *batch, Element *e);
+ void renderMergedBatch(const Batch *batch); // GL
+ void renderUnmergedBatch(const Batch *batch); // GL
+ ClipState::ClipType updateStencilClip(const QSGClipNode *clip);
void updateClip(const QSGClipNode *clipList, const Batch *batch);
+ void applyClipStateToGraphicsState();
+ QRhiGraphicsPipeline *buildStencilPipeline(const Batch *batch, bool firstStencilClipInBatch);
+ void updateClipState(const QSGClipNode *clipList, Batch *batch);
+ void enqueueStencilDraw(const Batch *batch);
const QMatrix4x4 &matrixForRoot(Node *node);
void renderRenderNode(Batch *batch);
+ bool prepareRhiRenderNode(Batch *batch, PreparedRenderBatch *renderBatch);
+ void renderRhiRenderNode(const Batch *batch);
void setActiveShader(QSGMaterialShader *program, ShaderManager::Shader *shader);
+ void setActiveRhiShader(QSGMaterialRhiShader *program, ShaderManager::Shader *shader);
bool changeBatchRoot(Node *node, Node *newRoot);
void registerBatchRoot(Node *childRoot, Node *parentRoot);
@@ -683,15 +838,8 @@ private:
inline Batch *newBatch();
void invalidateAndRecycleBatch(Batch *b);
- void visualize();
- void visualizeBatch(Batch *b);
- void visualizeClipping(QSGNode *node);
- void visualizeChangesPrepare(Node *n, uint parentChanges = 0);
- void visualizeChanges(Node *n);
- void visualizeOverdraw();
- void visualizeOverdraw_helper(Node *node);
- void visualizeDrawGeometry(const QSGGeometry *g);
void setCustomRenderMode(const QByteArray &mode) override;
+ bool hasCustomRenderModeWithContinuousUpdate() const override;
QSGDefaultRenderContext *m_context;
QSet<Node *> m_taggedRoots;
@@ -722,29 +870,54 @@ private:
int m_batchNodeThreshold;
int m_batchVertexThreshold;
+ Visualizer *m_visualizer;
+
// Stuff used during rendering only...
- ShaderManager *m_shaderManager;
+ ShaderManager *m_shaderManager; // per rendercontext, shared
QSGMaterial *m_currentMaterial;
QSGMaterialShader *m_currentProgram;
+ QSGMaterialRhiShader *m_currentRhiProgram;
ShaderManager::Shader *m_currentShader;
+ ClipState m_currentClipState;
+ // *** legacy (GL) only
QRect m_currentScissorRect;
int m_currentStencilValue;
QOpenGLShaderProgram m_clipProgram;
int m_clipMatrixId;
const QSGClipNode *m_currentClip;
- ClipType m_currentClipType;
+ ClipState::ClipType m_currentClipType;
+ // ***
QDataBuffer<char> m_vertexUploadPool;
QDataBuffer<char> m_indexUploadPool;
// For minimal OpenGL core profile support
QOpenGLVertexArrayObject *m_vao;
- QHash<Node *, uint> m_visualizeChanceSet;
- VisualizeMode m_visualizeMode;
-
Allocator<Node, 256> m_nodeAllocator;
Allocator<Element, 64> m_elementAllocator;
+
+ QRhiResourceUpdateBatch *m_resourceUpdates = nullptr;
+ uint m_ubufAlignment;
+ bool m_uint32IndexForRhi;
+ GraphicsState m_gstate;
+ RenderPassState m_pstate;
+ QStack<GraphicsState> m_gstateStack;
+ QHash<GraphicsPipelineStateKey, QRhiGraphicsPipeline *> m_pipelines;
+ QHash<QSGSamplerDescription, QRhiSampler *> m_samplers;
+ QRhiTexture *m_dummyTexture = nullptr;
+
+ struct StencilClipCommonData {
+ QRhiGraphicsPipeline *replacePs = nullptr;
+ QRhiGraphicsPipeline *incrPs = nullptr;
+ QShader vs;
+ QShader fs;
+ QRhiVertexInputLayout inputLayout;
+ QRhiGraphicsPipeline::Topology topology;
+ inline void reset();
+ } m_stencilClipCommon;
+
+ inline int mergedIndexElemSize() const;
};
Batch *Renderer::newBatch()
@@ -753,18 +926,69 @@ Batch *Renderer::newBatch()
int size = m_batchPool.size();
if (size) {
b = m_batchPool.at(size - 1);
+ // vbo, ibo, ubuf, stencil-related buffers are reused
m_batchPool.resize(size - 1);
} else {
b = new Batch();
Q_ASSERT(offsetof(Batch, ibo) == sizeof(Buffer) + offsetof(Batch, vbo));
memset(&b->vbo, 0, sizeof(Buffer) * 2); // Clear VBO & IBO
+ b->ubuf = nullptr;
+ b->stencilClipState.reset();
}
+ // initialize (when new batch) or reset (when reusing a batch) the non-reusable fields
b->init();
return b;
}
+int Renderer::mergedIndexElemSize() const
+{
+ return m_uint32IndexForRhi ? sizeof(quint32) : sizeof(quint16);
+}
+
+void Renderer::StencilClipCommonData::reset()
+{
+ delete replacePs;
+ replacePs = nullptr;
+
+ delete incrPs;
+ incrPs = nullptr;
+
+ vs = QShader();
+ fs = QShader();
+}
+
+void ClipState::reset()
+{
+ clipList = nullptr;
+ type = NoClip;
+ stencilRef = 0;
+}
+
+void StencilClipState::reset()
+{
+ updateStencilBuffer = false;
+
+ delete srb;
+ srb = nullptr;
+
+ delete vbuf;
+ vbuf = nullptr;
+
+ delete ibuf;
+ ibuf = nullptr;
+
+ delete ubuf;
+ ubuf = nullptr;
+
+ drawCalls.reset();
}
+}
+
+Q_DECLARE_TYPEINFO(QSGBatchRenderer::GraphicsState, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QSGBatchRenderer::GraphicsPipelineStateKey, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QSGBatchRenderer::RenderPassState, Q_MOVABLE_TYPE);
+
QT_END_NAMESPACE
#endif // QSGBATCHRENDERER_P_H
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.cpp b/src/quick/scenegraph/coreapi/qsggeometry.cpp
index dd701fba5f..a28eee5288 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.cpp
+++ b/src/quick/scenegraph/coreapi/qsggeometry.cpp
@@ -443,6 +443,7 @@ QSGGeometry::QSGGeometry(const QSGGeometry::AttributeSet &attributes,
Q_ASSERT(m_attributes.stride > 0);
#if QT_CONFIG(opengl)
Q_ASSERT_X(indexType != GL_UNSIGNED_INT
+ || !QOpenGLContext::currentContext() // rhi, support for uint cannot be checked here
|| static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions())
->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint),
"QSGGeometry::QSGGeometry",
@@ -575,6 +576,10 @@ const void *QSGGeometry::indexData() const
\value IntType
\value UnsignedIntType
\value FloatType
+ \value Bytes2Type Added in Qt 5.14.
+ \value Bytes3Type Added in Qt 5.14.
+ \value Bytes4Type Added in Qt 5.14.
+ \value DoubleType Added in Qt 5.14.
*/
/*!
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.h b/src/quick/scenegraph/coreapi/qsggeometry.h
index 7a916610e3..d17915a842 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.h
+++ b/src/quick/scenegraph/coreapi/qsggeometry.h
@@ -88,7 +88,11 @@ public:
UnsignedShortType = 0x1403,
IntType = 0x1404,
UnsignedIntType = 0x1405,
- FloatType = 0x1406
+ FloatType = 0x1406,
+ Bytes2Type = 0x1407,
+ Bytes3Type = 0x1408,
+ Bytes4Type = 0x1409,
+ DoubleType = 0x140A
};
struct Q_QUICK_EXPORT Attribute
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
index 8557de1b1f..c8ae26311b 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterial.cpp
+++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
@@ -39,14 +39,6 @@
#include "qsgmaterial.h"
#include "qsgrenderer_p.h"
-#include "qsgmaterialshader_p.h"
-#if QT_CONFIG(opengl)
-# include <private/qsgshadersourcebuilder_p.h>
-# include <private/qsgdefaultcontext_p.h>
-# include <private/qsgdefaultrendercontext_p.h>
-# include <QtGui/QOpenGLFunctions>
-# include <QtGui/QOpenGLContext>
-#endif
QT_BEGIN_NAMESPACE
@@ -64,17 +56,6 @@ void qsg_set_material_failure()
qsg_material_failure = true;
}
#endif
-#if QT_CONFIG(opengl)
-const char *QSGMaterialShaderPrivate::loadShaderSource(QOpenGLShader::ShaderType type) const
-{
- const QStringList files = m_sourceFiles[type];
- QSGShaderSourceBuilder builder;
- for (const QString &file : files)
- builder.appendSourceFile(file);
- m_sources[type] = builder.source();
- return m_sources[type].constData();
-}
-#endif
#ifndef QT_NO_DEBUG
static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK");
@@ -89,494 +70,6 @@ static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK"
\l {scene graph}{Qt Quick Scene Graph}.
*/
-/*!
- \class QSGMaterialShader
- \brief The QSGMaterialShader class represents an OpenGL shader program
- in the renderer.
- \inmodule QtQuick
- \ingroup qtquick-scenegraph-materials
-
- The QSGMaterialShader API is very low-level. A more convenient API, which
- provides almost all the same features, is available through
- QSGSimpleMaterialShader.
-
- The QSGMaterial and QSGMaterialShader form a tight relationship. For one
- scene graph (including nested graphs), there is one unique QSGMaterialShader
- instance which encapsulates the QOpenGLShaderProgram the scene graph uses
- to render that material, such as a shader to flat coloring of geometry.
- Each QSGGeometryNode can have a unique QSGMaterial containing the
- how the shader should be configured when drawing that node, such as
- the actual color used to render the geometry.
-
- An instance of QSGMaterialShader is never created explicitly by the user,
- it will be created on demand by the scene graph through
- QSGMaterial::createShader(). The scene graph will make sure that there
- is only one instance of each shader implementation through a scene graph.
-
- The source code returned from vertexShader() is used to control what the
- material does with the vertiex data that comes in from the geometry.
- The source code returned from the fragmentShader() is used to control
- what how the material should fill each individual pixel in the geometry.
- The vertex and fragment source code is queried once during initialization,
- changing what is returned from these functions later will not have
- any effect.
-
- The activate() function is called by the scene graph when a shader is
- is starting to be used. The deactivate function is called by the scene
- graph when the shader is no longer going to be used. While active,
- the scene graph may make one or more calls to updateState() which
- will update the state of the shader for each individual geometry to
- render.
-
- The attributeNames() returns the name of the attributes used in the
- vertexShader(). These are used in the default implementation of
- activate() and deactivate() to decide whice vertex registers are enabled.
-
- The initialize() function is called during program creation to allow
- subclasses to prepare for use, such as resolve uniform names in the
- vertexShader() and fragmentShader().
-
- A minimal example:
- \code
- class Shader : public QSGMaterialShader
- {
- public:
- const char *vertexShader() const {
- return
- "attribute highp vec4 vertex; \n"
- "uniform highp mat4 matrix; \n"
- "void main() { \n"
- " gl_Position = matrix * vertex; \n"
- "}";
- }
-
- const char *fragmentShader() const {
- return
- "uniform lowp float opacity; \n"
- "void main() { \n"
- " gl_FragColor = vec4(1, 0, 0, 1) * opacity; \n"
- "}";
- }
-
- char const *const *attributeNames() const
- {
- static char const *const names[] = { "vertex", 0 };
- return names;
- }
-
- void initialize()
- {
- QSGMaterialShader::initialize();
- m_id_matrix = program()->uniformLocation("matrix");
- m_id_opacity = program()->uniformLocation("opacity");
- }
-
- void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
- {
- Q_ASSERT(program()->isLinked());
- if (state.isMatrixDirty())
- program()->setUniformValue(m_id_matrix, state.combinedMatrix());
- if (state.isOpacityDirty())
- program()->setUniformValue(m_id_opacity, state.opacity());
- }
-
- private:
- int m_id_matrix;
- int m_id_opacity;
- };
- \endcode
-
- \note All classes with QSG prefix should be used solely on the scene graph's
- rendering thread. See \l {Scene Graph and Rendering} for more information.
-
- */
-
-
-
-/*!
- Creates a new QSGMaterialShader.
- */
-QSGMaterialShader::QSGMaterialShader()
- : d_ptr(new QSGMaterialShaderPrivate)
-{
-}
-
-/*!
- \internal
- */
-QSGMaterialShader::QSGMaterialShader(QSGMaterialShaderPrivate &dd)
- : d_ptr(&dd)
-{
-}
-
-/*!
- \internal
- */
-QSGMaterialShader::~QSGMaterialShader()
-{
-}
-
-/*!
- \fn char const *const *QSGMaterialShader::attributeNames() const
-
- Returns a zero-terminated array describing the names of the
- attributes used in the vertex shader.
-
- This function is called when the shader is compiled to specify
- which attributes exist. The order of the attribute names
- defines the attribute register position in the vertex shader.
- */
-
-#if QT_CONFIG(opengl)
-/*!
- \fn const char *QSGMaterialShader::vertexShader() const
-
- Called when the shader is being initialized to get the vertex
- shader source code.
-
- The contents returned from this function should never change.
-*/
-const char *QSGMaterialShader::vertexShader() const
-{
- Q_D(const QSGMaterialShader);
- return d->loadShaderSource(QOpenGLShader::Vertex);
-}
-
-
-/*!
- \fn const char *QSGMaterialShader::fragmentShader() const
-
- Called when the shader is being initialized to get the fragment
- shader source code.
-
- The contents returned from this function should never change.
-*/
-const char *QSGMaterialShader::fragmentShader() const
-{
- Q_D(const QSGMaterialShader);
- return d->loadShaderSource(QOpenGLShader::Fragment);
-}
-
-
-/*!
- \fn QOpenGLShaderProgram *QSGMaterialShader::program()
-
- Returns the shader program used by this QSGMaterialShader.
- */
-#endif
-
-/*!
- \fn void QSGMaterialShader::initialize()
-
- Reimplement this function to do one-time initialization when the
- shader program is compiled. The OpenGL shader program is compiled
- and linked, but not bound, when this function is called.
- */
-
-
-/*!
- This function is called by the scene graph to indicate that geometry is
- about to be rendered using this shader.
-
- State that is global for all uses of the shader, independent of the geometry
- that is being drawn, can be setup in this function.
- */
-
-void QSGMaterialShader::activate()
-{
-}
-
-
-
-/*!
- This function is called by the scene graph to indicate that geometry will
- no longer to be rendered using this shader.
- */
-
-void QSGMaterialShader::deactivate()
-{
-}
-
-
-
-/*!
- This function is called by the scene graph before geometry is rendered
- to make sure the shader is in the right state.
-
- The current rendering \a state is passed from the scene graph. If the state
- indicates that any state is dirty, the updateState implementation must
- update accordingly for the geometry to render correctly.
-
- The subclass specific state, such as the color of a flat color material, should
- be extracted from \a newMaterial to update the color uniforms accordingly.
-
- The \a oldMaterial can be used to minimze state changes when updating
- material states. The \a oldMaterial is 0 if this shader was just activated.
-
- \sa activate(), deactivate()
- */
-
-void QSGMaterialShader::updateState(const RenderState & /* state */, QSGMaterial * /* newMaterial */, QSGMaterial * /* oldMaterial */)
-{
-}
-
-#if QT_CONFIG(opengl)
-/*!
- Sets the GLSL source file for the shader stage \a type to \a sourceFile. The
- default implementation of the vertexShader() and fragmentShader() functions
- will load the source files set by this function.
-
- This function is useful when you have a single source file for a given shader
- stage. If your shader consists of multiple source files then use
- setShaderSourceFiles()
-
- \sa setShaderSourceFiles(), vertexShader(), fragmentShader()
- */
-void QSGMaterialShader::setShaderSourceFile(QOpenGLShader::ShaderType type, const QString &sourceFile)
-{
- Q_D(QSGMaterialShader);
- d->m_sourceFiles[type] = (QStringList() << sourceFile);
-}
-
-/*!
- Sets the GLSL source files for the shader stage \a type to \a sourceFiles. The
- default implementation of the vertexShader() and fragmentShader() functions
- will load the source files set by this function in the order given.
-
- \sa setShaderSourceFile(), vertexShader(), fragmentShader()
- */
-void QSGMaterialShader::setShaderSourceFiles(QOpenGLShader::ShaderType type, const QStringList &sourceFiles)
-{
- Q_D(QSGMaterialShader);
- d->m_sourceFiles[type] = sourceFiles;
-}
-
-/*!
- This function is called when the shader is initialized to compile the
- actual QOpenGLShaderProgram. Do not call it explicitly.
-
- The default implementation will extract the vertexShader() and
- fragmentShader() and bind the names returned from attributeNames()
- to consecutive vertex attribute registers starting at 0.
- */
-
-void QSGMaterialShader::compile()
-{
- Q_ASSERT_X(!m_program.isLinked(), "QSGSMaterialShader::compile()", "Compile called multiple times!");
-
- program()->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader());
- program()->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader());
-
- char const *const *attr = attributeNames();
-#ifndef QT_NO_DEBUG
- int maxVertexAttribs = 0;
- QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
- funcs->glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
- for (int i = 0; attr[i]; ++i) {
- if (i >= maxVertexAttribs) {
- qFatal("List of attribute names is either too long or not null-terminated.\n"
- "Maximum number of attributes on this hardware is %i.\n"
- "Vertex shader:\n%s\n"
- "Fragment shader:\n%s\n",
- maxVertexAttribs, vertexShader(), fragmentShader());
- }
- if (*attr[i])
- program()->bindAttributeLocation(attr[i], i);
- }
-#else
- for (int i = 0; attr[i]; ++i) {
- if (*attr[i])
- program()->bindAttributeLocation(attr[i], i);
- }
-#endif
-
- if (!program()->link()) {
- qWarning("QSGMaterialShader: Shader compilation failed:");
- qWarning() << program()->log();
- }
-}
-
-#endif
-
-/*!
- \class QSGMaterialShader::RenderState
- \brief The QSGMaterialShader::RenderState encapsulates the current rendering state
- during a call to QSGMaterialShader::updateState().
- \inmodule QtQuick
-
- The render state contains a number of accessors that the shader needs to respect
- in order to conform to the current state of the scene graph.
-
- The instance is only valid inside a call to QSGMaterialShader::updateState() and
- should not be used outisde this function.
- */
-
-
-
-/*!
- \enum QSGMaterialShader::RenderState::DirtyState
-
- \value DirtyMatrix Used to indicate that the matrix has changed and must be updated.
-
- \value DirtyOpacity Used to indicate that the opacity has changed and must be updated.
-
- \value DirtyCachedMaterialData Used to indicate that the cached material data have changed and must be updated.
-
- \value DirtyAll Used to indicate that everything needs to be updated.
- */
-
-
-
-/*!
- \fn bool QSGMaterialShader::RenderState::isMatrixDirty() const
-
- Returns \c true if the dirtyStates() contain the dirty matrix state,
- otherwise returns \c false.
- */
-
-
-
-/*!
- \fn bool QSGMaterialShader::RenderState::isOpacityDirty() const
-
- Returns \c true if the dirtyStates() contains the dirty opacity state,
- otherwise returns \c false.
- */
-
-/*!
- \fn bool QSGMaterialShader::RenderState::isCachedMaterialDataDirty() const
-
- Returns \c true if the dirtyStates() contains the dirty cached material state,
- otherwise returns \c false.
- */
-
-/*!
- \fn QSGMaterialShader::RenderState::DirtyStates QSGMaterialShader::RenderState::dirtyStates() const
-
- Returns which rendering states that have changed and needs to be updated
- for geometry rendered with this material to conform to the current
- rendering state.
- */
-
-
-
-/*!
- Returns the accumulated opacity to be used for rendering.
- */
-
-float QSGMaterialShader::RenderState::opacity() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->currentOpacity();
-}
-
-/*!
- Returns the modelview determinant to be used for rendering.
- */
-
-float QSGMaterialShader::RenderState::determinant() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->determinant();
-}
-
-/*!
- Returns the matrix combined of modelview matrix and project matrix.
- */
-
-QMatrix4x4 QSGMaterialShader::RenderState::combinedMatrix() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix();
-}
-/*!
- Returns the ratio between physical pixels and device-independent pixels
- to be used for rendering.
-*/
-float QSGMaterialShader::RenderState::devicePixelRatio() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->devicePixelRatio();
-}
-
-
-
-/*!
- Returns the model view matrix.
-
- If the material has the RequiresFullMatrix flag
- set, this is guaranteed to be the complete transform
- matrix calculated from the scenegraph.
-
- However, if this flag is not set, the renderer may
- choose to alter this matrix. For example, it may
- pre-transform vertices on the CPU and set this matrix
- to identity.
-
- In a situation such as the above, it is still possible
- to retrieve the actual matrix determinant by setting
- the RequiresDeterminant flag in the material and
- calling the determinant() accessor.
- */
-
-QMatrix4x4 QSGMaterialShader::RenderState::modelViewMatrix() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->currentModelViewMatrix();
-}
-
-/*!
- Returns the projection matrix.
- */
-
-QMatrix4x4 QSGMaterialShader::RenderState::projectionMatrix() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->currentProjectionMatrix();
-}
-
-
-
-/*!
- Returns the viewport rect of the surface being rendered to.
- */
-
-QRect QSGMaterialShader::RenderState::viewportRect() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->viewportRect();
-}
-
-
-
-/*!
- Returns the device rect of the surface being rendered to
- */
-
-QRect QSGMaterialShader::RenderState::deviceRect() const
-{
- Q_ASSERT(m_data);
- return static_cast<const QSGRenderer *>(m_data)->deviceRect();
-}
-
-#if QT_CONFIG(opengl)
-
-/*!
- Returns the QOpenGLContext that is being used for rendering
- */
-
-QOpenGLContext *QSGMaterialShader::RenderState::context() const
-{
- // Only the QSGDefaultRenderContext will have an OpenGL Context to query
- auto openGLRenderContext = static_cast<const QSGDefaultRenderContext *>(static_cast<const QSGRenderer *>(m_data)->context());
- if (openGLRenderContext != nullptr)
- return openGLRenderContext->openglContext();
- else
- return nullptr;
-}
-
-#endif
-
#ifndef QT_NO_DEBUG
static int qt_material_count = 0;
@@ -605,22 +98,20 @@ static void qt_print_material_count()
\inmodule QtQuick
\ingroup qtquick-scenegraph-materials
- The QSGMaterial API is very low-level. A more convenient API, which
- provides almost all the same features, is available through
- QSGSimpleMaterialShader.
-
- The QSGMaterial and QSGMaterialShader subclasses form a tight relationship. For
- one scene graph (including nested graphs), there is one unique QSGMaterialShader
- instance which encapsulates the QOpenGLShaderProgram the scene graph uses
- to render that material, such as a shader to flat coloring of geometry.
- Each QSGGeometryNode can have a unique QSGMaterial containing the
- how the shader should be configured when drawing that node, such as
- the actual color to used to render the geometry.
-
- The QSGMaterial has two virtual functions that both need to be implemented.
- The function type() should return a unique instance for all instances of a
+ The QSGMaterial, QSGMaterialShader and QSGMaterialRhiShader subclasses
+ form a tight relationship. For one scene graph (including nested graphs),
+ there is one unique QSGMaterialShader or QSGMaterialRhiShader instance
+ which encapsulates the shaders the scene graph uses to render that
+ material, such as a shader to flat coloring of geometry. Each
+ QSGGeometryNode can have a unique QSGMaterial containing the how the shader
+ should be configured when drawing that node, such as the actual color to
+ used to render the geometry.
+
+ QSGMaterial has two virtual functions that both need to be implemented. The
+ function type() should return a unique instance for all instances of a
specific subclass. The createShader() function should return a new instance
- of QSGMaterialShader, specific to the subclass of QSGMaterial.
+ of QSGMaterialShader or QSGMaterialRhiShader, specific to that subclass of
+ QSGMaterial.
A minimal QSGMaterial implementation could look like this:
\code
@@ -632,6 +123,27 @@ static void qt_print_material_count()
};
\endcode
+ This is suitable only for the OpenGL-based, traditional renderer of the
+ scene graph. When using the new, graphics API abstracted renderer,
+ materials must create QSGMaterialRhiShader instances instead, or in
+ addition:
+ \code
+ class Material : public QSGMaterial
+ {
+ public:
+ Material() { setFlag(SupportsRhiShader, true); }
+ QSGMaterialType *type() const { static QSGMaterialType type; return &type; }
+ QSGMaterialShader *createShader() {
+ if (flags().testFlag(RhiShaderWanted)) {
+ return new RhiShader;
+ } else {
+ // this is optional, relevant for materials that intend to be usable with the legacy OpenGL renderer as well
+ return new Shader;
+ }
+ }
+ };
+ \endcode
+
\note All classes with QSG prefix should be used solely on the scene graph's
rendering thread. See \l {Scene Graph and Rendering} for more information.
*/
@@ -693,6 +205,16 @@ QSGMaterial::~QSGMaterial()
QSGMaterialShader::compile() when its shader program is compiled and linked.
Set this flag to enforce that the function is called.
+ \value SupportsRhiShader Starting with Qt 5.14, the scene graph supports
+ QSGMaterialRhiShader as an alternative to the OpenGL-specific
+ QSGMaterialShader. Set this flag to indicate createShader() is capable of
+ returning QSGMaterialRhiShader instances when the RhiShaderWanted flag is
+ set.
+
+ \value RhiShaderWanted This flag is set by the scene graph, not by the
+ QSGMaterial. When set, and that can only happen when SupportsRhiShader was
+ set by the material, it indicates that createShader() must return a
+ QSGMaterialRhiShader instance instead of QSGMaterialShader.
*/
/*!
@@ -757,7 +279,11 @@ int QSGMaterial::compare(const QSGMaterial *other) const
The function will be called only once for each material type that
exists in the scene graph and will be cached internally.
-*/
+ When the QSGMaterial reports SupportsRhiShader in flags(), the scene graph
+ may request a QSGMaterialRhiShader instead of QSGMaterialShader. This is
+ indicated by having the RhiShaderWanted flag set. In this case the return
+ value must be a QSGRhiMaterialShader subclass.
+*/
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.h b/src/quick/scenegraph/coreapi/qsgmaterial.h
index c002cd5d5e..cb6e9a456a 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterial.h
+++ b/src/quick/scenegraph/coreapi/qsgmaterial.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -41,95 +41,12 @@
#define QSGMATERIAL_H
#include <QtQuick/qtquickglobal.h>
-#if QT_CONFIG(opengl)
-# include <QtGui/qopenglshaderprogram.h>
-#endif
-#include <QtGui/QMatrix4x4>
-#include <QtCore/QRect>
+#include <QtQuick/qsgmaterialshader.h>
+#include <QtQuick/qsgmaterialrhishader.h>
+#include <QtQuick/qsgmaterialtype.h>
QT_BEGIN_NAMESPACE
-class QSGMaterial;
-class QSGMaterialShaderPrivate;
-
-namespace QSGBatchRenderer {
- class ShaderManager;
-}
-
-class Q_QUICK_EXPORT QSGMaterialShader
-{
-public:
- class Q_QUICK_EXPORT RenderState {
- public:
- enum DirtyState
- {
- DirtyMatrix = 0x0001,
- DirtyOpacity = 0x0002,
- DirtyCachedMaterialData = 0x0004,
- DirtyAll = 0xFFFF
- };
- Q_DECLARE_FLAGS(DirtyStates, DirtyState)
-
- inline DirtyStates dirtyStates() const { return m_dirty; }
-
- inline bool isMatrixDirty() const { return m_dirty & DirtyMatrix; }
- inline bool isOpacityDirty() const { return m_dirty & DirtyOpacity; }
- bool isCachedMaterialDataDirty() const { return m_dirty & DirtyCachedMaterialData; }
-
- float opacity() const;
- QMatrix4x4 combinedMatrix() const;
- QMatrix4x4 modelViewMatrix() const;
- QMatrix4x4 projectionMatrix() const;
- QRect viewportRect() const;
- QRect deviceRect() const;
- float determinant() const;
- float devicePixelRatio() const;
-#if QT_CONFIG(opengl)
- QOpenGLContext *context() const;
-#endif
- private:
- friend class QSGRenderer;
- DirtyStates m_dirty;
- const void *m_data;
- };
-
- QSGMaterialShader();
- virtual ~QSGMaterialShader();
-
- virtual void activate();
- virtual void deactivate();
- // First time a material is used, oldMaterial is null.
- virtual void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
- virtual char const *const *attributeNames() const = 0; // Array must end with null.
-#if QT_CONFIG(opengl)
- inline QOpenGLShaderProgram *program() { return &m_program; }
-#endif
-protected:
- Q_DECLARE_PRIVATE(QSGMaterialShader)
- QSGMaterialShader(QSGMaterialShaderPrivate &dd);
-
- friend class QSGDefaultRenderContext;
- friend class QSGBatchRenderer::ShaderManager;
-#if QT_CONFIG(opengl)
- void setShaderSourceFile(QOpenGLShader::ShaderType type, const QString &sourceFile);
- void setShaderSourceFiles(QOpenGLShader::ShaderType type, const QStringList &sourceFiles);
-
- virtual void compile();
-#endif
- virtual void initialize() { }
-#if QT_CONFIG(opengl)
- virtual const char *vertexShader() const;
- virtual const char *fragmentShader() const;
-#endif
-private:
-#if QT_CONFIG(opengl)
- QOpenGLShaderProgram m_program;
-#endif
- QScopedPointer<QSGMaterialShaderPrivate> d_ptr;
-};
-
-struct QSGMaterialType { };
-
class Q_QUICK_EXPORT QSGMaterial
{
public:
@@ -139,7 +56,11 @@ public:
RequiresFullMatrixExceptTranslate = 0x0004 | RequiresDeterminant, // Allow precalculated translation
RequiresFullMatrix = 0x0008 | RequiresFullMatrixExceptTranslate,
- CustomCompileStep = 0x0010
+ CustomCompileStep = 0x0010,
+
+ SupportsRhiShader = 0x0020,
+
+ RhiShaderWanted = 0x1000 // // ### Qt 6: remove
};
Q_DECLARE_FLAGS(Flags, Flag)
@@ -160,7 +81,6 @@ private:
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterial::Flags)
-Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterialShader::RenderState::DirtyStates)
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp b/src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp
new file mode 100644
index 0000000000..117d477f9a
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgmaterialrhishader.cpp
@@ -0,0 +1,630 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "qsgmaterial.h"
+#include "qsgrenderer_p.h"
+#include "qsgmaterialrhishader_p.h"
+#include <QtCore/QFile>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QSGMaterialRhiShader
+ \brief The QSGMaterialRhiShader class represents a graphics API independent shader program.
+ \inmodule QtQuick
+ \ingroup qtquick-scenegraph-materials
+ \since 5.14
+
+ QSGMaterialRhiShader is a modern, cross-platform alternative to
+ QSGMaterialShader. The latter is tied to OpenGL and GLSL by design, whereas
+ QSGMaterialRhiShader is based on QShader, a container for multiple
+ versions of a graphics shader together with reflection information.
+
+ \note All classes with QSG prefix should be used solely on the scene graph's
+ rendering thread. See \l {Scene Graph and Rendering} for more information.
+ */
+
+/*!
+ \enum QSGMaterialRhiShader::Flag
+ Flag values to indicate special material properties.
+
+ \value UpdatesGraphicsPipelineState Setting this flag enables calling
+ updateGraphicsPipelineState().
+ */
+
+QShader QSGMaterialRhiShaderPrivate::loadShader(const QString &filename)
+{
+ QFile f(filename);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning() << "Failed to find shader" << filename;
+ return QShader();
+ }
+ return QShader::fromSerialized(f.readAll());
+}
+
+void QSGMaterialRhiShaderPrivate::clearCachedRendererData()
+{
+ for (int i = 0; i < MAX_SHADER_RESOURCE_BINDINGS; ++i)
+ textureBindingTable[i] = nullptr;
+ for (int i = 0; i < MAX_SHADER_RESOURCE_BINDINGS; ++i)
+ samplerBindingTable[i] = nullptr;
+}
+
+static inline QRhiShaderResourceBinding::StageFlags toSrbStage(QShader::Stage stage)
+{
+ switch (stage) {
+ case QShader::VertexStage:
+ return QRhiShaderResourceBinding::VertexStage;
+ case QShader::FragmentStage:
+ return QRhiShaderResourceBinding::FragmentStage;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return 0;
+}
+
+void QSGMaterialRhiShaderPrivate::prepare(QShader::Variant vertexShaderVariant)
+{
+ ubufBinding = -1;
+ ubufSize = 0;
+ ubufStages = 0;
+ memset(combinedImageSamplerBindings, 0, sizeof(combinedImageSamplerBindings));
+ vertexShader = fragmentShader = nullptr;
+ masterUniformData.clear();
+
+ clearCachedRendererData();
+
+ for (QShader::Stage stage : { QShader::VertexStage, QShader::FragmentStage }) {
+ auto it = shaderFileNames.find(stage);
+ if (it != shaderFileNames.end()) {
+ QString fn = *it;
+ const QShader s = loadShader(*it);
+ if (!s.isValid())
+ continue;
+ shaders[stage] = ShaderStageData(s);
+ // load only once, subsequent prepare() calls will have it all in shaders already
+ shaderFileNames.erase(it);
+ }
+ }
+
+ auto vsIt = shaders.find(QShader::VertexStage);
+ if (vsIt != shaders.end()) {
+ vsIt->shaderVariant = vertexShaderVariant;
+ vsIt->vertexInputLocations.clear();
+ vsIt->qt_order_attrib_location = -1;
+
+ const QShaderDescription desc = vsIt->shader.description();
+ const QVector<QShaderDescription::InOutVariable> vertexInputs = desc.inputVariables();
+ for (const QShaderDescription::InOutVariable &v : vertexInputs) {
+ const QByteArray name = v.name.toUtf8();
+ if (vertexShaderVariant == QShader::BatchableVertexShader
+ && name == QByteArrayLiteral("_qt_order"))
+ {
+ vsIt->qt_order_attrib_location = v.location;
+ } else {
+ vsIt->vertexInputLocations.append(v.location);
+ }
+ }
+
+ if (vsIt->vertexInputLocations.contains(vsIt->qt_order_attrib_location)) {
+ qWarning("Vertex input clash in rewritten (batchable) vertex shader at input location %d. "
+ "Vertex shaders must avoid using this location.", vsIt->qt_order_attrib_location);
+ }
+ }
+
+ for (auto it = shaders.begin(); it != shaders.end(); ++it) {
+ const QShaderDescription desc = it->shader.description();
+
+ const QVector<QShaderDescription::UniformBlock> ubufs = desc.uniformBlocks();
+ const int ubufCount = ubufs.count();
+ if (ubufCount > 1) {
+ qWarning("Multiple uniform blocks found in shader. "
+ "This should be avoided as Qt Quick supports only one.");
+ }
+ for (int i = 0; i < ubufCount; ++i) {
+ const QShaderDescription::UniformBlock &ubuf(ubufs[i]);
+ if (ubufBinding == -1 && ubuf.binding >= 0) {
+ ubufBinding = ubuf.binding;
+ ubufSize = ubuf.size;
+ ubufStages |= toSrbStage(it->shader.stage());
+ masterUniformData.fill('\0', ubufSize);
+ } else if (ubufBinding == ubuf.binding && ubuf.binding >= 0) {
+ if (ubuf.size > ubufSize) {
+ ubufSize = ubuf.size;
+ masterUniformData.fill('\0', ubufSize);
+ }
+ ubufStages |= toSrbStage(it->shader.stage());
+ } else {
+ qWarning("Uniform block %s (binding %d) ignored", qPrintable(ubuf.blockName), ubuf.binding);
+ }
+ }
+
+ const QVector<QShaderDescription::InOutVariable> imageSamplers = desc.combinedImageSamplers();
+ const int imageSamplersCount = imageSamplers.count();
+ for (int i = 0; i < imageSamplersCount; ++i) {
+ const QShaderDescription::InOutVariable &var(imageSamplers[i]);
+ if (var.binding >= 0 && var.binding < MAX_SHADER_RESOURCE_BINDINGS)
+ combinedImageSamplerBindings[var.binding] |= toSrbStage(it->shader.stage());
+ else
+ qWarning("Encountered invalid combined image sampler (%s) binding %d",
+ qPrintable(var.name), var.binding);
+ }
+
+ if (it.key() == QShader::VertexStage)
+ vertexShader = &it.value();
+ else if (it.key() == QShader::FragmentStage)
+ fragmentShader = &it.value();
+ }
+
+ if (vertexShader && vertexShaderVariant == QShader::BatchableVertexShader && vertexShader->qt_order_attrib_location == -1)
+ qWarning("No rewriter-inserted attribute found, this should not happen.");
+}
+
+/*!
+ Constructs a new QSGMaterialRhiShader.
+ */
+QSGMaterialRhiShader::QSGMaterialRhiShader()
+ : d_ptr(new QSGMaterialRhiShaderPrivate(this))
+{
+}
+
+/*!
+ \internal
+ */
+QSGMaterialRhiShader::QSGMaterialRhiShader(QSGMaterialRhiShaderPrivate &dd)
+ : d_ptr(&dd)
+{
+}
+
+/*!
+ \internal
+ */
+QSGMaterialRhiShader::~QSGMaterialRhiShader()
+{
+}
+
+// We have our own enum as QShader is not initially public. Internally
+// everything works with QShader::Stage however. So convert.
+static inline QShader::Stage toShaderStage(QSGMaterialRhiShader::Stage stage)
+{
+ switch (stage) {
+ case QSGMaterialRhiShader::VertexStage:
+ return QShader::VertexStage;
+ case QSGMaterialRhiShader::FragmentStage:
+ return QShader::FragmentStage;
+ default:
+ Q_UNREACHABLE();
+ return QShader::VertexStage;
+ }
+}
+
+/*!
+ Sets the \a shader for the specified \a stage.
+ */
+void QSGMaterialRhiShader::setShader(Stage stage, const QShader &shader)
+{
+ Q_D(QSGMaterialRhiShader);
+ d->shaders[toShaderStage(stage)] = QSGMaterialRhiShaderPrivate::ShaderStageData(shader);
+}
+
+/*!
+ Sets the \a filename for the shader for the specified \a stage.
+
+ The file is expected to contain a serialized QRhiShader.
+ */
+void QSGMaterialRhiShader::setShaderFileName(Stage stage, const QString &filename)
+{
+ Q_D(QSGMaterialRhiShader);
+ d->shaderFileNames[toShaderStage(stage)] = filename;
+}
+
+/*!
+ \return the currently set flags for this material shader.
+ */
+QSGMaterialRhiShader::Flags QSGMaterialRhiShader::flags() const
+{
+ Q_D(const QSGMaterialRhiShader);
+ return d->flags;
+}
+
+/*!
+ Sets the \a flags on this material shader if \a on is true;
+ otherwise clears the specified flags.
+*/
+void QSGMaterialRhiShader::setFlag(Flags flags, bool on)
+{
+ Q_D(QSGMaterialRhiShader);
+ if (on)
+ d->flags |= flags;
+ else
+ d->flags &= ~flags;
+}
+
+/*!
+ This function is called by the scene graph to get the contents of the
+ shader program's uniform buffer updated. The implementation is not expected
+ to perform any real graphics operations, it is merely responsible for
+ copying data to the QByteArray returned from RenderState::uniformData().
+ The scene graph takes care of making that buffer visible in the shaders.
+
+ The current rendering \a state is passed from the scene graph. If the state
+ indicates that any relevant state is dirty, the implementation must update
+ the appropriate region in the buffer data that is accessible via
+ RenderState::uniformData(). When a state, such as, matrix or opacity, is
+ not dirty, there is no need to touch the corresponding region since the
+ data is persistent.
+
+ The return value must be \c true whenever any change was made to the uniform data.
+
+ The subclass specific state, such as the color of a flat color material,
+ should be extracted from \a newMaterial to update the relevant regions in
+ the buffer accordingly.
+
+ \a oldMaterial can be used to minimize buffer changes (which are typically
+ memcpy calls) when updating material states. When \a oldMaterial is null,
+ this shader was just activated.
+ */
+bool QSGMaterialRhiShader::updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial,
+ QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(state);
+ Q_UNUSED(newMaterial);
+ Q_UNUSED(oldMaterial);
+ return false;
+}
+
+/*!
+ This function is called by the scene graph to prepare using a sampled image
+ in the shader, typically in form of a combined image sampler.
+
+ \a binding is the binding number of the sampler. The function is called for
+ each variable in the material's shaders'
+ \l{QShaderDescription::combinedImageSamplers()}.
+
+ When \c{*texture} is null, it must be set to a QSGTexture pointer before
+ returning. When non-null, it is up to the material to decide if a new
+ \c{QSGTexture *} is stored to it, or if it updates some parameters on the
+ already known QSGTexture. The ownership of the QSGTexture is not
+ transferred.
+
+ The current rendering \a state is passed from the scene graph. It is up to
+ the material to enqueue the texture data uploads to the
+ QRhiResourceUpdateBatch retriveable via RenderState::resourceUpdateBatch().
+
+ The subclass specific state can be extracted from \a newMaterial.
+
+ \a oldMaterial can be used to minimize changes. When \a oldMaterial is null,
+ this shader was just activated.
+ */
+void QSGMaterialRhiShader::updateSampledImage(RenderState &state,
+ int binding,
+ QSGTexture **texture,
+ QSGMaterial *newMaterial,
+ QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(state);
+ Q_UNUSED(binding);
+ Q_UNUSED(texture);
+ Q_UNUSED(newMaterial);
+ Q_UNUSED(oldMaterial);
+}
+
+/*!
+ This function is called by the scene graph to enable the material to
+ provide a custom set of graphics state. The set of states that are
+ customizable by material is limited to blending and related settings.
+
+ \note This function is only called when the UpdatesGraphicsPipelineState
+ flag was enabled via setFlags(). By default it is not set, and so this
+ function is never called.
+
+ The return value must be \c true whenever a change was made to any of the
+ members in \a ps.
+
+ \note The contents of \a ps is not persistent between invocations of this
+ function.
+
+ The current rendering \a state is passed from the scene graph.
+
+ The subclass specific state can be extracted from \a newMaterial. When \a
+ oldMaterial is null, this shader was just activated.
+ */
+bool QSGMaterialRhiShader::updateGraphicsPipelineState(RenderState &state, GraphicsPipelineState *ps,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(state);
+ Q_UNUSED(ps);
+ Q_UNUSED(newMaterial);
+ Q_UNUSED(oldMaterial);
+ return false;
+}
+
+/*!
+ \class QSGMaterialRhiShader::RenderState
+
+ \brief Encapsulates the current rendering state during a call to
+ QSGMaterialRhiShader::updateUniformData() and the other \c update type of
+ functions.
+
+ \inmodule QtQuick
+ \since 5.14
+
+ The render state contains a number of accessors that the shader needs to
+ respect in order to conform to the current state of the scene graph.
+ */
+
+/*!
+ \enum QSGMaterialRhiShader::RenderState::DirtyState
+
+ \value DirtyMatrix Used to indicate that the matrix has changed and must be
+ updated.
+
+ \value DirtyOpacity Used to indicate that the opacity has changed and must
+ be updated.
+
+ \value DirtyAll Used to indicate that everything needs to be updated.
+ */
+
+/*!
+ \fn bool QSGMaterialRhiShader::RenderState::isMatrixDirty() const
+
+ Returns \c true if the dirtyStates() contain the dirty matrix state,
+ otherwise returns \c false.
+ */
+
+/*!
+ \fn bool QSGMaterialRhiShader::RenderState::isOpacityDirty() const
+
+ Returns \c true if the dirtyStates() contains the dirty opacity state,
+ otherwise returns \c false.
+ */
+
+/*!
+ \fn QSGMaterialRhiShader::RenderState::DirtyStates QSGMaterialRhiShader::RenderState::dirtyStates() const
+
+ Returns which rendering states that have changed and needs to be updated
+ for geometry rendered with this material to conform to the current
+ rendering state.
+ */
+
+/*!
+ \class QSGMaterialRhiShader::GraphicsPipelineState
+
+ \brief Describes state changes that the material wants to apply to the
+ currently active graphics pipeline state.
+
+ \inmodule QtQuick
+ \since 5.14
+
+ Unlike QSGMaterialShader, directly issuing state change commands with the
+ underlying graphics API is not possible with QSGMaterialRhiShader. This is
+ mainly because the concept of individually changeable states is considered
+ deprecated and not supported with modern graphics APIs.
+
+ Therefore, it is up to QSGMaterialRhiShader to expose a data structure with
+ the set of supported states, which the material can change in its
+ updatePipelineState() implementation, if there is one. The scenegraph will
+ then internally apply these changes to the active graphics pipeline state,
+ then rolling them back as appropriate.
+ */
+
+/*!
+ \enum QSGMaterialRhiShader::GraphicsPipelineState::BlendFactor
+ \since 5.14
+
+ \value Zero
+ \value One
+ \value SrcColor
+ \value OneMinusSrcColor
+ \value DstColor
+ \value OneMinusDstColor
+ \value SrcAlpha
+ \value OneMinusSrcAlpha
+ \value DstAlpha
+ \value OneMinusDstAlpha
+ \value ConstantColor
+ \value OneMinusConstantColor
+ \value ConstantAlpha
+ \value OneMinusConstantAlpha
+ \value SrcAlphaSaturate
+ \value Src1Color
+ \value OneMinusSrc1Color
+ \value Src1Alpha
+ \value OneMinusSrc1Alpha
+ */
+
+/*!
+ \enum QSGMaterialRhiShader::GraphicsPipelineState::ColorMaskComponent
+ \since 5.14
+
+ \value R
+ \value G
+ \value B
+ \value A
+ */
+
+/*!
+ \enum QSGMaterialRhiShader::GraphicsPipelineState::CullMode
+ \since 5.14
+
+ \value CullNone
+ \value CullFront
+ \value CullBack
+ */
+
+/*!
+ Returns the accumulated opacity to be used for rendering.
+ */
+float QSGMaterialRhiShader::RenderState::opacity() const
+{
+ Q_ASSERT(m_data);
+ return float(static_cast<const QSGRenderer *>(m_data)->currentOpacity());
+}
+
+/*!
+ Returns the modelview determinant to be used for rendering.
+ */
+float QSGMaterialRhiShader::RenderState::determinant() const
+{
+ Q_ASSERT(m_data);
+ return float(static_cast<const QSGRenderer *>(m_data)->determinant());
+}
+
+/*!
+ Returns the matrix combined of modelview matrix and project matrix.
+ */
+QMatrix4x4 QSGMaterialRhiShader::RenderState::combinedMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix();
+}
+
+/*!
+ Returns the ratio between physical pixels and device-independent pixels
+ to be used for rendering.
+*/
+float QSGMaterialRhiShader::RenderState::devicePixelRatio() const
+{
+ Q_ASSERT(m_data);
+ return float(static_cast<const QSGRenderer *>(m_data)->devicePixelRatio());
+}
+
+/*!
+ Returns the model view matrix.
+
+ If the material has the RequiresFullMatrix flag set, this is guaranteed to
+ be the complete transform matrix calculated from the scenegraph.
+
+ However, if this flag is not set, the renderer may choose to alter this
+ matrix. For example, it may pre-transform vertices on the CPU and set this
+ matrix to identity.
+
+ In a situation such as the above, it is still possible to retrieve the
+ actual matrix determinant by setting the RequiresDeterminant flag in the
+ material and calling the determinant() accessor.
+ */
+QMatrix4x4 QSGMaterialRhiShader::RenderState::modelViewMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentModelViewMatrix();
+}
+
+/*!
+ Returns the projection matrix.
+ */
+QMatrix4x4 QSGMaterialRhiShader::RenderState::projectionMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentProjectionMatrix();
+}
+
+/*!
+ Returns the viewport rect of the surface being rendered to.
+ */
+QRect QSGMaterialRhiShader::RenderState::viewportRect() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->viewportRect();
+}
+
+/*!
+ Returns the device rect of the surface being rendered to
+ */
+QRect QSGMaterialRhiShader::RenderState::deviceRect() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->deviceRect();
+}
+
+/*!
+ Returns a pointer to the data for the uniform (constant) buffer in the
+ shader. Uniform data must only be updated from
+ QSGMaterialRhiShader::updateUniformData(). The return value is null in the
+ other reimplementable functions, such as,
+ QSGMaterialRhiShader::updateSampledImage().
+
+ \note It is strongly recommended to declare the uniform block with \c
+ std140 in the shader, and to carefully study the standard uniform block
+ layout as described in section 7.6.2.2 of the OpenGL specification. It is
+ up to the QSGMaterialRhiShader implementation to ensure data gets placed
+ at the right location in this QByteArray, taking alignment requirements
+ into account. Shader code translated to other shading languages is expected
+ to use the same offsets for block members, even when the target language
+ uses different packing rules by default.
+
+ \note Avoid copying from C++ POD types, such as, structs, in order to
+ update multiple members at once, unless it has been verified that the
+ layouts of the C++ struct and the GLSL uniform block match.
+ */
+QByteArray *QSGMaterialRhiShader::RenderState::uniformData()
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentUniformData();
+}
+
+/*!
+ Returns a resource update batch to which upload and copy operatoins can be
+ queued. This is typically used by
+ QSGMaterialRhiShader::updateSampledImage() to enqueue texture image
+ content updates.
+ */
+QRhiResourceUpdateBatch *QSGMaterialRhiShader::RenderState::resourceUpdateBatch()
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentResourceUpdateBatch();
+}
+
+/*!
+ Returns the current QRhi.
+ */
+QRhi *QSGMaterialRhiShader::RenderState::rhi()
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentRhi();
+}
+
+char const *const *QSGMaterialRhiShader::attributeNames() const
+{
+ Q_ASSERT_X(false, "QSGMaterialRhiShader::attributeNames()", "Not implemented for RHI");
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialrhishader.h b/src/quick/scenegraph/coreapi/qsgmaterialrhishader.h
new file mode 100644
index 0000000000..86208516cd
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgmaterialrhishader.h
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 QSGMATERIALRHISHADER_H
+#define QSGMATERIALRHISHADER_H
+
+#include <QtQuick/qtquickglobal.h>
+#include <QtCore/QRect>
+#include <QtGui/QMatrix4x4>
+#include <QtGui/QColor>
+#include <QtQuick/qsgmaterialshader.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGMaterial;
+class QSGMaterialRhiShaderPrivate;
+class QSGTexture;
+class QRhiResourceUpdateBatch;
+class QRhi;
+class QShader;
+
+class Q_QUICK_EXPORT QSGMaterialRhiShader : public QSGMaterialShader // ### Qt 6: remove inheritance
+{
+public:
+ class Q_QUICK_EXPORT RenderState {
+ public:
+ using DirtyStates = QSGMaterialShader::RenderState::DirtyStates;
+
+ inline DirtyStates dirtyStates() const { return m_dirty; }
+
+ inline bool isMatrixDirty() const { return m_dirty & QSGMaterialShader::RenderState::DirtyMatrix; }
+ inline bool isOpacityDirty() const { return m_dirty & QSGMaterialShader::RenderState::DirtyOpacity; }
+
+ float opacity() const;
+ QMatrix4x4 combinedMatrix() const;
+ QMatrix4x4 modelViewMatrix() const;
+ QMatrix4x4 projectionMatrix() const;
+ QRect viewportRect() const;
+ QRect deviceRect() const;
+ float determinant() const;
+ float devicePixelRatio() const;
+
+ QByteArray *uniformData();
+ QRhiResourceUpdateBatch *resourceUpdateBatch();
+ QRhi *rhi();
+
+ private:
+ friend class QSGRenderer;
+ DirtyStates m_dirty;
+ const void *m_data;
+ };
+
+ struct Q_QUICK_EXPORT GraphicsPipelineState {
+ enum BlendFactor {
+ Zero,
+ One,
+ SrcColor,
+ OneMinusSrcColor,
+ DstColor,
+ OneMinusDstColor,
+ SrcAlpha,
+ OneMinusSrcAlpha,
+ DstAlpha,
+ OneMinusDstAlpha,
+ ConstantColor,
+ OneMinusConstantColor,
+ ConstantAlpha,
+ OneMinusConstantAlpha,
+ SrcAlphaSaturate,
+ Src1Color,
+ OneMinusSrc1Color,
+ Src1Alpha,
+ OneMinusSrc1Alpha
+ };
+
+ enum ColorMaskComponent {
+ R = 1 << 0,
+ G = 1 << 1,
+ B = 1 << 2,
+ A = 1 << 3
+ };
+ Q_DECLARE_FLAGS(ColorMask, ColorMaskComponent)
+
+ enum CullMode {
+ CullNone,
+ CullFront,
+ CullBack
+ };
+
+ bool blendEnable;
+ BlendFactor srcColor;
+ BlendFactor dstColor;
+ ColorMask colorWrite;
+ QColor blendConstant;
+ CullMode cullMode;
+ // This struct is extensible while keeping BC since apps only ever get
+ // a ptr to the struct, it is not created by them.
+ };
+
+ enum Flag {
+ UpdatesGraphicsPipelineState = 0x0001
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ enum Stage {
+ VertexStage,
+ FragmentStage,
+ };
+
+ QSGMaterialRhiShader();
+ virtual ~QSGMaterialRhiShader();
+
+ virtual bool updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
+
+ virtual void updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
+
+ virtual bool updateGraphicsPipelineState(RenderState &state, GraphicsPipelineState *ps,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
+
+ Flags flags() const;
+ void setFlag(Flags flags, bool on = true);
+
+ // dummy impl for base class pure virtual, never called
+ char const *const *attributeNames() const override;
+
+protected:
+ Q_DECLARE_PRIVATE(QSGMaterialRhiShader)
+ QSGMaterialRhiShader(QSGMaterialRhiShaderPrivate &dd);
+
+ // filename is for a file containing a serialized QShader.
+ void setShaderFileName(Stage stage, const QString &filename);
+
+ void setShader(Stage stage, const QShader &shader);
+
+private:
+ QScopedPointer<QSGMaterialRhiShaderPrivate> d_ptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterialRhiShader::GraphicsPipelineState::ColorMask)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterialRhiShader::Flags)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialrhishader_p.h b/src/quick/scenegraph/coreapi/qsgmaterialrhishader_p.h
new file mode 100644
index 0000000000..153b4b120a
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgmaterialrhishader_p.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 QSGMATERIALRHISHADER_P_H
+#define QSGMATERIALRHISHADER_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/qtquickglobal_p.h>
+#include "qsgmaterialrhishader.h"
+#include "qsgmaterial.h"
+#include <QtGui/private/qrhi_p.h>
+#include <QtGui/private/qshader_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRhiSampler;
+
+class Q_QUICK_PRIVATE_EXPORT QSGMaterialRhiShaderPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QSGMaterialRhiShader)
+
+ QSGMaterialRhiShaderPrivate(QSGMaterialRhiShader *q) : q_ptr(q) { }
+ static QSGMaterialRhiShaderPrivate *get(QSGMaterialRhiShader *s) { return s->d_func(); }
+ static const QSGMaterialRhiShaderPrivate *get(const QSGMaterialRhiShader *s) { return s->d_func(); }
+
+ void clearCachedRendererData();
+ void prepare(QShader::Variant vertexShaderVariant);
+
+ QShader shader(QShader::Stage stage) const { return shaders[stage].shader; }
+
+ static QShader loadShader(const QString &filename);
+
+ QSGMaterialRhiShader *q_ptr;
+ QHash<QShader::Stage, QString> shaderFileNames;
+ QSGMaterialRhiShader::Flags flags;
+
+ struct ShaderStageData {
+ ShaderStageData() { } // so shader.isValid() == false
+ ShaderStageData(const QShader &shader) : shader(shader) { }
+ QShader shader;
+ QShader::Variant shaderVariant = QShader::StandardShader;
+ QVector<int> vertexInputLocations; // excluding rewriter-inserted ones
+ int qt_order_attrib_location = -1; // rewriter-inserted
+ };
+ QHash<QShader::Stage, ShaderStageData> shaders;
+
+ static const int MAX_SHADER_RESOURCE_BINDINGS = 32;
+
+ int ubufBinding = -1;
+ int ubufSize = 0;
+ QRhiShaderResourceBinding::StageFlags ubufStages;
+ QRhiShaderResourceBinding::StageFlags combinedImageSamplerBindings[MAX_SHADER_RESOURCE_BINDINGS];
+
+ ShaderStageData *vertexShader = nullptr;
+ ShaderStageData *fragmentShader = nullptr;
+
+ QByteArray masterUniformData;
+
+ QSGTexture *textureBindingTable[MAX_SHADER_RESOURCE_BINDINGS];
+ QRhiSampler *samplerBindingTable[MAX_SHADER_RESOURCE_BINDINGS];
+};
+
+Q_DECLARE_TYPEINFO(QSGMaterialRhiShaderPrivate::ShaderStageData, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif // QSGMATERIALRHISHADER_P_H
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp b/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp
new file mode 100644
index 0000000000..d614f9be4c
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgmaterialshader.cpp
@@ -0,0 +1,556 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "qsgmaterial.h"
+#include "qsgrenderer_p.h"
+#include "qsgmaterialshader_p.h"
+#if QT_CONFIG(opengl)
+# include <private/qsgshadersourcebuilder_p.h>
+# include <private/qsgdefaultcontext_p.h>
+# include <private/qsgdefaultrendercontext_p.h>
+# include <QtGui/QOpenGLFunctions>
+# include <QtGui/QOpenGLContext>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if QT_CONFIG(opengl)
+const char *QSGMaterialShaderPrivate::loadShaderSource(QOpenGLShader::ShaderType type) const
+{
+ const QStringList files = m_sourceFiles[type];
+ QSGShaderSourceBuilder builder;
+ for (const QString &file : files)
+ builder.appendSourceFile(file);
+ m_sources[type] = builder.source();
+ return m_sources[type].constData();
+}
+#endif
+
+/*!
+ \class QSGMaterialShader
+ \brief The QSGMaterialShader class represents an OpenGL shader program
+ in the renderer.
+ \inmodule QtQuick
+ \ingroup qtquick-scenegraph-materials
+
+ The QSGMaterialShader API is relatively low-level. A more convenient API,
+ which provides almost all the same features, is available through
+ QSGSimpleMaterialShader.
+
+ \warning This class is only functional when running with the legacy OpenGL
+ renderer of the Qt Quick scenegraph.
+
+ The QSGMaterial and QSGMaterialShader form a tight relationship. For one
+ scene graph (including nested graphs), there is one unique QSGMaterialShader
+ instance which encapsulates the QOpenGLShaderProgram the scene graph uses
+ to render that material, such as a shader to flat coloring of geometry.
+ Each QSGGeometryNode can have a unique QSGMaterial containing the
+ how the shader should be configured when drawing that node, such as
+ the actual color used to render the geometry.
+
+ An instance of QSGMaterialShader is never created explicitly by the user,
+ it will be created on demand by the scene graph through
+ QSGMaterial::createShader(). The scene graph will make sure that there
+ is only one instance of each shader implementation through a scene graph.
+
+ The source code returned from vertexShader() is used to control what the
+ material does with the vertiex data that comes in from the geometry.
+ The source code returned from the fragmentShader() is used to control
+ what how the material should fill each individual pixel in the geometry.
+ The vertex and fragment source code is queried once during initialization,
+ changing what is returned from these functions later will not have
+ any effect.
+
+ The activate() function is called by the scene graph when a shader is
+ is starting to be used. The deactivate function is called by the scene
+ graph when the shader is no longer going to be used. While active,
+ the scene graph may make one or more calls to updateState() which
+ will update the state of the shader for each individual geometry to
+ render.
+
+ The attributeNames() returns the name of the attributes used in the
+ vertexShader(). These are used in the default implementation of
+ activate() and deactivate() to decide whice vertex registers are enabled.
+
+ The initialize() function is called during program creation to allow
+ subclasses to prepare for use, such as resolve uniform names in the
+ vertexShader() and fragmentShader().
+
+ A minimal example:
+ \code
+ class Shader : public QSGMaterialShader
+ {
+ public:
+ const char *vertexShader() const {
+ return
+ "attribute highp vec4 vertex; \n"
+ "uniform highp mat4 matrix; \n"
+ "void main() { \n"
+ " gl_Position = matrix * vertex; \n"
+ "}";
+ }
+
+ const char *fragmentShader() const {
+ return
+ "uniform lowp float opacity; \n"
+ "void main() { \n"
+ " gl_FragColor = vec4(1, 0, 0, 1) * opacity; \n"
+ "}";
+ }
+
+ char const *const *attributeNames() const
+ {
+ static char const *const names[] = { "vertex", 0 };
+ return names;
+ }
+
+ void initialize()
+ {
+ QSGMaterialShader::initialize();
+ m_id_matrix = program()->uniformLocation("matrix");
+ m_id_opacity = program()->uniformLocation("opacity");
+ }
+
+ void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+ {
+ Q_ASSERT(program()->isLinked());
+ if (state.isMatrixDirty())
+ program()->setUniformValue(m_id_matrix, state.combinedMatrix());
+ if (state.isOpacityDirty())
+ program()->setUniformValue(m_id_opacity, state.opacity());
+ }
+
+ private:
+ int m_id_matrix;
+ int m_id_opacity;
+ };
+ \endcode
+
+ \note All classes with QSG prefix should be used solely on the scene graph's
+ rendering thread. See \l {Scene Graph and Rendering} for more information.
+
+ */
+
+
+
+/*!
+ Creates a new QSGMaterialShader.
+ */
+QSGMaterialShader::QSGMaterialShader()
+ : d_ptr(new QSGMaterialShaderPrivate)
+{
+}
+
+/*!
+ \internal
+ */
+QSGMaterialShader::QSGMaterialShader(QSGMaterialShaderPrivate &dd)
+ : d_ptr(&dd)
+{
+}
+
+/*!
+ \internal
+ */
+QSGMaterialShader::~QSGMaterialShader()
+{
+}
+
+/*!
+ \fn char const *const *QSGMaterialShader::attributeNames() const
+
+ Returns a zero-terminated array describing the names of the
+ attributes used in the vertex shader.
+
+ This function is called when the shader is compiled to specify
+ which attributes exist. The order of the attribute names
+ defines the attribute register position in the vertex shader.
+ */
+
+#if QT_CONFIG(opengl)
+/*!
+ \fn const char *QSGMaterialShader::vertexShader() const
+
+ Called when the shader is being initialized to get the vertex
+ shader source code.
+
+ The contents returned from this function should never change.
+*/
+const char *QSGMaterialShader::vertexShader() const
+{
+ Q_D(const QSGMaterialShader);
+ return d->loadShaderSource(QOpenGLShader::Vertex);
+}
+
+
+/*!
+ \fn const char *QSGMaterialShader::fragmentShader() const
+
+ Called when the shader is being initialized to get the fragment
+ shader source code.
+
+ The contents returned from this function should never change.
+*/
+const char *QSGMaterialShader::fragmentShader() const
+{
+ Q_D(const QSGMaterialShader);
+ return d->loadShaderSource(QOpenGLShader::Fragment);
+}
+
+
+/*!
+ \fn QOpenGLShaderProgram *QSGMaterialShader::program()
+
+ Returns the shader program used by this QSGMaterialShader.
+ */
+#endif
+
+/*!
+ \fn void QSGMaterialShader::initialize()
+
+ Reimplement this function to do one-time initialization when the
+ shader program is compiled. The OpenGL shader program is compiled
+ and linked, but not bound, when this function is called.
+ */
+
+
+/*!
+ This function is called by the scene graph to indicate that geometry is
+ about to be rendered using this shader.
+
+ State that is global for all uses of the shader, independent of the geometry
+ that is being drawn, can be setup in this function.
+ */
+
+void QSGMaterialShader::activate()
+{
+}
+
+
+
+/*!
+ This function is called by the scene graph to indicate that geometry will
+ no longer to be rendered using this shader.
+ */
+
+void QSGMaterialShader::deactivate()
+{
+}
+
+
+
+/*!
+ This function is called by the scene graph before geometry is rendered
+ to make sure the shader is in the right state.
+
+ The current rendering \a state is passed from the scene graph. If the state
+ indicates that any state is dirty, the updateState implementation must
+ update accordingly for the geometry to render correctly.
+
+ The subclass specific state, such as the color of a flat color material, should
+ be extracted from \a newMaterial to update the color uniforms accordingly.
+
+ The \a oldMaterial can be used to minimze state changes when updating
+ material states. The \a oldMaterial is 0 if this shader was just activated.
+
+ \sa activate(), deactivate()
+ */
+
+void QSGMaterialShader::updateState(const RenderState & /* state */, QSGMaterial * /* newMaterial */, QSGMaterial * /* oldMaterial */)
+{
+}
+
+#if QT_CONFIG(opengl)
+/*!
+ Sets the GLSL source file for the shader stage \a type to \a sourceFile. The
+ default implementation of the vertexShader() and fragmentShader() functions
+ will load the source files set by this function.
+
+ This function is useful when you have a single source file for a given shader
+ stage. If your shader consists of multiple source files then use
+ setShaderSourceFiles()
+
+ \sa setShaderSourceFiles(), vertexShader(), fragmentShader()
+ */
+void QSGMaterialShader::setShaderSourceFile(QOpenGLShader::ShaderType type, const QString &sourceFile)
+{
+ Q_D(QSGMaterialShader);
+ d->m_sourceFiles[type] = (QStringList() << sourceFile);
+}
+
+/*!
+ Sets the GLSL source files for the shader stage \a type to \a sourceFiles. The
+ default implementation of the vertexShader() and fragmentShader() functions
+ will load the source files set by this function in the order given.
+
+ \sa setShaderSourceFile(), vertexShader(), fragmentShader()
+ */
+void QSGMaterialShader::setShaderSourceFiles(QOpenGLShader::ShaderType type, const QStringList &sourceFiles)
+{
+ Q_D(QSGMaterialShader);
+ d->m_sourceFiles[type] = sourceFiles;
+}
+
+/*!
+ This function is called when the shader is initialized to compile the
+ actual QOpenGLShaderProgram. Do not call it explicitly.
+
+ The default implementation will extract the vertexShader() and
+ fragmentShader() and bind the names returned from attributeNames()
+ to consecutive vertex attribute registers starting at 0.
+ */
+
+void QSGMaterialShader::compile()
+{
+ Q_ASSERT_X(!m_program.isLinked(), "QSGSMaterialShader::compile()", "Compile called multiple times!");
+
+ program()->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader());
+ program()->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader());
+
+ char const *const *attr = attributeNames();
+#ifndef QT_NO_DEBUG
+ int maxVertexAttribs = 0;
+ QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
+ funcs->glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
+ for (int i = 0; attr[i]; ++i) {
+ if (i >= maxVertexAttribs) {
+ qFatal("List of attribute names is either too long or not null-terminated.\n"
+ "Maximum number of attributes on this hardware is %i.\n"
+ "Vertex shader:\n%s\n"
+ "Fragment shader:\n%s\n",
+ maxVertexAttribs, vertexShader(), fragmentShader());
+ }
+ if (*attr[i])
+ program()->bindAttributeLocation(attr[i], i);
+ }
+#else
+ for (int i = 0; attr[i]; ++i) {
+ if (*attr[i])
+ program()->bindAttributeLocation(attr[i], i);
+ }
+#endif
+
+ if (!program()->link()) {
+ qWarning("QSGMaterialShader: Shader compilation failed:");
+ qWarning() << program()->log();
+ }
+}
+
+#endif
+
+/*!
+ \class QSGMaterialShader::RenderState
+ \brief The QSGMaterialShader::RenderState encapsulates the current rendering state
+ during a call to QSGMaterialShader::updateState().
+ \inmodule QtQuick
+
+ The render state contains a number of accessors that the shader needs to respect
+ in order to conform to the current state of the scene graph.
+
+ The instance is only valid inside a call to QSGMaterialShader::updateState() and
+ should not be used outisde this function.
+ */
+
+
+
+/*!
+ \enum QSGMaterialShader::RenderState::DirtyState
+
+ \value DirtyMatrix Used to indicate that the matrix has changed and must be updated.
+
+ \value DirtyOpacity Used to indicate that the opacity has changed and must be updated.
+
+ \value DirtyCachedMaterialData Used to indicate that the cached material data have changed and must be updated.
+
+ \value DirtyAll Used to indicate that everything needs to be updated.
+ */
+
+
+
+/*!
+ \fn bool QSGMaterialShader::RenderState::isMatrixDirty() const
+
+ Returns \c true if the dirtyStates() contain the dirty matrix state,
+ otherwise returns \c false.
+ */
+
+
+
+/*!
+ \fn bool QSGMaterialShader::RenderState::isOpacityDirty() const
+
+ Returns \c true if the dirtyStates() contains the dirty opacity state,
+ otherwise returns \c false.
+ */
+
+/*!
+ \fn bool QSGMaterialShader::RenderState::isCachedMaterialDataDirty() const
+
+ Returns \c true if the dirtyStates() contains the dirty cached material state,
+ otherwise returns \c false.
+ */
+
+/*!
+ \fn QSGMaterialShader::RenderState::DirtyStates QSGMaterialShader::RenderState::dirtyStates() const
+
+ Returns which rendering states that have changed and needs to be updated
+ for geometry rendered with this material to conform to the current
+ rendering state.
+ */
+
+
+
+/*!
+ Returns the accumulated opacity to be used for rendering.
+ */
+
+float QSGMaterialShader::RenderState::opacity() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentOpacity();
+}
+
+/*!
+ Returns the modelview determinant to be used for rendering.
+ */
+
+float QSGMaterialShader::RenderState::determinant() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->determinant();
+}
+
+/*!
+ Returns the matrix combined of modelview matrix and project matrix.
+ */
+
+QMatrix4x4 QSGMaterialShader::RenderState::combinedMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix();
+}
+/*!
+ Returns the ratio between physical pixels and device-independent pixels
+ to be used for rendering.
+*/
+float QSGMaterialShader::RenderState::devicePixelRatio() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->devicePixelRatio();
+}
+
+
+
+/*!
+ Returns the model view matrix.
+
+ If the material has the RequiresFullMatrix flag
+ set, this is guaranteed to be the complete transform
+ matrix calculated from the scenegraph.
+
+ However, if this flag is not set, the renderer may
+ choose to alter this matrix. For example, it may
+ pre-transform vertices on the CPU and set this matrix
+ to identity.
+
+ In a situation such as the above, it is still possible
+ to retrieve the actual matrix determinant by setting
+ the RequiresDeterminant flag in the material and
+ calling the determinant() accessor.
+ */
+
+QMatrix4x4 QSGMaterialShader::RenderState::modelViewMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentModelViewMatrix();
+}
+
+/*!
+ Returns the projection matrix.
+ */
+
+QMatrix4x4 QSGMaterialShader::RenderState::projectionMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentProjectionMatrix();
+}
+
+
+
+/*!
+ Returns the viewport rect of the surface being rendered to.
+ */
+
+QRect QSGMaterialShader::RenderState::viewportRect() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->viewportRect();
+}
+
+
+
+/*!
+ Returns the device rect of the surface being rendered to
+ */
+
+QRect QSGMaterialShader::RenderState::deviceRect() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->deviceRect();
+}
+
+#if QT_CONFIG(opengl)
+
+/*!
+ Returns the QOpenGLContext that is being used for rendering
+ */
+
+QOpenGLContext *QSGMaterialShader::RenderState::context() const
+{
+ // Only the QSGDefaultRenderContext will have an OpenGL Context to query
+ auto openGLRenderContext = static_cast<const QSGDefaultRenderContext *>(static_cast<const QSGRenderer *>(m_data)->context());
+ if (openGLRenderContext != nullptr)
+ return openGLRenderContext->openglContext();
+ else
+ return nullptr;
+}
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader.h b/src/quick/scenegraph/coreapi/qsgmaterialshader.h
new file mode 100644
index 0000000000..d7ee23384f
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgmaterialshader.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 QSGMATERIALSHADER_H
+#define QSGMATERIALSHADER_H
+
+#include <QtQuick/qtquickglobal.h>
+#if QT_CONFIG(opengl)
+# include <QtGui/qopenglshaderprogram.h>
+#endif
+#include <QtGui/QMatrix4x4>
+#include <QtCore/QRect>
+#include <QtQuick/qsgmaterialtype.h> // for source compat
+
+QT_BEGIN_NAMESPACE
+
+class QSGMaterial;
+class QSGMaterialShaderPrivate;
+
+namespace QSGBatchRenderer {
+ class ShaderManager;
+}
+
+class Q_QUICK_EXPORT QSGMaterialShader
+{
+public:
+ class Q_QUICK_EXPORT RenderState {
+ public:
+ enum DirtyState
+ {
+ DirtyMatrix = 0x0001,
+ DirtyOpacity = 0x0002,
+ DirtyCachedMaterialData = 0x0004,
+ DirtyAll = 0xFFFF
+ };
+ Q_DECLARE_FLAGS(DirtyStates, DirtyState)
+
+ inline DirtyStates dirtyStates() const { return m_dirty; }
+
+ inline bool isMatrixDirty() const { return m_dirty & DirtyMatrix; }
+ inline bool isOpacityDirty() const { return m_dirty & DirtyOpacity; }
+ bool isCachedMaterialDataDirty() const { return m_dirty & DirtyCachedMaterialData; }
+
+ float opacity() const;
+ QMatrix4x4 combinedMatrix() const;
+ QMatrix4x4 modelViewMatrix() const;
+ QMatrix4x4 projectionMatrix() const;
+ QRect viewportRect() const;
+ QRect deviceRect() const;
+ float determinant() const;
+ float devicePixelRatio() const;
+#if QT_CONFIG(opengl)
+ QOpenGLContext *context() const;
+#endif
+ private:
+ friend class QSGRenderer;
+ DirtyStates m_dirty;
+ const void *m_data;
+ };
+
+ QSGMaterialShader();
+ virtual ~QSGMaterialShader();
+
+ virtual void activate();
+ virtual void deactivate();
+ // First time a material is used, oldMaterial is null.
+ virtual void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
+ virtual char const *const *attributeNames() const = 0; // Array must end with null.
+#if QT_CONFIG(opengl)
+ inline QOpenGLShaderProgram *program() { return &m_program; }
+#endif
+protected:
+ Q_DECLARE_PRIVATE(QSGMaterialShader)
+ QSGMaterialShader(QSGMaterialShaderPrivate &dd);
+
+ friend class QSGDefaultRenderContext;
+ friend class QSGBatchRenderer::ShaderManager;
+#if QT_CONFIG(opengl)
+ void setShaderSourceFile(QOpenGLShader::ShaderType type, const QString &sourceFile);
+ void setShaderSourceFiles(QOpenGLShader::ShaderType type, const QStringList &sourceFiles);
+
+ virtual void compile();
+#endif
+ virtual void initialize() { }
+#if QT_CONFIG(opengl)
+ virtual const char *vertexShader() const;
+ virtual const char *fragmentShader() const;
+#endif
+private:
+#if QT_CONFIG(opengl)
+ QOpenGLShaderProgram m_program;
+#endif
+ QScopedPointer<QSGMaterialShaderPrivate> d_ptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterialShader::RenderState::DirtyStates)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h b/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h
index 47f5e5de09..ae23b4a8ce 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h
+++ b/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h
@@ -1,5 +1,6 @@
/****************************************************************************
**
+** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
** Contact: https://www.qt.io/licensing/
**
@@ -52,7 +53,7 @@
//
#include <private/qtquickglobal_p.h>
-#include <QOpenGLShader>
+#include "qsgmaterial.h"
QT_BEGIN_NAMESPACE
@@ -67,11 +68,6 @@ public:
#endif
};
-#ifndef QT_NO_DEBUG
-Q_QUICK_PRIVATE_EXPORT bool qsg_test_and_clear_material_failure();
-Q_QUICK_PRIVATE_EXPORT void qsg_set_material_failure();
-#endif
-
QT_END_NAMESPACE
#endif // QSGMATERIALSHADER_P_H
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialtype.h b/src/quick/scenegraph/coreapi/qsgmaterialtype.h
new file mode 100644
index 0000000000..15141c2d9e
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgmaterialtype.h
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 QSGMATERIALTYPE_H
+#define QSGMATERIALTYPE_H
+
+#include <QtQuick/qtquickglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QSGMaterialType { };
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp b/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp
new file mode 100644
index 0000000000..6c2ff0b176
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp
@@ -0,0 +1,365 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
+** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
+** 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 "qsgopenglvisualizer_p.h"
+#include <qmath.h>
+#include <private/qsgshadersourcebuilder_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSGBatchRenderer
+{
+
+#define QSGNODE_TRAVERSE(NODE) for (QSGNode *child = NODE->firstChild(); child; child = child->nextSibling())
+#define SHADOWNODE_TRAVERSE(NODE) for (Node *child = NODE->firstChild(); child; child = child->sibling())
+#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \
+ | QSGNode::DirtyOpacity \
+ | QSGNode::DirtyMatrix \
+ | QSGNode::DirtyNodeRemoved)
+
+QMatrix4x4 qsg_matrixForRoot(Node *node);
+
+class VisualizeShader : public QOpenGLShaderProgram
+{
+public:
+ int color;
+ int matrix;
+ int rotation;
+ int pattern;
+ int projection;
+};
+
+OpenGLVisualizer::OpenGLVisualizer(Renderer *renderer)
+ : Visualizer(renderer),
+ m_funcs(QOpenGLContext::currentContext()->functions()),
+ m_visualizeProgram(nullptr)
+{
+}
+
+OpenGLVisualizer::~OpenGLVisualizer()
+{
+ releaseResources();
+}
+
+void OpenGLVisualizer::releaseResources()
+{
+ delete m_visualizeProgram;
+ m_visualizeProgram = nullptr;
+}
+
+void OpenGLVisualizer::prepareVisualize()
+{
+ // nothing to do here
+}
+
+void OpenGLVisualizer::visualizeDrawGeometry(const QSGGeometry *g)
+{
+ if (g->attributeCount() < 1)
+ return;
+ const QSGGeometry::Attribute *a = g->attributes();
+ m_funcs->glVertexAttribPointer(0, a->tupleSize, a->type, false, g->sizeOfVertex(), g->vertexData());
+ if (g->indexCount())
+ m_funcs->glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
+ else
+ m_funcs->glDrawArrays(g->drawingMode(), 0, g->vertexCount());
+
+}
+
+void OpenGLVisualizer::visualizeBatch(Batch *b)
+{
+ VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
+
+ if (b->positionAttribute != 0)
+ return;
+
+ QSGGeometryNode *gn = b->first->node;
+ QSGGeometry *g = gn->geometry();
+ const QSGGeometry::Attribute &a = g->attributes()[b->positionAttribute];
+
+ m_funcs->glBindBuffer(GL_ARRAY_BUFFER, b->vbo.id);
+
+ QMatrix4x4 matrix(m_renderer->m_current_projection_matrix);
+ if (b->root)
+ matrix = matrix * qsg_matrixForRoot(b->root);
+
+ shader->setUniformValue(shader->pattern, float(b->merged ? 0 : 1));
+
+ QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 1.0, 1.0);
+ float cr = color.redF();
+ float cg = color.greenF();
+ float cb = color.blueF();
+ shader->setUniformValue(shader->color, cr, cg, cb, 1.0);
+
+ if (b->merged) {
+ shader->setUniformValue(shader->matrix, matrix);
+ const char *dataStart = m_renderer->m_context->separateIndexBuffer() ? b->ibo.data : b->vbo.data;
+ for (int ds=0; ds<b->drawSets.size(); ++ds) {
+ const DrawSet &set = b->drawSets.at(ds);
+ m_funcs->glVertexAttribPointer(a.position, 2, a.type, false, g->sizeOfVertex(),
+ (void *) (qintptr) (set.vertices));
+ m_funcs->glDrawElements(g->drawingMode(), set.indexCount, GL_UNSIGNED_SHORT,
+ (void *)(qintptr)(dataStart + set.indices));
+ }
+ } else {
+ Element *e = b->first;
+ int offset = 0;
+ while (e) {
+ gn = e->node;
+ g = gn->geometry();
+ shader->setUniformValue(shader->matrix, matrix * *gn->matrix());
+ m_funcs->glVertexAttribPointer(a.position, a.tupleSize, a.type, false, g->sizeOfVertex(),
+ (void *) (qintptr) offset);
+ if (g->indexCount())
+ m_funcs->glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
+ else
+ m_funcs->glDrawArrays(g->drawingMode(), 0, g->vertexCount());
+ offset += g->sizeOfVertex() * g->vertexCount();
+ e = e->nextInBatch;
+ }
+ }
+}
+
+void OpenGLVisualizer::visualizeClipping(QSGNode *node)
+{
+ if (node->type() == QSGNode::ClipNodeType) {
+ VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
+ QSGClipNode *clipNode = static_cast<QSGClipNode *>(node);
+ QMatrix4x4 matrix = m_renderer->m_current_projection_matrix;
+ if (clipNode->matrix())
+ matrix = matrix * *clipNode->matrix();
+ shader->setUniformValue(shader->matrix, matrix);
+ visualizeDrawGeometry(clipNode->geometry());
+ }
+
+ QSGNODE_TRAVERSE(node) {
+ visualizeClipping(child);
+ }
+}
+
+void OpenGLVisualizer::visualizeChanges(Node *n)
+{
+
+ if (n->type() == QSGNode::GeometryNodeType && n->element()->batch && m_visualizeChangeSet.contains(n)) {
+ uint dirty = m_visualizeChangeSet.value(n);
+ bool tinted = (dirty & QSGNODE_DIRTY_PARENT) != 0;
+
+ VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
+ QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 0.3, 1.0);
+ float ca = 0.5;
+ float cr = color.redF() * ca;
+ float cg = color.greenF() * ca;
+ float cb = color.blueF() * ca;
+ shader->setUniformValue(shader->color, cr, cg, cb, ca);
+ shader->setUniformValue(shader->pattern, float(tinted ? 0.5 : 0));
+
+ QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode);
+
+ QMatrix4x4 matrix = m_renderer->m_current_projection_matrix;
+ if (n->element()->batch->root)
+ matrix = matrix * qsg_matrixForRoot(n->element()->batch->root);
+ matrix = matrix * *gn->matrix();
+ shader->setUniformValue(shader->matrix, matrix);
+ visualizeDrawGeometry(gn->geometry());
+
+ // This is because many changes don't propegate their dirty state to the
+ // parent so the node updater will not unset these states. They are
+ // not used for anything so, unsetting it should have no side effects.
+ n->dirtyState = nullptr;
+ }
+
+ SHADOWNODE_TRAVERSE(n) {
+ visualizeChanges(child);
+ }
+}
+
+void OpenGLVisualizer::visualizeOverdraw_helper(Node *node)
+{
+ if (node->type() == QSGNode::GeometryNodeType && node->element()->batch) {
+ VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
+ QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node->sgNode);
+
+ QMatrix4x4 matrix = m_renderer->m_current_projection_matrix;
+ matrix(2, 2) = m_renderer->m_zRange;
+ matrix(2, 3) = 1.0f - node->element()->order * m_renderer->m_zRange;
+
+ if (node->element()->batch->root)
+ matrix = matrix * qsg_matrixForRoot(node->element()->batch->root);
+ matrix = matrix * *gn->matrix();
+ shader->setUniformValue(shader->matrix, matrix);
+
+ QColor color = node->element()->batch->isOpaque ? QColor::fromRgbF(0.3, 1.0, 0.3) : QColor::fromRgbF(1.0, 0.3, 0.3);
+ float ca = 0.33f;
+ shader->setUniformValue(shader->color, color.redF() * ca, color.greenF() * ca, color.blueF() * ca, ca);
+
+ visualizeDrawGeometry(gn->geometry());
+ }
+
+ SHADOWNODE_TRAVERSE(node) {
+ visualizeOverdraw_helper(child);
+ }
+}
+
+void OpenGLVisualizer::visualizeOverdraw()
+{
+ VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
+ shader->setUniformValue(shader->color, 0.5f, 0.5f, 1.0f, 1.0f);
+ shader->setUniformValue(shader->projection, 1);
+
+ m_funcs->glBlendFunc(GL_ONE, GL_ONE);
+
+ static float step = 0;
+ step += static_cast<float>(M_PI * 2 / 1000.);
+ if (step > M_PI * 2)
+ step = 0;
+ float angle = 80.0 * std::sin(step);
+
+ QMatrix4x4 xrot; xrot.rotate(20, 1, 0, 0);
+ QMatrix4x4 zrot; zrot.rotate(angle, 0, 0, 1);
+ QMatrix4x4 tx; tx.translate(0, 0, 1);
+
+ QMatrix4x4 m;
+
+// m.rotate(180, 0, 1, 0);
+
+ m.translate(0, 0.5, 4);
+ m.scale(2, 2, 1);
+
+ m.rotate(-30, 1, 0, 0);
+ m.rotate(angle, 0, 1, 0);
+ m.translate(0, 0, -1);
+
+ shader->setUniformValue(shader->rotation, m);
+
+ float box[] = {
+ // lower
+ -1, 1, 0, 1, 1, 0,
+ -1, 1, 0, -1, -1, 0,
+ 1, 1, 0, 1, -1, 0,
+ -1, -1, 0, 1, -1, 0,
+
+ // upper
+ -1, 1, 1, 1, 1, 1,
+ -1, 1, 1, -1, -1, 1,
+ 1, 1, 1, 1, -1, 1,
+ -1, -1, 1, 1, -1, 1,
+
+ // sides
+ -1, -1, 0, -1, -1, 1,
+ 1, -1, 0, 1, -1, 1,
+ -1, 1, 0, -1, 1, 1,
+ 1, 1, 0, 1, 1, 1
+ };
+ m_funcs->glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, box);
+ m_funcs->glLineWidth(2);
+ m_funcs->glDrawArrays(GL_LINES, 0, 24);
+
+ visualizeOverdraw_helper(m_renderer->m_nodes.value(m_renderer->rootNode()));
+}
+
+void OpenGLVisualizer::visualize()
+{
+ if (m_visualizeMode == VisualizeNothing)
+ return;
+
+ if (!m_visualizeProgram) {
+ VisualizeShader *prog = new VisualizeShader();
+ QSGShaderSourceBuilder::initializeProgramFromFiles(
+ prog,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.vert"),
+ QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.frag"));
+ prog->bindAttributeLocation("v", 0);
+ prog->link();
+ prog->bind();
+ prog->color = prog->uniformLocation("color");
+ prog->pattern = prog->uniformLocation("pattern");
+ prog->projection = prog->uniformLocation("projection");
+ prog->matrix = prog->uniformLocation("matrix");
+ prog->rotation = prog->uniformLocation("rotation");
+ m_visualizeProgram = prog;
+ } else {
+ m_visualizeProgram->bind();
+ }
+ VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
+
+ m_funcs->glDisable(GL_DEPTH_TEST);
+ m_funcs->glEnable(GL_BLEND);
+ m_funcs->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ m_funcs->glEnableVertexAttribArray(0);
+
+ // Blacken out the actual rendered content...
+ float bgOpacity = 0.8f;
+ if (m_visualizeMode == VisualizeBatches)
+ bgOpacity = 1.0;
+ float v[] = { -1, 1, 1, 1, -1, -1, 1, -1 };
+ shader->setUniformValue(shader->color, 0.0f, 0.0f, 0.0f, bgOpacity);
+ shader->setUniformValue(shader->matrix, QMatrix4x4());
+ shader->setUniformValue(shader->rotation, QMatrix4x4());
+ shader->setUniformValue(shader->pattern, 0.0f);
+ shader->setUniformValue(shader->projection, false);
+ m_funcs->glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, v);
+ m_funcs->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ if (m_visualizeMode == VisualizeBatches) {
+ srand(0); // To force random colors to be roughly the same every time..
+ for (int i = 0; i < m_renderer->m_opaqueBatches.size(); ++i)
+ visualizeBatch(m_renderer->m_opaqueBatches.at(i));
+ for (int i = 0; i < m_renderer->m_alphaBatches.size(); ++i)
+ visualizeBatch(m_renderer->m_alphaBatches.at(i));
+ } else if (m_visualizeMode == VisualizeClipping) {
+ shader->setUniformValue(shader->pattern, 0.5f);
+ shader->setUniformValue(shader->color, 0.2f, 0.0f, 0.0f, 0.2f);
+ visualizeClipping(m_renderer->rootNode());
+ } else if (m_visualizeMode == VisualizeChanges) {
+ visualizeChanges(m_renderer->m_nodes.value(m_renderer->rootNode()));
+ m_visualizeChangeSet.clear();
+ } else if (m_visualizeMode == VisualizeOverdraw) {
+ visualizeOverdraw();
+ }
+
+ // Reset state back to defaults..
+ m_funcs->glDisable(GL_BLEND);
+ m_funcs->glDisableVertexAttribArray(0);
+ shader->release();
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgopenglvisualizer_p.h b/src/quick/scenegraph/coreapi/qsgopenglvisualizer_p.h
new file mode 100644
index 0000000000..0b21694351
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgopenglvisualizer_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
+** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
+** 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 QSGOPENGLVISUALIZER_P_H
+#define QSGOPENGLVISUALIZER_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 "qsgbatchrenderer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QSGBatchRenderer
+{
+
+// ### Qt 6: remove
+class OpenGLVisualizer : public Visualizer
+{
+public:
+ OpenGLVisualizer(Renderer *renderer);
+ ~OpenGLVisualizer();
+
+ void prepareVisualize() override;
+ void visualize() override;
+
+ void releaseResources() override;
+
+private:
+ void visualizeBatch(Batch *b);
+ void visualizeClipping(QSGNode *node);
+ void visualizeChanges(Node *n);
+ void visualizeOverdraw();
+ void visualizeOverdraw_helper(Node *node);
+ void visualizeDrawGeometry(const QSGGeometry *g);
+
+ QOpenGLFunctions *m_funcs;
+ QOpenGLShaderProgram *m_visualizeProgram;
+};
+
+} // namespace QSGBatchRenderer
+
+QT_END_NAMESPACE
+
+#endif // QSGOPENGLVISUALIZER_P_H
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
index e1ba001d2d..7af932eeb5 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
@@ -132,6 +132,12 @@ QSGRenderer::QSGRenderer(QSGRenderContext *context)
, m_current_determinant(1)
, m_device_pixel_ratio(1)
, m_context(context)
+ , m_current_uniform_data(nullptr)
+ , m_current_resource_update_batch(nullptr)
+ , m_rhi(nullptr)
+ , m_rt(nullptr)
+ , m_cb(nullptr)
+ , m_rp_desc(nullptr)
, m_node_updater(nullptr)
, m_bindable(nullptr)
, m_changed_emitted(false)
@@ -184,21 +190,30 @@ bool QSGRenderer::isMirrored() const
void QSGRenderer::renderScene(uint fboId)
{
-#if QT_CONFIG(opengl)
- if (fboId) {
- QSGBindableFboId bindable(fboId);
- renderScene(bindable);
- } else {
+ if (m_rt) {
class B : public QSGBindable
{
public:
- void bind() const override { QOpenGLFramebufferObject::bindDefault(); }
+ void bind() const override { }
} bindable;
renderScene(bindable);
- }
+ } else {
+#if QT_CONFIG(opengl)
+ if (fboId) {
+ QSGBindableFboId bindable(fboId);
+ renderScene(bindable);
+ } else {
+ class B : public QSGBindable
+ {
+ public:
+ void bind() const override { QOpenGLFramebufferObject::bindDefault(); }
+ } bindable;
+ renderScene(bindable);
+ }
#else
- Q_UNUSED(fboId)
+ Q_UNUSED(fboId)
#endif
+ }
}
void QSGRenderer::renderScene(const QSGBindable &bindable)
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
index d4ff6ea9fe..c4ed0072f6 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
@@ -62,6 +62,10 @@ QT_BEGIN_NAMESPACE
class QSGBindable;
class QSGNodeUpdater;
+class QRhiRenderTarget;
+class QRhiCommandBuffer;
+class QRhiRenderPassDescriptor;
+class QRhiResourceUpdateBatch;
Q_QUICK_PRIVATE_EXPORT bool qsg_test_and_clear_fatal_render_error();
Q_QUICK_PRIVATE_EXPORT void qsg_set_fatal_renderer_error();
@@ -69,11 +73,10 @@ Q_QUICK_PRIVATE_EXPORT void qsg_set_fatal_renderer_error();
class Q_QUICK_PRIVATE_EXPORT QSGRenderer : public QSGAbstractRenderer
{
public:
-
QSGRenderer(QSGRenderContext *context);
virtual ~QSGRenderer();
- // Accessed by QSGMaterialShader::RenderState.
+ // Accessed by QSGMaterial[Rhi]Shader::RenderState.
QMatrix4x4 currentProjectionMatrix() const { return m_current_projection_matrix; }
QMatrix4x4 currentModelViewMatrix() const { return m_current_model_view_matrix; }
QMatrix4x4 currentCombinedMatrix() const { return m_current_projection_matrix * m_current_model_view_matrix; }
@@ -92,11 +95,36 @@ public:
QSGNodeUpdater *nodeUpdater() const;
void setNodeUpdater(QSGNodeUpdater *updater);
inline QSGMaterialShader::RenderState state(QSGMaterialShader::RenderState::DirtyStates dirty) const;
+ inline QSGMaterialRhiShader::RenderState rhiState(QSGMaterialRhiShader::RenderState::DirtyStates dirty) const;
virtual void setCustomRenderMode(const QByteArray &) { }
+ virtual bool hasCustomRenderModeWithContinuousUpdate() const { return false; }
virtual void releaseCachedResources() { }
void clearChangedFlag() { m_changed_emitted = false; }
+ // Accessed by QSGMaterialRhiShader::RenderState.
+ QByteArray *currentUniformData() const { return m_current_uniform_data; }
+ QRhiResourceUpdateBatch *currentResourceUpdateBatch() const { return m_current_resource_update_batch; }
+ QRhi *currentRhi() const { return m_rhi; }
+
+ void setRenderTarget(QRhiRenderTarget *rt) { m_rt = rt; }
+ QRhiRenderTarget *renderTarget() const { return m_rt; }
+
+ void setCommandBuffer(QRhiCommandBuffer *cb) { m_cb = cb; }
+ QRhiCommandBuffer *commandBuffer() const { return m_cb; }
+
+ void setRenderPassDescriptor(QRhiRenderPassDescriptor *rpDesc) { m_rp_desc = rpDesc; }
+ QRhiRenderPassDescriptor *renderPassDescriptor() const { return m_rp_desc; }
+
+ void setRenderPassRecordingCallbacks(QSGRenderContext::RenderPassCallback start,
+ QSGRenderContext::RenderPassCallback end,
+ void *userData)
+ {
+ m_renderPassRecordingCallbacks.start = start;
+ m_renderPassRecordingCallbacks.end = end;
+ m_renderPassRecordingCallbacks.userData = userData;
+ }
+
protected:
virtual void render() = 0;
@@ -107,7 +135,8 @@ protected:
void addNodesToPreprocess(QSGNode *node);
void removeNodesToPreprocess(QSGNode *node);
- QMatrix4x4 m_current_projection_matrix;
+ QMatrix4x4 m_current_projection_matrix; // includes adjustment, where applicable, so can be treated as Y up in NDC always
+ QMatrix4x4 m_current_projection_matrix_native_ndc; // Vulkan has Y down in normalized device coordinates, others Y up...
QMatrix4x4 m_current_model_view_matrix;
qreal m_current_opacity;
qreal m_current_determinant;
@@ -115,6 +144,18 @@ protected:
QSGRenderContext *m_context;
+ QByteArray *m_current_uniform_data;
+ QRhiResourceUpdateBatch *m_current_resource_update_batch;
+ QRhi *m_rhi;
+ QRhiRenderTarget *m_rt;
+ QRhiCommandBuffer *m_cb;
+ QRhiRenderPassDescriptor *m_rp_desc;
+ struct {
+ QSGRenderContext::RenderPassCallback start = nullptr;
+ QSGRenderContext::RenderPassCallback end = nullptr;
+ void *userData = nullptr;
+ } m_renderPassRecordingCallbacks;
+
private:
QSGNodeUpdater *m_node_updater;
@@ -156,6 +197,14 @@ QSGMaterialShader::RenderState QSGRenderer::state(QSGMaterialShader::RenderState
return s;
}
+QSGMaterialRhiShader::RenderState QSGRenderer::rhiState(QSGMaterialRhiShader::RenderState::DirtyStates dirty) const
+{
+ QSGMaterialRhiShader::RenderState s;
+ s.m_dirty = dirty;
+ s.m_data = this;
+ return s;
+}
+
class Q_QUICK_PRIVATE_EXPORT QSGNodeDumper : public QSGNodeVisitor {
diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp
index 0f49e615e4..0fee1486cf 100644
--- a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp
@@ -55,13 +55,13 @@ QT_BEGIN_NAMESPACE
the Direct3D or Vulkan device) that is used by the scenegraph.
QSGRendererInterface's functions have varying availability. API and
- language queries, like graphicsApi() or shaderType() are always available,
- meaning it is sufficient to construct a QQuickWindow or QQuickView, and the
- graphics API or shading language in use can be queried right after via
- QQuickWindow::rendererInterface(). This guarantees that utilities like the
- GraphicsInfo QML type are able to report the correct values as early as
- possible, without having conditional property values - depending on for
- instance shaderType() - evaluate to unexpected values.
+ language queries, such as, graphicsApi() or shaderType() are always
+ available, meaning it is sufficient to construct a QQuickWindow or
+ QQuickView, and the graphics API or shading language in use can be queried
+ right after via QQuickWindow::rendererInterface(). This guarantees that
+ utilities like the GraphicsInfo QML type are able to report the correct
+ values as early as possible, without having conditional property values -
+ depending on for instance shaderType() - evaluate to unexpected values.
Engine-specific accessors, like getResource(), are however available only
after the scenegraph is initialized. Additionally, there may be
@@ -78,14 +78,69 @@ QT_BEGIN_NAMESPACE
\value OpenGL OpenGL ES 2.0 or higher
\value Direct3D12 Direct3D 12
\value OpenVG OpenVG via EGL
+ \value OpenGLRhi OpenGL ES 2.0 or higher via a graphics abstraction layer. This value was introduced in Qt 5.14.
+ \value Direct3D11Rhi Direct3D 11 via a graphics abstraction layer. This value was introduced in Qt 5.14.
+ \value VulkanRhi Vulkan 1.0 via a graphics abstraction layer. This value was introduced in Qt 5.14.
+ \value MetalRhi Metal via a graphics abstraction layer. This value was introduced in Qt 5.14.
+ \value NullRhi Null (no output) via a graphics abstraction layer. This value was introduced in Qt 5.14.
*/
/*!
\enum QSGRendererInterface::Resource
- \value DeviceResource The graphics device, when applicable.
- \value CommandQueueResource The graphics command queue used by the scenegraph, when applicable.
- \value CommandListResource The command list or buffer used by the scenegraph, when applicable.
- \value PainterResource The active QPainter used by the scenegraph, when running with the software backend.
+
+ \value DeviceResource The resource is a pointer to the graphics device,
+ when applicable. For example, a \c{VkDevice *}, \c{MTLDevice *} or
+ \c{ID3D11Device *}. Note that with Vulkan the returned value is a pointer
+ to the VkDevice, not the handle itself. This is because Vulkan handles may
+ not be pointers, and may use a different size from the architecture's
+ pointer size so merely casting to/from \c{void *} is wrong.
+
+ \value CommandQueueResource The resource is a pointer to the graphics
+ command queue used by the scenegraph, when applicable. For example, a
+ \c{VkQueue *} or \c{MTLCommandQueue *}. Note that with Vulkan the returned
+ value is a pointer to the VkQueue, not the handle itself.
+
+ \value CommandListResource The resource is a pointer to the command list or
+ buffer used by the scenegraph, when applicable. For example, a
+ \c{VkCommandBuffer *} or \c{MTLCommandBuffer *}. This object has limited
+ validity, and is only valid while the scene graph is preparing the next
+ frame. Note that with Vulkan the returned value is a pointer to the
+ VkCommandBuffer, not the handle itself.
+
+ \value PainterResource The resource is a pointer to the active QPainter
+ used by the scenegraph, when running with the software backend.
+
+ \value RhiResource The resource is a pointer to the QRhi instance used by
+ the scenegraph, when applicable. This value was introduced in Qt 5.14.
+
+ \value PhysicalDeviceResource The resource is a pointer to the pysical
+ device object used by the scenegraph, when applicable. For example, a
+ \c{VkPhysicalDevice *}. Note that with Vulkan the returned value is a
+ pointer to the VkPhysicalDevice, not the handle itself. This value was
+ introduced in Qt 5.14.
+
+ \value OpenGLContextResource The resource is a pointer to the
+ QOpenGLContext used by the scenegraph (on the render thread), when
+ applicable. This value was introduced in Qt 5.14.
+
+ \value DeviceContextResource The resource is a pointer to the device
+ context used by the scenegraph, when applicable. For example, a
+ \c{ID3D11DeviceContext *}. This value was introduced in Qt 5.14.
+
+ \value CommandEncoderResource The resource is a pointer to the currently
+ active render command encoder object used by the scenegraph, when
+ applicable. For example, a \c{MTLRenderCommandEncoder *}. This object has
+ limited validity, and is only valid while the scene graph is recording a
+ render pass for the next frame. This value was introduced in Qt 5.14.
+
+ \value VulkanInstanceResource The resource is a pointer to the
+ QVulkanInstance used by the scenegraph, when applicable. This value was
+ introduced in Qt 5.14.
+
+ \value RenderPassResource The resource is a pointer to the render pass used
+ by the scenegraph, describing the color and depth/stecil attachments and
+ how they are used. For example, a \c{VkRenderPass *}. This value was
+ introduced in Qt 5.14.
*/
/*!
@@ -93,6 +148,9 @@ QT_BEGIN_NAMESPACE
\value UnknownShadingLanguage Not yet known due to no window and scenegraph associated
\value GLSL GLSL or GLSL ES
\value HLSL HLSL
+ \value RhiShader Consumes QShader instances containing shader variants for
+ multiple target languages and intermediate formats. This value was introduced in
+ Qt 5.14.
*/
/*!
@@ -164,6 +222,32 @@ void *QSGRendererInterface::getResource(QQuickWindow *window, const char *resour
}
/*!
+ \return true if \a api is based on a graphics abstraction layer (QRhi)
+ instead of directly calling the native graphics API.
+
+ \note This function can be called on any thread.
+
+ \since 5.14
+ */
+bool QSGRendererInterface::isApiRhiBased(GraphicsApi api)
+{
+ switch (api) {
+ case OpenGLRhi:
+ Q_FALLTHROUGH();
+ case Direct3D11Rhi:
+ Q_FALLTHROUGH();
+ case VulkanRhi:
+ Q_FALLTHROUGH();
+ case MetalRhi:
+ Q_FALLTHROUGH();
+ case NullRhi:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*!
\fn QSGRendererInterface::ShaderType QSGRendererInterface::shaderType() const
\return the shading language supported by the Qt Quick backend the
diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.h b/src/quick/scenegraph/coreapi/qsgrendererinterface.h
index 722488201b..7aa7d0e769 100644
--- a/src/quick/scenegraph/coreapi/qsgrendererinterface.h
+++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.h
@@ -54,20 +54,33 @@ public:
Software,
OpenGL,
Direct3D12,
- OpenVG
+ OpenVG,
+ OpenGLRhi,
+ Direct3D11Rhi,
+ VulkanRhi,
+ MetalRhi,
+ NullRhi,
};
enum Resource {
DeviceResource,
CommandQueueResource,
CommandListResource,
- PainterResource
+ PainterResource,
+ RhiResource,
+ PhysicalDeviceResource,
+ OpenGLContextResource,
+ DeviceContextResource,
+ CommandEncoderResource,
+ VulkanInstanceResource,
+ RenderPassResource
};
enum ShaderType {
UnknownShadingLanguage,
GLSL,
- HLSL
+ HLSL,
+ RhiShader
};
enum ShaderCompilationType {
@@ -93,6 +106,8 @@ public:
virtual ShaderType shaderType() const = 0;
virtual ShaderCompilationTypes shaderCompilationType() const = 0;
virtual ShaderSourceTypes shaderSourceType() const = 0;
+
+ static bool isApiRhiBased(GraphicsApi api);
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRendererInterface::ShaderCompilationTypes)
diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp
index df3fa16a32..2892f2f966 100644
--- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp
@@ -81,8 +81,10 @@ QSGRenderNodePrivate::QSGRenderNodePrivate()
}
/*!
- This function should return a mask where each bit represents graphics states changed by
- the \l render() function:
+ When the underlying rendering API is OpenGL, this function should return a
+ mask where each bit represents graphics states changed by the \l render()
+ function:
+
\list
\li DepthState - depth write mask, depth test enabled, depth comparison function
\li StencilState - stencil write masks, stencil test enabled, stencil operations,
@@ -95,6 +97,29 @@ QSGRenderNodePrivate::QSGRenderNodePrivate()
\li RenderTargetState - render target
\endlist
+ With APIs other than OpenGL, the only relevant values are the ones that
+ correspond to dynamic state changes recorded on the command list/buffer.
+ For example, RSSetViewports, RSSetScissorRects, OMSetBlendFactor,
+ OMSetStencilRef in case of D3D12, or vkCmdSetViewport, vkCmdSetScissor,
+ vkCmdSetBlendConstants, vkCmdSetStencilRef in case of Vulkan, and only when
+ such commands were added to the scenegraph's command list queried via the
+ QSGRendererInterface::CommandList resource enum. States set in pipeline
+ state objects do not need to be reported here. Similarly, draw call related
+ settings (pipeline states, descriptor sets, vertex or index buffer
+ bindings, root signature, descriptor heaps, etc.) are always set again by
+ the scenegraph so render() can freely change them.
+
+ \note RenderTargetState is no longer supported with APIs like Vulkan. This
+ is by nature. render() is invoked while the Qt Quick scenegraph's main
+ command buffer is recording a renderpass, so there is no possibility of
+ changing the target and starting another renderpass (on that command buffer
+ at least). Therefore returning a value with RenderTargetState set is not
+ sensible.
+
+ The software backend exposes its QPainter and saves and restores before and
+ after invoking render(). Therefore reporting any changed states from here
+ is not necessary.
+
The function is called by the renderer so it can reset the states after
rendering this node. This makes the implementation of render() simpler
since it does not have to query and restore these states.
@@ -102,19 +127,6 @@ QSGRenderNodePrivate::QSGRenderNodePrivate()
The default implementation returns 0, meaning no relevant state was changed
in render().
- With APIs other than OpenGL the relevant states are only those that are set
- via the command list (for example, OMSetRenderTargets, RSSetViewports,
- RSSetScissorRects, OMSetBlendFactor, OMSetStencilRef in case of D3D12), and
- only when such commands were added to the scenegraph's command list queried
- via the QSGRendererInterface::CommandList resource enum. States set in
- pipeline state objects do not need to be reported here. Similarly, draw
- call related settings (root signature, descriptor heaps, etc.) are always
- set again by the scenegraph so render() can freely change them.
-
- The software backend exposes its QPainter and saves and restores before and
- after invoking render(). Therefore reporting any changed states from here
- is not necessary.
-
\note This function may be called before render().
*/
QSGRenderNode::StateFlags QSGRenderNode::changedStates() const
@@ -149,18 +161,31 @@ QSGRenderNode::StateFlags QSGRenderNode::changedStates() const
QQuickFramebufferObject, QQuickWindow::beforeRendering(), or the
equivalents of those for APIs other than OpenGL.
- Clip information is calculated before the function is called, it is however
- not enabled. Implementations wishing to take clipping into account can set
- up scissoring or stencil based on the information in \a state. Some
- scenegraph backends, software in particular, use no scissor or stencil.
- There the clip region is provided as an ordinary QRegion.
+ \note QSGRenderNode can perform significantly better than texture-based
+ approaches (such as, QQuickFramebufferObject), especially on systems where
+ the fragment processing power is limited. This is because it avoids
+ rendering to a texture and then drawing a textured quad. Rather,
+ QSGRenderNode allows recording draw calls in line with the scenegraph's
+ other commands, avoiding an additional render target and the potentially
+ expensive texturing and blending.
+
+ Clip information is calculated before the function is called.
+ Implementations wishing to take clipping into account can set up scissoring
+ or stencil based on the information in \a state. The stencil buffer is
+ filled with the necessary clip shapes, but it is up to the implementation
+ to enable stencil testing.
+
+ Some scenegraph backends, software in particular, use no scissor or
+ stencil. There the clip region is provided as an ordinary QRegion.
+
+ With the legacy, direct OpenGL based renderer, the following states are set
+ on the render thread's context before this function is called:
- For OpenGL the following states are set on the render thread's context
- before this function is called:
\list
+ \li glColorMask(true, true, true, true)
\li glDepthMask(false)
\li glDisable(GL_DEPTH_TEST)
- \li glStencilFunc(GL_EQUAL, state.stencilValue, 0xff) depending on clip
+ \li glStencilFunc(GL_EQUAL, state.stencilValue, 0xff); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP) depending on clip
\li glScissor(state.scissorRect.x(), state.scissorRect.y(),
state.scissorRect.width(), state.scissorRect.height()) depending on clip
\li glEnable(GL_BLEND)
@@ -168,23 +193,42 @@ QSGRenderNode::StateFlags QSGRenderNode::changedStates() const
\li glDisable(GL_CULL_FACE)
\endlist
- States that are not listed above, but are included in \l StateFlags, can
+ States that are not listed above, but are covered by \l StateFlags, can
have arbitrary values.
+ \note There is no state set with other graphics APIs, considering that many
+ of them do not have a concept of the traditional OpenGL state machine.
+ Rather, it is up to the implementation to create pipeline state objects
+ with the desired blending, scissor, and stencil tests enabled. Note that
+ this also includes OpenGL via the RHI. New QSGRenderNode implementations
+ are recommended to set all scissor, stencil and blend state explicitly (as
+ shown in the above list), even if they are targeting OpenGL.
+
\l changedStates() should return which states this function changes. If a
state is not covered by \l StateFlags, the state should be set to the
default value according to the OpenGL specification. For other APIs, see
the documentation for changedStates() for more information.
- \note Depth writes are disabled when this function is called (for example,
- glDepthMask(false) in case of OpenGL). Enabling depth writes can lead to
- unexpected results, depending on the scenegraph backend in use, so nodes
- should avoid this.
+ \note Depth writes are disabled when this function is called
+ (glDepthMask(false) with OpenGL). Enabling depth writes can lead to
+ unexpected results, depending on the scenegraph backend in use and the
+ content in the scene, so exercise caution with this.
For APIs other than OpenGL, it will likely be necessary to query certain
API-specific resources (for example, the graphics device or the command
list/buffer to add the commands to). This is done via QSGRendererInterface.
+ Assume nothing about the pipelines and dynamic states bound on the command
+ list/buffer when this function is called.
+
+ With some graphics APIs it can be necessary to also connect to the
+ QQuickWindow::beforeRendering() signal, because that is emitted before
+ recording the beginning of a renderpass on the command buffer
+ (vkCmdBeginRenderPass with Vulkan, or starting to encode via
+ MTLRenderCommandEncoder in case of Metal). Recording copy operations cannot
+ be done inside render() with such APIs. Rather, do it in the slot connected
+ (with DirectConnection) to the beforeRendering signal.
+
\sa QSGRendererInterface, QQuickWindow::rendererInterface()
*/
@@ -335,7 +379,8 @@ QSGRenderNode::RenderState::~RenderState()
/*!
\fn const QMatrix4x4 *QSGRenderNode::RenderState::scissorRect() const
- \return the current scissor rectangle when clipping is active.
+ \return the current scissor rectangle when clipping is active. x and y are
+ the bottom left coordinates.
\note Be aware of the differences between graphics APIs: for some the
scissor rect is only active when scissoring is enabled (for example,
diff --git a/src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp b/src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp
new file mode 100644
index 0000000000..38d4c4440f
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp
@@ -0,0 +1,929 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
+** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
+** 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 "qsgrhivisualizer_p.h"
+#include <qmath.h>
+#include <QQuickWindow>
+#include <private/qsgmaterialrhishader_p.h>
+#include <private/qsgshadersourcebuilder_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSGBatchRenderer
+{
+
+#define QSGNODE_TRAVERSE(NODE) for (QSGNode *child = NODE->firstChild(); child; child = child->nextSibling())
+#define SHADOWNODE_TRAVERSE(NODE) for (Node *child = NODE->firstChild(); child; child = child->sibling())
+#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \
+ | QSGNode::DirtyOpacity \
+ | QSGNode::DirtyMatrix \
+ | QSGNode::DirtyNodeRemoved)
+
+QMatrix4x4 qsg_matrixForRoot(Node *node);
+QRhiVertexInputAttribute::Format qsg_vertexInputFormat(const QSGGeometry::Attribute &a);
+QRhiCommandBuffer::IndexFormat qsg_indexFormat(const QSGGeometry *geometry);
+QRhiGraphicsPipeline::Topology qsg_topology(int geomDrawMode);
+
+RhiVisualizer::RhiVisualizer(Renderer *renderer)
+ : Visualizer(renderer)
+{
+}
+
+RhiVisualizer::~RhiVisualizer()
+{
+ releaseResources();
+}
+
+void RhiVisualizer::releaseResources()
+{
+ m_pipelines.releaseResources();
+
+ m_fade.releaseResources();
+
+ m_changeVis.releaseResources();
+ m_batchVis.releaseResources();
+ m_clipVis.releaseResources();
+ m_overdrawVis.releaseResources();
+}
+
+void RhiVisualizer::prepareVisualize()
+{
+ // Called before the render pass has begun (but after preparing the
+ // batches). Now is the time to put resource updates to the renderer's
+ // current m_resourceUpdates instance.
+
+ if (m_visualizeMode == VisualizeNothing)
+ return;
+
+ if (!m_vs.isValid()) {
+ m_vs = QSGMaterialRhiShaderPrivate::loadShader(
+ QLatin1String(":/qt-project.org/scenegraph/shaders_ng/visualization.vert.qsb"));
+ m_fs = QSGMaterialRhiShaderPrivate::loadShader(
+ QLatin1String(":/qt-project.org/scenegraph/shaders_ng/visualization.frag.qsb"));
+ }
+
+ m_fade.prepare(this, m_renderer->m_rhi, m_renderer->m_resourceUpdates, m_renderer->renderPassDescriptor());
+
+ const bool forceUintIndex = m_renderer->m_uint32IndexForRhi;
+
+ switch (m_visualizeMode) {
+ case VisualizeBatches:
+ m_batchVis.prepare(m_renderer->m_opaqueBatches, m_renderer->m_alphaBatches,
+ this,
+ m_renderer->m_rhi, m_renderer->m_resourceUpdates,
+ forceUintIndex);
+ break;
+ case VisualizeClipping:
+ m_clipVis.prepare(m_renderer->rootNode(), this,
+ m_renderer->m_rhi, m_renderer->m_resourceUpdates);
+ break;
+ case VisualizeChanges:
+ m_changeVis.prepare(m_renderer->m_nodes.value(m_renderer->rootNode()),
+ this,
+ m_renderer->m_rhi, m_renderer->m_resourceUpdates);
+ m_visualizeChangeSet.clear();
+ break;
+ case VisualizeOverdraw:
+ m_overdrawVis.prepare(m_renderer->m_nodes.value(m_renderer->rootNode()),
+ this,
+ m_renderer->m_rhi, m_renderer->m_resourceUpdates);
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+}
+
+void RhiVisualizer::visualize()
+{
+ if (m_visualizeMode == VisualizeNothing)
+ return;
+
+ QRhiCommandBuffer *cb = m_renderer->commandBuffer();
+ m_fade.render(cb);
+
+ switch (m_visualizeMode) {
+ case VisualizeBatches:
+ m_batchVis.render(cb);
+ break;
+ case VisualizeClipping:
+ m_clipVis.render(cb);
+ break;
+ case VisualizeChanges:
+ m_changeVis.render(cb);
+ break;
+ case VisualizeOverdraw:
+ m_overdrawVis.render(cb);
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+}
+
+void RhiVisualizer::recordDrawCalls(const QVector<DrawCall> &drawCalls,
+ QRhiCommandBuffer *cb,
+ QRhiShaderResourceBindings *srb,
+ bool blendOneOne)
+{
+ for (const DrawCall &dc : drawCalls) {
+ QRhiGraphicsPipeline *ps = m_pipelines.pipeline(this, m_renderer->m_rhi, srb, m_renderer->renderPassDescriptor(),
+ dc.vertex.topology, dc.vertex.format, dc.vertex.stride,
+ blendOneOne);
+ if (!ps)
+ continue;
+ cb->setGraphicsPipeline(ps); // no-op if same as the last one
+ QRhiCommandBuffer::DynamicOffset dynofs(0, dc.buf.ubufOffset);
+ cb->setShaderResources(srb, 1, &dynofs);
+ QRhiCommandBuffer::VertexInput vb(dc.buf.vbuf, dc.buf.vbufOffset);
+ if (dc.index.count) {
+ cb->setVertexInput(0, 1, &vb, dc.buf.ibuf, dc.buf.ibufOffset, dc.index.format);
+ cb->drawIndexed(dc.index.count);
+ } else {
+ cb->setVertexInput(0, 1, &vb);
+ cb->draw(dc.vertex.count);
+ }
+ }
+}
+
+const QRhiShaderResourceBinding::StageFlags ubufVisibility =
+ QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage;
+
+void RhiVisualizer::Fade::prepare(RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u, QRhiRenderPassDescriptor *rpDesc)
+{
+ this->visualizer = visualizer;
+
+ if (!vbuf) {
+ float v[] = { -1, 1, 1, 1, -1, -1, 1, -1 };
+ vbuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(v));
+ if (!vbuf->build())
+ return;
+ u->uploadStaticBuffer(vbuf, v);
+ }
+
+ if (!ubuf) {
+ ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, DrawCall::UBUF_SIZE);
+ if (!ubuf->build())
+ return;
+ float bgOpacity = 0.8f;
+ if (visualizer->m_visualizeMode == Visualizer::VisualizeBatches)
+ bgOpacity = 1.0;
+ QMatrix4x4 ident;
+ u->updateDynamicBuffer(ubuf, 0, 64, ident.constData()); // matrix
+ u->updateDynamicBuffer(ubuf, 64, 64, ident.constData()); // rotation
+ float color[4] = { 0.0f, 0.0f, 0.0f, bgOpacity };
+ u->updateDynamicBuffer(ubuf, 128, 16, color);
+ float pattern = 0.0f;
+ u->updateDynamicBuffer(ubuf, 144, 4, &pattern);
+ qint32 projection = 0;
+ u->updateDynamicBuffer(ubuf, 148, 4, &projection);
+ }
+
+ if (!srb) {
+ srb = rhi->newShaderResourceBindings();
+ srb->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, ubufVisibility, ubuf) });
+ if (!srb->build())
+ return;
+ }
+
+ if (!ps) {
+ ps = rhi->newGraphicsPipeline();
+ ps->setTopology(QRhiGraphicsPipeline::TriangleStrip);
+ QRhiGraphicsPipeline::TargetBlend blend; // defaults to premul alpha, just what we need
+ blend.enable = true;
+ ps->setTargetBlends({ blend });
+ ps->setShaderStages({ { QRhiShaderStage::Vertex, visualizer->m_vs },
+ { QRhiShaderStage::Fragment, visualizer->m_fs } });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({ { 2 * sizeof(float) } });
+ inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float2, 0 } });
+ ps->setVertexInputLayout(inputLayout);
+ ps->setShaderResourceBindings(srb);
+ ps->setRenderPassDescriptor(rpDesc);
+ if (!ps->build())
+ return;
+ }
+}
+
+void RhiVisualizer::Fade::releaseResources()
+{
+ delete ps;
+ ps = nullptr;
+
+ delete srb;
+ srb = nullptr;
+
+ delete ubuf;
+ ubuf = nullptr;
+
+ delete vbuf;
+ vbuf = nullptr;
+}
+
+void RhiVisualizer::Fade::render(QRhiCommandBuffer *cb)
+{
+ cb->setGraphicsPipeline(ps);
+ cb->setViewport(visualizer->m_renderer->m_pstate.viewport);
+ cb->setShaderResources();
+ QRhiCommandBuffer::VertexInput vb(vbuf, 0);
+ cb->setVertexInput(0, 1, &vb);
+ cb->draw(4);
+}
+
+static void fillVertexIndex(RhiVisualizer::DrawCall *dc, QSGGeometry *g, bool withData, bool forceUintIndex)
+{
+ dc->vertex.topology = qsg_topology(g->drawingMode());
+ dc->vertex.format = qsg_vertexInputFormat(g->attributes()[0]);
+ dc->vertex.count = g->vertexCount();
+ dc->vertex.stride = g->sizeOfVertex();
+ if (withData)
+ dc->vertex.data = g->vertexData();
+
+ dc->index.format = forceUintIndex ? QRhiCommandBuffer::IndexUInt32 : qsg_indexFormat(g);
+ dc->index.count = g->indexCount();
+ dc->index.stride = forceUintIndex ? sizeof(quint32) : g->sizeOfIndex();
+ if (withData && g->indexCount())
+ dc->index.data = g->indexData();
+}
+
+static inline uint aligned(uint v, uint byteAlign)
+{
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+static bool ensureBuffer(QRhi *rhi, QRhiBuffer **buf, QRhiBuffer::UsageFlags usage, int newSize)
+{
+ if (!*buf) {
+ *buf = rhi->newBuffer(QRhiBuffer::Dynamic, usage, newSize);
+ if (!(*buf)->build())
+ return false;
+ } else if ((*buf)->size() < newSize) {
+ (*buf)->setSize(newSize);
+ if (!(*buf)->build())
+ return false;
+ }
+ return true;
+}
+
+QRhiGraphicsPipeline *RhiVisualizer::PipelineCache::pipeline(RhiVisualizer *visualizer,
+ QRhi *rhi,
+ QRhiShaderResourceBindings *srb,
+ QRhiRenderPassDescriptor *rpDesc,
+ QRhiGraphicsPipeline::Topology topology,
+ QRhiVertexInputAttribute::Format vertexFormat,
+ quint32 vertexStride,
+ bool blendOneOne)
+{
+ for (int i = 0, ie = pipelines.count(); i != ie; ++i) {
+ const Pipeline &p(pipelines.at(i));
+ if (p.topology == topology && p.format == vertexFormat && p.stride == vertexStride)
+ return p.ps;
+ }
+
+ QRhiGraphicsPipeline *ps = rhi->newGraphicsPipeline();
+ ps->setTopology(topology);
+ QRhiGraphicsPipeline::TargetBlend blend; // premul alpha
+ blend.enable = true;
+ if (blendOneOne) {
+ // only for visualizing overdraw, other modes use premul alpha
+ blend.srcColor = QRhiGraphicsPipeline::One;
+ blend.dstColor = QRhiGraphicsPipeline::One;
+ blend.srcAlpha = QRhiGraphicsPipeline::One;
+ blend.dstAlpha = QRhiGraphicsPipeline::One;
+ }
+ ps->setTargetBlends({ blend });
+ ps->setShaderStages({ { QRhiShaderStage::Vertex, visualizer->m_vs },
+ { QRhiShaderStage::Fragment, visualizer->m_fs } });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({ { vertexStride } });
+ inputLayout.setAttributes({ { 0, 0, vertexFormat, 0 } });
+ ps->setVertexInputLayout(inputLayout);
+ ps->setShaderResourceBindings(srb);
+ ps->setRenderPassDescriptor(rpDesc);
+ if (!ps->build())
+ return nullptr;
+
+ Pipeline p;
+ p.topology = topology;
+ p.format = vertexFormat;
+ p.stride = vertexStride;
+ p.ps = ps;
+ pipelines.append(p);
+
+ return ps;
+}
+
+void RhiVisualizer::PipelineCache::releaseResources()
+{
+ for (int i = 0, ie = pipelines.count(); i != ie; ++i)
+ delete pipelines.at(i).ps;
+
+ pipelines.clear();
+}
+
+void RhiVisualizer::ChangeVis::gather(Node *n)
+{
+ if (n->type() == QSGNode::GeometryNodeType && n->element()->batch && visualizer->m_visualizeChangeSet.contains(n)) {
+ const uint dirty = visualizer->m_visualizeChangeSet.value(n);
+ const bool tinted = (dirty & QSGNODE_DIRTY_PARENT) != 0;
+ const QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0f, 0.3f, 1.0f);
+ const float alpha = 0.5f;
+
+ QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix;
+ if (n->element()->batch->root)
+ matrix = matrix * qsg_matrixForRoot(n->element()->batch->root);
+
+ QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode);
+ matrix = matrix * *gn->matrix();
+
+ QSGGeometry *g = gn->geometry();
+ if (g->attributeCount() >= 1) {
+ DrawCall dc;
+ memcpy(dc.uniforms.data, matrix.constData(), 64);
+ QMatrix4x4 rotation;
+ memcpy(dc.uniforms.data + 64, rotation.constData(), 64);
+ float c[4] = {
+ float(color.redF()) * alpha,
+ float(color.greenF()) * alpha,
+ float(color.blueF()) * alpha,
+ alpha
+ };
+ memcpy(dc.uniforms.data + 128, c, 16);
+ float pattern = tinted ? 0.5f : 0.0f;
+ memcpy(dc.uniforms.data + 144, &pattern, 4);
+ qint32 projection = 0;
+ memcpy(dc.uniforms.data + 148, &projection, 4);
+
+ fillVertexIndex(&dc, g, true, false);
+ drawCalls.append(dc);
+ }
+
+ // This is because many changes don't propegate their dirty state to the
+ // parent so the node updater will not unset these states. They are
+ // not used for anything so, unsetting it should have no side effects.
+ n->dirtyState = nullptr;
+ }
+
+ SHADOWNODE_TRAVERSE(n) {
+ gather(child);
+ }
+}
+
+void RhiVisualizer::ChangeVis::prepare(Node *n, RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u)
+{
+ this->visualizer = visualizer;
+
+ drawCalls.clear();
+ gather(n);
+
+ if (drawCalls.isEmpty())
+ return;
+
+ const int ubufAlign = rhi->ubufAlignment();
+ int vbufOffset = 0;
+ int ibufOffset = 0;
+ int ubufOffset = 0;
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ dc.buf.vbufOffset = aligned(vbufOffset, 4);
+ vbufOffset = dc.buf.vbufOffset + dc.vertex.count * dc.vertex.stride;
+
+ dc.buf.ibufOffset = aligned(ibufOffset, 4);
+ ibufOffset = dc.buf.ibufOffset + dc.index.count * dc.index.stride;
+
+ dc.buf.ubufOffset = aligned(ubufOffset, ubufAlign);
+ ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE;
+ }
+
+ ensureBuffer(rhi, &vbuf, QRhiBuffer::VertexBuffer, vbufOffset);
+ if (ibufOffset)
+ ensureBuffer(rhi, &ibuf, QRhiBuffer::IndexBuffer, ibufOffset);
+ const int ubufSize = ubufOffset;
+ ensureBuffer(rhi, &ubuf, QRhiBuffer::UniformBuffer, ubufSize);
+
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ u->updateDynamicBuffer(vbuf, dc.buf.vbufOffset, dc.vertex.count * dc.vertex.stride, dc.vertex.data);
+ dc.buf.vbuf = vbuf;
+ if (dc.index.count) {
+ u->updateDynamicBuffer(ibuf, dc.buf.ibufOffset, dc.index.count * dc.index.stride, dc.index.data);
+ dc.buf.ibuf = ibuf;
+ }
+ u->updateDynamicBuffer(ubuf, dc.buf.ubufOffset, DrawCall::UBUF_SIZE, dc.uniforms.data);
+ }
+
+ if (!srb) {
+ srb = rhi->newShaderResourceBindings();
+ srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, ubufVisibility, ubuf, DrawCall::UBUF_SIZE) });
+ if (!srb->build())
+ return;
+ }
+}
+
+void RhiVisualizer::ChangeVis::releaseResources()
+{
+ delete srb;
+ srb = nullptr;
+
+ delete ubuf;
+ ubuf = nullptr;
+
+ delete ibuf;
+ ibuf = nullptr;
+
+ delete vbuf;
+ vbuf = nullptr;
+}
+
+void RhiVisualizer::ChangeVis::render(QRhiCommandBuffer *cb)
+{
+ visualizer->recordDrawCalls(drawCalls, cb, srb);
+}
+
+void RhiVisualizer::BatchVis::gather(Batch *b)
+{
+ if (b->positionAttribute != 0)
+ return;
+
+ QMatrix4x4 matrix(visualizer->m_renderer->m_current_projection_matrix);
+ if (b->root)
+ matrix = matrix * qsg_matrixForRoot(b->root);
+
+ DrawCall dc;
+
+ QMatrix4x4 rotation;
+ memcpy(dc.uniforms.data + 64, rotation.constData(), 64);
+
+ QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 1.0, 1.0);
+
+ float c[4] = {
+ float(color.redF()),
+ float(color.greenF()),
+ float(color.blueF()),
+ 1.0f
+ };
+ memcpy(dc.uniforms.data + 128, c, 16);
+
+ float pattern = b->merged ? 0.0f : 1.0f;
+ memcpy(dc.uniforms.data + 144, &pattern, 4);
+
+ qint32 projection = 0;
+ memcpy(dc.uniforms.data + 148, &projection, 4);
+
+ if (b->merged) {
+ memcpy(dc.uniforms.data, matrix.constData(), 64);
+
+ QSGGeometryNode *gn = b->first->node;
+ QSGGeometry *g = gn->geometry();
+
+ fillVertexIndex(&dc, g, false, forceUintIndex);
+
+ for (int ds = 0; ds < b->drawSets.size(); ++ds) {
+ const DrawSet &set = b->drawSets.at(ds);
+ dc.buf.vbuf = b->vbo.buf;
+ dc.buf.vbufOffset = set.vertices;
+ dc.buf.ibuf = b->ibo.buf;
+ dc.buf.ibufOffset = set.indices;
+ dc.index.count = set.indexCount;
+ drawCalls.append(dc);
+ }
+ } else {
+ Element *e = b->first;
+ int vOffset = 0;
+ int iOffset = 0;
+
+ while (e) {
+ QSGGeometryNode *gn = e->node;
+ QSGGeometry *g = gn->geometry();
+
+ QMatrix4x4 m = matrix * *gn->matrix();
+ memcpy(dc.uniforms.data, m.constData(), 64);
+
+ fillVertexIndex(&dc, g, false, forceUintIndex);
+
+ dc.buf.vbuf = b->vbo.buf;
+ dc.buf.vbufOffset = vOffset;
+ if (g->indexCount()) {
+ dc.buf.ibuf = b->ibo.buf;
+ dc.buf.ibufOffset = iOffset;
+ }
+
+ drawCalls.append(dc);
+
+ vOffset += dc.vertex.count * dc.vertex.stride;
+ iOffset += dc.index.count * dc.index.stride;
+
+ e = e->nextInBatch;
+ }
+ }
+}
+
+void RhiVisualizer::BatchVis::prepare(const QDataBuffer<Batch *> &opaqueBatches, const QDataBuffer<Batch *> &alphaBatches,
+ RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u,
+ bool forceUintIndex)
+{
+ this->visualizer = visualizer;
+ this->forceUintIndex = forceUintIndex;
+
+ drawCalls.clear();
+
+ srand(0); // To force random colors to be roughly the same every time..
+ for (int i = 0; i < opaqueBatches.size(); ++i)
+ gather(opaqueBatches.at(i));
+ for (int i = 0; i < alphaBatches.size(); ++i)
+ gather(alphaBatches.at(i));
+
+ if (drawCalls.isEmpty())
+ return;
+
+ const int ubufAlign = rhi->ubufAlignment();
+ int ubufOffset = 0;
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ dc.buf.ubufOffset = aligned(ubufOffset, ubufAlign);
+ ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE;
+ }
+
+ const int ubufSize = ubufOffset;
+ ensureBuffer(rhi, &ubuf, QRhiBuffer::UniformBuffer, ubufSize);
+
+ for (RhiVisualizer::DrawCall &dc : drawCalls)
+ u->updateDynamicBuffer(ubuf, dc.buf.ubufOffset, DrawCall::UBUF_SIZE, dc.uniforms.data);
+
+ if (!srb) {
+ srb = rhi->newShaderResourceBindings();
+ srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, ubufVisibility, ubuf, DrawCall::UBUF_SIZE) });
+ if (!srb->build())
+ return;
+ }
+}
+
+void RhiVisualizer::BatchVis::releaseResources()
+{
+ delete srb;
+ srb = nullptr;
+
+ delete ubuf;
+ ubuf = nullptr;
+}
+
+void RhiVisualizer::BatchVis::render(QRhiCommandBuffer *cb)
+{
+ visualizer->recordDrawCalls(drawCalls, cb, srb);
+}
+
+void RhiVisualizer::ClipVis::gather(QSGNode *node)
+{
+ if (node->type() == QSGNode::ClipNodeType) {
+ QSGClipNode *clipNode = static_cast<QSGClipNode *>(node);
+ QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix;
+ if (clipNode->matrix())
+ matrix = matrix * *clipNode->matrix();
+
+ QSGGeometry *g = clipNode->geometry();
+ if (g->attributeCount() >= 1) {
+ DrawCall dc;
+ memcpy(dc.uniforms.data, matrix.constData(), 64);
+ QMatrix4x4 rotation;
+ memcpy(dc.uniforms.data + 64, rotation.constData(), 64);
+ float c[4] = { 0.2f, 0.0f, 0.0f, 0.2f };
+ memcpy(dc.uniforms.data + 128, c, 16);
+ float pattern = 0.5f;
+ memcpy(dc.uniforms.data + 144, &pattern, 4);
+ qint32 projection = 0;
+ memcpy(dc.uniforms.data + 148, &projection, 4);
+ fillVertexIndex(&dc, g, true, false);
+ drawCalls.append(dc);
+ }
+ }
+
+ QSGNODE_TRAVERSE(node) {
+ gather(child);
+ }
+}
+
+void RhiVisualizer::ClipVis::prepare(QSGNode *node, RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u)
+{
+ this->visualizer = visualizer;
+
+ drawCalls.clear();
+ gather(node);
+
+ if (drawCalls.isEmpty())
+ return;
+
+ const int ubufAlign = rhi->ubufAlignment();
+ int vbufOffset = 0;
+ int ibufOffset = 0;
+ int ubufOffset = 0;
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ dc.buf.vbufOffset = aligned(vbufOffset, 4);
+ vbufOffset = dc.buf.vbufOffset + dc.vertex.count * dc.vertex.stride;
+
+ dc.buf.ibufOffset = aligned(ibufOffset, 4);
+ ibufOffset = dc.buf.ibufOffset + dc.index.count * dc.index.stride;
+
+ dc.buf.ubufOffset = aligned(ubufOffset, ubufAlign);
+ ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE;
+ }
+
+ ensureBuffer(rhi, &vbuf, QRhiBuffer::VertexBuffer, vbufOffset);
+ if (ibufOffset)
+ ensureBuffer(rhi, &ibuf, QRhiBuffer::IndexBuffer, ibufOffset);
+ const int ubufSize = ubufOffset;
+ ensureBuffer(rhi, &ubuf, QRhiBuffer::UniformBuffer, ubufSize);
+
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ u->updateDynamicBuffer(vbuf, dc.buf.vbufOffset, dc.vertex.count * dc.vertex.stride, dc.vertex.data);
+ dc.buf.vbuf = vbuf;
+ if (dc.index.count) {
+ u->updateDynamicBuffer(ibuf, dc.buf.ibufOffset, dc.index.count * dc.index.stride, dc.index.data);
+ dc.buf.ibuf = ibuf;
+ }
+ u->updateDynamicBuffer(ubuf, dc.buf.ubufOffset, DrawCall::UBUF_SIZE, dc.uniforms.data);
+ }
+
+ if (!srb) {
+ srb = rhi->newShaderResourceBindings();
+ srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, ubufVisibility, ubuf, DrawCall::UBUF_SIZE) });
+ if (!srb->build())
+ return;
+ }
+}
+
+void RhiVisualizer::ClipVis::releaseResources()
+{
+ delete srb;
+ srb = nullptr;
+
+ delete ubuf;
+ ubuf = nullptr;
+
+ delete ibuf;
+ ibuf = nullptr;
+
+ delete vbuf;
+ vbuf = nullptr;
+}
+
+void RhiVisualizer::ClipVis::render(QRhiCommandBuffer *cb)
+{
+ visualizer->recordDrawCalls(drawCalls, cb, srb);
+}
+
+void RhiVisualizer::OverdrawVis::gather(Node *n)
+{
+ if (n->type() == QSGNode::GeometryNodeType && n->element()->batch) {
+ QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix;
+ matrix(2, 2) = visualizer->m_renderer->m_zRange;
+ matrix(2, 3) = 1.0f - n->element()->order * visualizer->m_renderer->m_zRange;
+
+ if (n->element()->batch->root)
+ matrix = matrix * qsg_matrixForRoot(n->element()->batch->root);
+
+ QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode);
+ matrix = matrix * *gn->matrix();
+
+ QSGGeometry *g = gn->geometry();
+ if (g->attributeCount() >= 1) {
+ DrawCall dc;
+ memcpy(dc.uniforms.data, matrix.constData(), 64);
+ memcpy(dc.uniforms.data + 64, rotation.constData(), 64);
+
+ float c[4];
+ const float ca = 0.33f;
+ if (n->element()->batch->isOpaque) {
+ c[0] = ca * 0.3f; c[1] = ca * 1.0f; c[2] = ca * 0.3f; c[3] = ca;
+ } else {
+ c[0] = ca * 1.0f; c[1] = ca * 0.3f; c[2] = ca * 0.3f; c[3] = ca;
+ }
+ memcpy(dc.uniforms.data + 128, c, 16);
+ float pattern = 0.0f;
+ memcpy(dc.uniforms.data + 144, &pattern, 4);
+ qint32 projection = 1;
+ memcpy(dc.uniforms.data + 148, &projection, 4);
+
+ fillVertexIndex(&dc, g, true, false);
+ drawCalls.append(dc);
+ }
+ }
+
+ SHADOWNODE_TRAVERSE(n) {
+ gather(child);
+ }
+}
+
+void RhiVisualizer::OverdrawVis::prepare(Node *n, RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u)
+{
+ this->visualizer = visualizer;
+
+ step += float(M_PI * 2 / 1000.0);
+ if (step > float(M_PI * 2))
+ step = 0.0f;
+
+ const float yfix = rhi->isYUpInNDC() ? 1.0f : -1.0f;
+ rotation.setToIdentity();
+ rotation.translate(0.0f, 0.5f * yfix, 4.0f);
+ rotation.scale(2.0f, 2.0f, 1.0f);
+ rotation.rotate(-30.0f * yfix, 1.0f, 0.0f, 0.0f);
+ rotation.rotate(80.0f * std::sin(step), 0.0f, 1.0f, 0.0f);
+ rotation.translate(0.0f, 0.0f, -1.0f);
+
+ drawCalls.clear();
+ gather(n);
+
+ if (!box.vbuf) {
+ const float v[] = {
+ // lower
+ -1, 1, 0, 1, 1, 0,
+ -1, 1, 0, -1, -1, 0,
+ 1, 1, 0, 1, -1, 0,
+ -1, -1, 0, 1, -1, 0,
+
+ // upper
+ -1, 1, 1, 1, 1, 1,
+ -1, 1, 1, -1, -1, 1,
+ 1, 1, 1, 1, -1, 1,
+ -1, -1, 1, 1, -1, 1,
+
+ // sides
+ -1, -1, 0, -1, -1, 1,
+ 1, -1, 0, 1, -1, 1,
+ -1, 1, 0, -1, 1, 1,
+ 1, 1, 0, 1, 1, 1
+ };
+ box.vbuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(v));
+ if (!box.vbuf->build())
+ return;
+ u->uploadStaticBuffer(box.vbuf, v);
+ }
+
+ if (!box.ubuf) {
+ box.ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, DrawCall::UBUF_SIZE);
+ if (!box.ubuf->build())
+ return;
+ QMatrix4x4 ident;
+ u->updateDynamicBuffer(box.ubuf, 0, 64, ident.constData());
+ float color[4] = { 0.5f, 0.5f, 1.0f, 1.0f };
+ u->updateDynamicBuffer(box.ubuf, 128, 16, color);
+ float pattern = 0.0f;
+ u->updateDynamicBuffer(box.ubuf, 144, 4, &pattern);
+ qint32 projection = 1;
+ u->updateDynamicBuffer(box.ubuf, 148, 4, &projection);
+ }
+
+ u->updateDynamicBuffer(box.ubuf, 64, 64, rotation.constData());
+
+ if (!box.srb) {
+ box.srb = rhi->newShaderResourceBindings();
+ box.srb->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, ubufVisibility, box.ubuf) });
+ if (!box.srb->build())
+ return;
+ }
+
+ if (!box.ps) {
+ box.ps = rhi->newGraphicsPipeline();
+ box.ps->setTopology(QRhiGraphicsPipeline::Lines);
+ box.ps->setLineWidth(2); // may be be ignored (D3D, Metal), but may be used on GL and Vulkan
+ QRhiGraphicsPipeline::TargetBlend blend;
+ blend.enable = true;
+ blend.srcColor = QRhiGraphicsPipeline::One;
+ blend.dstColor = QRhiGraphicsPipeline::One;
+ blend.srcAlpha = QRhiGraphicsPipeline::One;
+ blend.dstAlpha = QRhiGraphicsPipeline::One;
+ box.ps->setTargetBlends({ blend });
+ box.ps->setShaderStages({ { QRhiShaderStage::Vertex, visualizer->m_vs },
+ { QRhiShaderStage::Fragment, visualizer->m_fs } });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({ { 3 * sizeof(float) } });
+ inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float3, 0 } });
+ box.ps->setVertexInputLayout(inputLayout);
+ box.ps->setShaderResourceBindings(box.srb);
+ box.ps->setRenderPassDescriptor(visualizer->m_renderer->renderPassDescriptor());
+ if (!box.ps->build())
+ return;
+ }
+
+ if (drawCalls.isEmpty())
+ return;
+
+ const int ubufAlign = rhi->ubufAlignment();
+ int vbufOffset = 0;
+ int ibufOffset = 0;
+ int ubufOffset = 0;
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ dc.buf.vbufOffset = aligned(vbufOffset, 4);
+ vbufOffset = dc.buf.vbufOffset + dc.vertex.count * dc.vertex.stride;
+
+ dc.buf.ibufOffset = aligned(ibufOffset, 4);
+ ibufOffset = dc.buf.ibufOffset + dc.index.count * dc.index.stride;
+
+ dc.buf.ubufOffset = aligned(ubufOffset, ubufAlign);
+ ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE;
+ }
+
+ ensureBuffer(rhi, &vbuf, QRhiBuffer::VertexBuffer, vbufOffset);
+ if (ibufOffset)
+ ensureBuffer(rhi, &ibuf, QRhiBuffer::IndexBuffer, ibufOffset);
+ const int ubufSize = ubufOffset;
+ ensureBuffer(rhi, &ubuf, QRhiBuffer::UniformBuffer, ubufSize);
+
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ u->updateDynamicBuffer(vbuf, dc.buf.vbufOffset, dc.vertex.count * dc.vertex.stride, dc.vertex.data);
+ dc.buf.vbuf = vbuf;
+ if (dc.index.count) {
+ u->updateDynamicBuffer(ibuf, dc.buf.ibufOffset, dc.index.count * dc.index.stride, dc.index.data);
+ dc.buf.ibuf = ibuf;
+ }
+ u->updateDynamicBuffer(ubuf, dc.buf.ubufOffset, DrawCall::UBUF_SIZE, dc.uniforms.data);
+ }
+
+ if (!srb) {
+ srb = rhi->newShaderResourceBindings();
+ srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, ubufVisibility, ubuf, DrawCall::UBUF_SIZE) });
+ if (!srb->build())
+ return;
+ }
+}
+
+void RhiVisualizer::OverdrawVis::releaseResources()
+{
+ delete srb;
+ srb = nullptr;
+
+ delete ubuf;
+ ubuf = nullptr;
+
+ delete ibuf;
+ ibuf = nullptr;
+
+ delete vbuf;
+ vbuf = nullptr;
+
+ delete box.ps;
+ box.ps = nullptr;
+
+ delete box.srb;
+ box.srb = nullptr;
+
+ delete box.ubuf;
+ box.ubuf = nullptr;
+
+ delete box.vbuf;
+ box.vbuf = nullptr;
+}
+
+void RhiVisualizer::OverdrawVis::render(QRhiCommandBuffer *cb)
+{
+ cb->setGraphicsPipeline(box.ps);
+ cb->setShaderResources();
+ QRhiCommandBuffer::VertexInput vb(box.vbuf, 0);
+ cb->setVertexInput(0, 1, &vb);
+ cb->draw(24);
+
+ visualizer->recordDrawCalls(drawCalls, cb, srb, true);
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgrhivisualizer_p.h b/src/quick/scenegraph/coreapi/qsgrhivisualizer_p.h
new file mode 100644
index 0000000000..1ce52a81f1
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgrhivisualizer_p.h
@@ -0,0 +1,233 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
+** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
+** 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 QSGRHIVISUALIZER_P_H
+#define QSGRHIVISUALIZER_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 "qsgbatchrenderer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QSGBatchRenderer
+{
+
+class RhiVisualizer : public Visualizer
+{
+public:
+ RhiVisualizer(Renderer *renderer);
+ ~RhiVisualizer();
+
+ void prepareVisualize() override;
+ void visualize() override;
+
+ void releaseResources() override;
+
+ struct DrawCall
+ {
+ static const int UBUF_SIZE = 152; // visualization.vert/frag
+ struct {
+ char data[UBUF_SIZE]; // matrix, rotation, color, pattern, projection
+ } uniforms;
+ struct {
+ QRhiGraphicsPipeline::Topology topology;
+ QRhiVertexInputAttribute::Format format;
+ int count;
+ int stride;
+ const void *data; // only when using own vbuf
+ } vertex;
+ struct {
+ QRhiCommandBuffer::IndexFormat format;
+ int count;
+ int stride;
+ const void *data; // only when using own ibuf
+ } index;
+ struct {
+ QRhiBuffer *vbuf; // either same for all draw calls and owned by the *Vis, or points to a Batch.Buffer.vbo.buf
+ int vbufOffset;
+ QRhiBuffer *ibuf; // same, but for index
+ int ibufOffset;
+ int ubufOffset;
+ } buf;
+ };
+
+private:
+ QShader m_vs;
+ QShader m_fs;
+
+ void recordDrawCalls(const QVector<DrawCall> &drawCalls,
+ QRhiCommandBuffer *cb,
+ QRhiShaderResourceBindings *srb,
+ bool blendOneOne = false);
+
+ class PipelineCache {
+ public:
+ QRhiGraphicsPipeline *pipeline(RhiVisualizer *visualizer,
+ QRhi *rhi,
+ QRhiShaderResourceBindings *srb,
+ QRhiRenderPassDescriptor *rpDesc,
+ QRhiGraphicsPipeline::Topology topology,
+ QRhiVertexInputAttribute::Format vertexFormat,
+ quint32 vertexStride,
+ bool blendOneOne);
+ void releaseResources();
+ private:
+ struct Pipeline {
+ QRhiGraphicsPipeline::Topology topology;
+ QRhiVertexInputAttribute::Format format;
+ quint32 stride;
+ QRhiGraphicsPipeline *ps;
+ };
+ QVarLengthArray<Pipeline, 16> pipelines;
+ };
+
+ PipelineCache m_pipelines;
+
+ class Fade {
+ public:
+ void prepare(RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u, QRhiRenderPassDescriptor *rpDesc);
+ void releaseResources();
+ void render(QRhiCommandBuffer *cb);
+ private:
+ RhiVisualizer *visualizer;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiGraphicsPipeline *ps = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ } m_fade;
+
+ class ChangeVis {
+ public:
+ void prepare(Node *n, RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u);
+ void releaseResources();
+ void render(QRhiCommandBuffer *cb);
+ private:
+ void gather(Node *n);
+ RhiVisualizer *visualizer;
+ QVector<DrawCall> drawCalls;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ibuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ } m_changeVis;
+
+ class BatchVis {
+ public:
+ void prepare(const QDataBuffer<Batch *> &opaqueBatches,
+ const QDataBuffer<Batch *> &alphaBatches,
+ RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u,
+ bool forceUintIndex);
+ void releaseResources();
+ void render(QRhiCommandBuffer *cb);
+ private:
+ void gather(Batch *b);
+ RhiVisualizer *visualizer;
+ bool forceUintIndex;
+ QVector<DrawCall> drawCalls;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ } m_batchVis;
+
+ class ClipVis {
+ public:
+ void prepare(QSGNode *node, RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u);
+ void releaseResources();
+ void render(QRhiCommandBuffer *cb);
+ private:
+ void gather(QSGNode *node);
+ RhiVisualizer *visualizer;
+ QVector<DrawCall> drawCalls;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ibuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ } m_clipVis;
+
+ class OverdrawVis {
+ public:
+ void prepare(Node *n, RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u);
+ void releaseResources();
+ void render(QRhiCommandBuffer *cb);
+ private:
+ void gather(Node *n);
+ RhiVisualizer *visualizer;
+ QVector<DrawCall> drawCalls;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ibuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ float step = 0.0f;
+ QMatrix4x4 rotation;
+ struct {
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ QRhiGraphicsPipeline *ps = nullptr;
+ } box;
+ } m_overdrawVis;
+
+ friend class Fade;
+ friend class PipelineCache;
+ friend class ChangeVis;
+ friend class ClipVis;
+ friend class OverdrawVis;
+};
+
+} // namespace QSGBatchRenderer
+
+QT_END_NAMESPACE
+
+#endif // QSGRHIVISUALIZER_P_H
diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/coreapi/qsgtexture.cpp
index 042eee19f5..edcee96bdb 100644
--- a/src/quick/scenegraph/util/qsgtexture.cpp
+++ b/src/quick/scenegraph/coreapi/qsgtexture.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -38,21 +38,13 @@
****************************************************************************/
#include "qsgtexture_p.h"
-#include <QtQuick/private/qsgcontext_p.h>
-#include <qthread.h>
-#include <qmath.h>
-#include <private/qquickprofiler_p.h>
-#include <private/qqmlglobal_p.h>
-#include <QtGui/qguiapplication.h>
-#include <QtGui/qpa/qplatformnativeinterface.h>
#if QT_CONFIG(opengl)
-# include <qopenglfunctions.h>
# include <QtGui/qopenglcontext.h>
# include <QtGui/qopenglfunctions.h>
-# include <QtGui/private/qopengltextureuploader_p.h>
-# include <private/qsgdefaultrendercontext_p.h>
#endif
+#include <private/qqmlglobal_p.h>
#include <private/qsgmaterialshader_p.h>
+#include <QtGui/private/qrhi_p.h>
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && defined(__GLIBC__)
#define CAN_BACKTRACE_EXECINFO
@@ -71,32 +63,55 @@
#include <QHash>
#endif
-#if QT_CONFIG(opengl)
-static QElapsedTimer qsg_renderer_timer;
-#endif
-
#ifndef QT_NO_DEBUG
static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK");
#endif
+QT_BEGIN_NAMESPACE
-#ifndef GL_BGRA
-#define GL_BGRA 0x80E1
-#endif
+bool operator==(const QSGSamplerDescription &a, const QSGSamplerDescription &b) Q_DECL_NOTHROW
+{
+ return a.filtering == b.filtering
+ && a.mipmapFiltering == b.mipmapFiltering
+ && a.horizontalWrap == b.horizontalWrap
+ && a.verticalWrap == b.verticalWrap
+ && a.anisotropylevel == b.anisotropylevel;
+}
-#ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT
-#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
-#endif
+bool operator!=(const QSGSamplerDescription &a, const QSGSamplerDescription &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
-QT_BEGIN_NAMESPACE
+uint qHash(const QSGSamplerDescription &s, uint seed) Q_DECL_NOTHROW
+{
+ const int f = s.filtering;
+ const int m = s.mipmapFiltering;
+ const int w = s.horizontalWrap;
+ const int a = s.anisotropylevel;
+ return (((f & 7) << 24) | ((m & 7) << 16) | ((w & 7) << 8) | (a & 7)) ^ seed;
+}
-#if QT_CONFIG(opengl) && !defined(QT_NO_DEBUG)
+QSGSamplerDescription QSGSamplerDescription::fromTexture(QSGTexture *t)
+{
+ QSGSamplerDescription s;
+ s.filtering = t->filtering();
+ s.mipmapFiltering = t->mipmapFiltering();
+ s.horizontalWrap = t->horizontalWrapMode();
+ s.verticalWrap = t->verticalWrapMode();
+ s.anisotropylevel = t->anisotropyLevel();
+ return s;
+}
+
+#if QT_CONFIG(opengl)
+#ifndef QT_NO_DEBUG
inline static bool isPowerOfTwo(int x)
{
// Assumption: x >= 1
return x == (x & -x);
}
#endif
+#endif
QSGTexturePrivate::QSGTexturePrivate()
: wrapChanged(false)
@@ -302,10 +317,9 @@ static void qt_debug_remove_texture(QSGTexture* texture)
\since 5.9
*/
-/*!
- \fn QSGTexture::QSGTexture(QSGTexturePrivate &dd)
- \internal
- */
+#ifndef QT_NO_DEBUG
+Q_QUICK_PRIVATE_EXPORT void qsg_set_material_failure();
+#endif
#ifndef QT_NO_DEBUG
Q_GLOBAL_STATIC(QSet<QSGTexture *>, qsg_valid_texture_set)
@@ -344,6 +358,21 @@ QSGTexture::QSGTexture()
}
/*!
+ \internal
+ */
+QSGTexture::QSGTexture(QSGTexturePrivate &dd)
+ : QObject(dd)
+{
+#ifndef QT_NO_DEBUG
+ if (qsg_leak_check)
+ qt_debug_add_texture(this);
+
+ QMutexLocker locker(qsg_valid_texture_mutex());
+ qsg_valid_texture_set()->insert(this);
+#endif
+}
+
+/*!
Destroys the QSGTexture.
*/
QSGTexture::~QSGTexture()
@@ -357,7 +386,6 @@ QSGTexture::~QSGTexture()
#endif
}
-
/*!
\fn void QSGTexture::bind()
@@ -389,7 +417,7 @@ QSGTexture::~QSGTexture()
it to a shader that operates on the texture coordinates 0-1 instead
of the texture subrect inside the atlas.
- If the texture is not part of a texture atlas, this function returns \nullptr.
+ If the texture is not part of a texture atlas, this function returns 0.
Implementations of this function are recommended to return the same instance
for multiple calls to limit memory usage.
@@ -426,6 +454,34 @@ bool QSGTexture::isAtlasTexture() const
*/
/*!
+ Returns a key suitable for comparing textures. Typically used in
+ QSGMaterial::compare() implementations.
+
+ Just comparing QSGTexture pointers is not always sufficient because two
+ QSGTexture instances that refer to the same native texture object
+ underneath should also be considered equal. Hence this function.
+
+ \note Unlike textureId(), implementations of this function are not expected
+ to and should not create any graphics resources (so texture objects) in
+ case there is none yet.
+
+ A QSGTexture that does not have a native texture object underneath is
+ typically not equal to any other QSGTexture. There are exceptions to this,
+ in particular when atlasing is used (where multiple textures share the same
+ atlas texture under the hood), that is then up to the subclass
+ implementations to deal with as appropriate.
+
+ \warning This function can only be called from the rendering thread.
+
+ \since 5.14
+ */
+int QSGTexture::comparisonKey() const
+{
+ Q_D(const QSGTexture);
+ return d->comparisonKey();
+}
+
+/*!
\fn QSize QSGTexture::textureSize() const
Returns the size of the texture.
@@ -579,7 +635,7 @@ QSGTexture::WrapMode QSGTexture::verticalWrapMode() const
If \a force is true, all properties will be updated regardless of weither
they have changed or not.
*/
-void QSGTexture::updateBindOptions(bool force)
+void QSGTexture::updateBindOptions(bool force) // legacy (GL-only)
{
#if QT_CONFIG(opengl)
Q_D(QSGTexture);
@@ -639,185 +695,82 @@ void QSGTexture::updateBindOptions(bool force)
#endif
}
-QSGPlainTexture::QSGPlainTexture()
- : QSGTexture()
- , m_texture_id(0)
- , m_has_alpha(false)
- , m_dirty_texture(false)
- , m_dirty_bind_options(false)
- , m_owns_texture(true)
- , m_mipmaps_generated(false)
- , m_retain_image(false)
-{
-}
+/*!
+ Call this function to enqueue image upload operations to \a
+ resourceUpdates, in case there are any pending ones. When there is no new
+ data (for example, because there was no setImage() since the last call to
+ this function), the function does nothing.
+ Materials involving textures are expected to call this function from their
+ updateSampledImage() implementation, typically without any conditions.
-QSGPlainTexture::~QSGPlainTexture()
+ \note This function is only used when running the graphics API independent
+ rendering path of the scene graph.
+
+ \warning This function can only be called from the rendering thread.
+
+ \since 5.14
+ */
+void QSGTexture::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
{
-#if QT_CONFIG(opengl)
- if (m_texture_id && m_owns_texture && QOpenGLContext::currentContext())
- QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_texture_id);
-#endif
+ Q_D(QSGTexture);
+ d->updateRhiTexture(rhi, resourceUpdates);
}
-void QSGPlainTexture::setImage(const QImage &image)
+/*!
+ \internal
+ */
+void QSGTexture::setWorkResourceUpdateBatch(QRhiResourceUpdateBatch *resourceUpdates)
{
- m_image = image;
- m_texture_size = image.size();
- m_has_alpha = image.hasAlphaChannel();
- m_dirty_texture = true;
- m_dirty_bind_options = true;
- m_mipmaps_generated = false;
- }
+ Q_D(QSGTexture);
+ d->workResourceUpdateBatch = resourceUpdates;
+}
-int QSGPlainTexture::textureId() const
+bool QSGTexturePrivate::hasDirtySamplerOptions() const
{
- if (m_dirty_texture) {
- if (m_image.isNull()) {
- // The actual texture and id will be updated/deleted in a later bind()
- // or ~QSGPlainTexture so just keep it minimal here.
- return 0;
- } else if (m_texture_id == 0){
-#if QT_CONFIG(opengl)
- // Generate a texture id for use later and return it.
- QOpenGLContext::currentContext()->functions()->glGenTextures(1, &const_cast<QSGPlainTexture *>(this)->m_texture_id);
-#endif
- return m_texture_id;
- }
- }
- return m_texture_id;
+ return wrapChanged || filteringChanged || anisotropyChanged;
}
-void QSGPlainTexture::setTextureId(int id)
+void QSGTexturePrivate::resetDirtySamplerOptions()
{
-#if QT_CONFIG(opengl)
- if (m_texture_id && m_owns_texture)
- QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_texture_id);
-#endif
-
- m_texture_id = id;
- m_dirty_texture = false;
- m_dirty_bind_options = true;
- m_image = QImage();
- m_mipmaps_generated = false;
+ wrapChanged = filteringChanged = anisotropyChanged = false;
}
-void QSGPlainTexture::bind()
+int QSGTexturePrivate::comparisonKey() const
{
-#if QT_CONFIG(opengl)
- QOpenGLContext *context = QOpenGLContext::currentContext();
- QOpenGLFunctions *funcs = context->functions();
- if (!m_dirty_texture) {
- funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id);
- if (mipmapFiltering() != QSGTexture::None && !m_mipmaps_generated) {
- funcs->glGenerateMipmap(GL_TEXTURE_2D);
- m_mipmaps_generated = true;
- }
- updateBindOptions(m_dirty_bind_options);
- m_dirty_bind_options = false;
- return;
- }
-
- m_dirty_texture = false;
-
- bool profileFrames = QSG_LOG_TIME_TEXTURE().isDebugEnabled();
- if (profileFrames)
- qsg_renderer_timer.start();
- Q_QUICK_SG_PROFILE_START_SYNCHRONIZED(QQuickProfiler::SceneGraphTexturePrepare,
- QQuickProfiler::SceneGraphTextureDeletion);
-
-
- if (m_image.isNull()) {
- if (m_texture_id && m_owns_texture) {
- funcs->glDeleteTextures(1, &m_texture_id);
- qCDebug(QSG_LOG_TIME_TEXTURE, "plain texture deleted in %dms - %dx%d",
- (int) qsg_renderer_timer.elapsed(),
- m_texture_size.width(),
- m_texture_size.height());
- Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphTextureDeletion,
- QQuickProfiler::SceneGraphTextureDeletionDelete);
- }
- m_texture_id = 0;
- m_texture_size = QSize();
- m_has_alpha = false;
-
- return;
- }
-
- if (m_texture_id == 0)
- funcs->glGenTextures(1, &m_texture_id);
- funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id);
-
- qint64 bindTime = 0;
- if (profileFrames)
- bindTime = qsg_renderer_timer.nsecsElapsed();
- Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare,
- QQuickProfiler::SceneGraphTexturePrepareBind);
-
- // ### TODO: check for out-of-memory situations...
-
- QOpenGLTextureUploader::BindOptions options = QOpenGLTextureUploader::PremultipliedAlphaBindOption;
-
- // Downscale the texture to fit inside the max texture limit if it is too big.
- // It would be better if the image was already downscaled to the right size,
- // but this information is not always available at that time, so as a last
- // resort we can do it here. Texture coordinates are normalized, so it
- // won't cause any problems and actual texture sizes will be written
- // based on QSGTexture::textureSize which is updated after this, so that
- // should be ok.
- int max;
- if (auto rc = QSGDefaultRenderContext::from(context))
- max = rc->maxTextureSize();
- else
- funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
-
- m_texture_size = m_texture_size.boundedTo(QSize(max, max));
-
- // Scale to a power of two size if mipmapping is requested and the
- // texture is npot and npot textures are not properly supported.
- if (mipmapFiltering() != QSGTexture::None
- && !funcs->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)) {
- options |= QOpenGLTextureUploader::PowerOfTwoBindOption;
- }
-
- updateBindOptions(m_dirty_bind_options);
+ // Must be overridden in subclasses but we cannot make this pure virtual
+ // before Qt 6 because the simple QSGTexture ctor must be kept working.
+ Q_Q(const QSGTexture);
+ return q->textureId(); // this is semantically wrong but at least compatible with existing, non-RHI-aware subclasses
+}
- QOpenGLTextureUploader::textureImage(GL_TEXTURE_2D, m_image, options, QSize(max, max));
+/*!
+ \internal
- qint64 uploadTime = 0;
- if (profileFrames)
- uploadTime = qsg_renderer_timer.nsecsElapsed();
- Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare,
- QQuickProfiler::SceneGraphTexturePrepareUpload);
+ \return the QRhiTexture for this QSGTexture or null if there is none.
- if (mipmapFiltering() != QSGTexture::None) {
- funcs->glGenerateMipmap(GL_TEXTURE_2D);
- m_mipmaps_generated = true;
- }
+ Unlike textureId(), this function is not expected to create a new
+ QRhiTexture in case there is none. Just return null in that case. The
+ expectation towards the renderer is that a null texture leads to using a
+ transparent, dummy texture instead.
- qint64 mipmapTime = 0;
- if (profileFrames) {
- mipmapTime = qsg_renderer_timer.nsecsElapsed();
- qCDebug(QSG_LOG_TIME_TEXTURE,
- "plain texture uploaded in: %dms (%dx%d), bind=%d, upload=%d, mipmap=%d%s",
- int(mipmapTime / 1000000),
- m_texture_size.width(), m_texture_size.height(),
- int(bindTime / 1000000),
- int((uploadTime - bindTime)/1000000),
- int((mipmapTime - uploadTime)/1000000),
- m_texture_size != m_image.size() ? " (scaled to GL_MAX_TEXTURE_SIZE)" : "");
- }
- Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphTexturePrepare,
- QQuickProfiler::SceneGraphTexturePrepareMipmap);
+ \note This function is only used when running the graphics API independent
+ rendering path of the scene graph.
- m_texture_rect = QRectF(0, 0, 1, 1);
+ \warning This function can only be called from the rendering thread.
- m_dirty_bind_options = false;
- if (!m_retain_image)
- m_image = QImage();
-#endif
+ \since 5.14
+ */
+QRhiTexture *QSGTexturePrivate::rhiTexture() const
+{
+ return nullptr;
}
+void QSGTexturePrivate::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_UNUSED(rhi);
+ Q_UNUSED(resourceUpdates);
+}
/*!
\class QSGDynamicTexture
@@ -843,9 +796,14 @@ void QSGPlainTexture::bind()
returns false.
*/
-
+/*!
+ \internal
+ */
+QSGDynamicTexture::QSGDynamicTexture(QSGTexturePrivate &dd)
+ : QSGTexture(dd)
+{
+}
QT_END_NAMESPACE
#include "moc_qsgtexture.cpp"
-#include "moc_qsgtexture_p.cpp"
diff --git a/src/quick/scenegraph/util/qsgtexture.h b/src/quick/scenegraph/coreapi/qsgtexture.h
index 7bd57a16e3..2efdd8b7c3 100644
--- a/src/quick/scenegraph/util/qsgtexture.h
+++ b/src/quick/scenegraph/coreapi/qsgtexture.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -47,6 +47,10 @@
QT_BEGIN_NAMESPACE
class QSGTexturePrivate;
+class QRhi;
+class QRhiTexture;
+class QRhiResourceUpdateBatch;
+
class Q_QUICK_EXPORT QSGTexture : public QObject
{
Q_OBJECT
@@ -76,7 +80,7 @@ public:
Anisotropy16x
};
- virtual int textureId() const = 0;
+ virtual int textureId() const = 0; // ### Qt 6: remove
virtual QSize textureSize() const = 0;
virtual bool hasAlphaChannel() const = 0;
virtual bool hasMipmaps() const = 0;
@@ -107,6 +111,13 @@ public:
inline QRectF convertToNormalizedSourceRect(const QRectF &rect) const;
+ // ### Qt 6: make these virtual
+ int comparisonKey() const;
+ void updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates);
+
+ // ### Qt 6: make this an argument for removedFromAtlas()
+ void setWorkResourceUpdateBatch(QRhiResourceUpdateBatch *resourceUpdates);
+
protected:
QSGTexture(QSGTexturePrivate &dd);
};
@@ -125,12 +136,16 @@ QRectF QSGTexture::convertToNormalizedSourceRect(const QRectF &rect) const
rect.height() * sy);
}
-
class Q_QUICK_EXPORT QSGDynamicTexture : public QSGTexture
{
Q_OBJECT
+
public:
+ QSGDynamicTexture() = default;
virtual bool updateTexture() = 0;
+
+protected:
+ QSGDynamicTexture(QSGTexturePrivate &dd);
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgtexture_p.h b/src/quick/scenegraph/coreapi/qsgtexture_p.h
new file mode 100644
index 0000000000..cb59d32012
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgtexture_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 QSGTEXTURE_P_H
+#define QSGTEXTURE_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 <QtQuick/private/qtquickglobal_p.h>
+#include <private/qobject_p.h>
+#include "qsgtexture.h"
+
+QT_BEGIN_NAMESPACE
+
+struct QSGSamplerDescription
+{
+ QSGTexture::Filtering filtering = QSGTexture::Nearest;
+ QSGTexture::Filtering mipmapFiltering = QSGTexture::None;
+ QSGTexture::WrapMode horizontalWrap = QSGTexture::ClampToEdge;
+ QSGTexture::WrapMode verticalWrap = QSGTexture::ClampToEdge;
+ QSGTexture::AnisotropyLevel anisotropylevel = QSGTexture::AnisotropyNone;
+
+ static QSGSamplerDescription fromTexture(QSGTexture *t);
+};
+
+Q_DECLARE_TYPEINFO(QSGSamplerDescription, Q_MOVABLE_TYPE);
+
+bool operator==(const QSGSamplerDescription &a, const QSGSamplerDescription &b) Q_DECL_NOTHROW;
+bool operator!=(const QSGSamplerDescription &a, const QSGSamplerDescription &b) Q_DECL_NOTHROW;
+uint qHash(const QSGSamplerDescription &s, uint seed = 0) Q_DECL_NOTHROW;
+
+class Q_QUICK_PRIVATE_EXPORT QSGTexturePrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QSGTexture)
+public:
+ QSGTexturePrivate();
+ static QSGTexturePrivate *get(QSGTexture *t) { return t->d_func(); }
+ void resetDirtySamplerOptions();
+ bool hasDirtySamplerOptions() const;
+
+ virtual QRhiTexture *rhiTexture() const;
+
+ // ### Qt 6: these should be virtuals in the public class instead
+ virtual int comparisonKey() const; // ### Qt 6: pure virtual
+ virtual void updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates);
+
+ QRhiResourceUpdateBatch *workResourceUpdateBatch = nullptr; // ### Qt 6: remove
+
+ uint wrapChanged : 1;
+ uint filteringChanged : 1;
+ uint anisotropyChanged : 1;
+
+ uint horizontalWrap : 2;
+ uint verticalWrap : 2;
+ uint mipmapMode : 2;
+ uint filterMode : 2;
+ uint anisotropyLevel: 3;
+};
+
+Q_QUICK_PRIVATE_EXPORT bool qsg_safeguard_texture(QSGTexture *);
+
+QT_END_NAMESPACE
+
+#endif // QSGTEXTURE_P_H
diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp
index 252e5a9c55..f7b07d724a 100644
--- a/src/quick/scenegraph/qsgadaptationlayer.cpp
+++ b/src/quick/scenegraph/qsgadaptationlayer.cpp
@@ -306,6 +306,19 @@ void QSGDistanceFieldGlyphCache::updateTexture(uint oldTex, uint newTex, const Q
}
}
+void QSGDistanceFieldGlyphCache::updateRhiTexture(QRhiTexture *oldTex, QRhiTexture *newTex, const QSize &newTexSize)
+{
+ int count = m_textures.count();
+ for (int i = 0; i < count; ++i) {
+ Texture &tex = m_textures[i];
+ if (tex.texture == oldTex) {
+ tex.texture = newTex;
+ tex.size = newTexSize;
+ return;
+ }
+ }
+}
+
#if defined(QSG_DISTANCEFIELD_CACHE_DEBUG)
#include <QtGui/qopenglfunctions.h>
@@ -526,14 +539,6 @@ void QSGNodeVisitorEx::visitChildren(QSGNode *node)
}
#ifndef QT_NO_DEBUG_STREAM
-QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::InputParameter &p)
-{
- QDebugStateSaver saver(debug);
- debug.space();
- debug << p.semanticName << "semindex" << p.semanticIndex;
- return debug;
-}
-
QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &v)
{
QDebugStateSaver saver(debug);
@@ -564,6 +569,14 @@ QDebug operator<<(QDebug debug, const QSGShaderEffectNode::VariableData &vd)
}
#endif
+/*!
+ \internal
+ */
+QSGLayer::QSGLayer(QSGTexturePrivate &dd)
+ : QSGDynamicTexture(dd)
+{
+}
+
QT_END_NAMESPACE
#include "moc_qsgadaptationlayer_p.cpp"
diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h
index 58ecae94e7..6baee33b53 100644
--- a/src/quick/scenegraph/qsgadaptationlayer_p.h
+++ b/src/quick/scenegraph/qsgadaptationlayer_p.h
@@ -64,6 +64,7 @@
#include <QtGui/private/qdatabuffer_p.h>
#include <private/qdistancefield_p.h>
#include <private/qintrusivelist_p.h>
+#include <QtGui/private/qshader_p.h>
// ### remove
#include <QtQuick/private/qquicktext_p.h>
@@ -82,6 +83,7 @@ class QSGRootNode;
class QSGSpriteNode;
class QSGRenderNode;
class QSGRenderContext;
+class QRhiTexture;
class Q_QUICK_PRIVATE_EXPORT QSGNodeVisitorEx
{
@@ -216,6 +218,9 @@ public:
Q_SIGNALS:
void updateRequested();
void scheduledUpdateCompleted();
+
+protected:
+ QSGLayer(QSGTexturePrivate &dd);
};
#if QT_CONFIG(quick_sprite)
@@ -266,27 +271,26 @@ public:
Sampler,
Texture // for APIs with separate texture and sampler objects
};
- struct InputParameter {
- InputParameter() {}
- // Semantics use the D3D keys (POSITION, TEXCOORD).
- // Attribute name based APIs can map based on pre-defined names.
- QByteArray semanticName;
- int semanticIndex = 0;
- };
struct Variable {
Variable() {}
VariableType type = Constant;
QByteArray name;
uint offset = 0; // for cbuffer members
uint size = 0; // for cbuffer members
- int bindPoint = 0; // for textures and samplers; for register-based APIs
+ int bindPoint = 0; // for textures/samplers, where applicable
};
- QByteArray blob; // source or bytecode
+ QString name; // optional, f.ex. the filename, used for debugging purposes only
+ QByteArray blob; // source or bytecode (when not using QRhi)
+ QShader rhiShader;
Type type;
- QVector<InputParameter> inputParameters;
QVector<Variable> variables;
uint constantDataSize;
+
+ // Vertex inputs are not tracked here as QSGGeometry::AttributeSet
+ // hardwires that anyways so it is up to the shader to provide
+ // compatible inputs (e.g. compatible with
+ // QSGGeometry::defaultAttributes_TexturedPoint2D()).
};
virtual void prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result) = 0;
@@ -298,7 +302,6 @@ Q_SIGNALS:
};
#ifndef QT_NO_DEBUG_STREAM
-Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::InputParameter &p);
Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &v);
#endif
@@ -439,10 +442,18 @@ public:
struct Texture {
uint textureId = 0;
+ QRhiTexture *texture = nullptr;
QSize size;
-
- Texture() : size(QSize()) { }
- bool operator == (const Texture &other) const { return textureId == other.textureId; }
+ bool rhiBased = false;
+
+ bool operator == (const Texture &other) const {
+ if (rhiBased != other.rhiBased)
+ return false;
+ if (rhiBased)
+ return texture == other.texture;
+ else
+ return textureId == other.textureId;
+ }
};
const QRawFont &referenceFont() const { return m_referenceFont; }
@@ -474,6 +485,8 @@ public:
virtual void unregisterOwnerElement(QQuickItem *ownerElement);
virtual void processPendingGlyphs();
+ virtual bool eightBitFormatIsAlphaSwizzled() const = 0;
+
protected:
struct GlyphPosition {
glyph_t glyph;
@@ -501,6 +514,7 @@ protected:
inline void removeGlyph(glyph_t glyph);
void updateTexture(uint oldTex, uint newTex, const QSize &newTexSize);
+ void updateRhiTexture(QRhiTexture *oldTex, QRhiTexture *newTex, const QSize &newTexSize);
inline bool containsGlyph(glyph_t glyph);
uint textureIdForGlyph(glyph_t glyph) const;
diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp
index 53648e352a..17eb1e312c 100644
--- a/src/quick/scenegraph/qsgcontext.cpp
+++ b/src/quick/scenegraph/qsgcontext.cpp
@@ -332,15 +332,61 @@ QSGRenderContext::~QSGRenderContext()
{
}
-void QSGRenderContext::initialize(void *context)
+void QSGRenderContext::initialize(const InitParams *params)
{
- Q_UNUSED(context);
+ Q_UNUSED(params);
}
void QSGRenderContext::invalidate()
{
}
+void QSGRenderContext::prepareSync(qreal devicePixelRatio)
+{
+ Q_UNUSED(devicePixelRatio);
+}
+
+void QSGRenderContext::beginNextFrame(QSGRenderer *renderer,
+ RenderPassCallback mainPassRecordingStart,
+ RenderPassCallback mainPassRecordingEnd,
+ void *callbackUserData)
+{
+ Q_UNUSED(renderer);
+ Q_UNUSED(mainPassRecordingStart);
+ Q_UNUSED(mainPassRecordingEnd);
+ Q_UNUSED(callbackUserData);
+}
+
+void QSGRenderContext::endNextFrame(QSGRenderer *renderer)
+{
+ Q_UNUSED(renderer);
+}
+
+void QSGRenderContext::beginNextRhiFrame(QSGRenderer *renderer,
+ QRhiRenderTarget *rt, QRhiRenderPassDescriptor *rp, QRhiCommandBuffer *cb,
+ RenderPassCallback mainPassRecordingStart,
+ RenderPassCallback mainPassRecordingEnd,
+ void *callbackUserData)
+{
+ Q_UNUSED(renderer);
+ Q_UNUSED(rt);
+ Q_UNUSED(rp);
+ Q_UNUSED(cb);
+ Q_UNUSED(mainPassRecordingStart);
+ Q_UNUSED(mainPassRecordingEnd);
+ Q_UNUSED(callbackUserData);
+}
+
+void QSGRenderContext::renderNextRhiFrame(QSGRenderer *renderer)
+{
+ Q_UNUSED(renderer);
+}
+
+void QSGRenderContext::endNextRhiFrame(QSGRenderer *renderer)
+{
+ Q_UNUSED(renderer);
+}
+
void QSGRenderContext::endSync()
{
qDeleteAll(m_texturesToDelete);
@@ -362,6 +408,11 @@ void QSGRenderContext::registerFontengineForCleanup(QFontEngine *engine)
m_fontEnginesToClean << engine;
}
+QRhi *QSGRenderContext::rhi() const
+{
+ return nullptr;
+}
+
/*!
Factory function for the scene graph renderers.
diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h
index 282ce828af..244bcfabd1 100644
--- a/src/quick/scenegraph/qsgcontext_p.h
+++ b/src/quick/scenegraph/qsgcontext_p.h
@@ -89,6 +89,10 @@ class QSGImageNode;
class QSGNinePatchNode;
class QSGSpriteNode;
class QSGRenderContext;
+class QRhi;
+class QRhiRenderTarget;
+class QRhiRenderPassDescriptor;
+class QRhiCommandBuffer;
Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERLOOP)
Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_COMPILATION)
@@ -119,7 +123,7 @@ public:
QSGInternalRectangleNode *createInternalRectangleNode(const QRectF &rect, const QColor &c);
virtual QSGInternalRectangleNode *createInternalRectangleNode() = 0;
- virtual QSGInternalImageNode *createInternalImageNode() = 0;
+ virtual QSGInternalImageNode *createInternalImageNode(QSGRenderContext *renderContext) = 0;
virtual QSGPainterNode *createPainterNode(QQuickPaintedItem *item) = 0;
virtual QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) = 0;
virtual QSGLayer *createLayer(QSGRenderContext *renderContext) = 0;
@@ -164,9 +168,28 @@ public:
QSGContext *sceneGraphContext() const { return m_sg; }
virtual bool isValid() const { return true; }
- virtual void initialize(void *context);
+ struct InitParams { };
+ virtual void initialize(const InitParams *params);
virtual void invalidate();
+
+ using RenderPassCallback = void (*)(void *);
+
+ virtual void prepareSync(qreal devicePixelRatio);
+ virtual void beginNextFrame(QSGRenderer *renderer,
+ RenderPassCallback mainPassRecordingStart,
+ RenderPassCallback mainPassRecordingEnd,
+ void *callbackUserData);
virtual void renderNextFrame(QSGRenderer *renderer, uint fboId) = 0;
+ virtual void endNextFrame(QSGRenderer *renderer);
+
+ virtual void beginNextRhiFrame(QSGRenderer *renderer,
+ QRhiRenderTarget *rt, QRhiRenderPassDescriptor *rp, QRhiCommandBuffer *cb,
+ RenderPassCallback mainPassRecordingStart,
+ RenderPassCallback mainPassRecordingEnd,
+ void *callbackUserData);
+ virtual void renderNextRhiFrame(QSGRenderer *renderer);
+ virtual void endNextRhiFrame(QSGRenderer *renderer);
+
virtual void endSync();
virtual QSGDistanceFieldGlyphCache *distanceFieldGlyphCache(const QRawFont &font);
@@ -182,6 +205,8 @@ public:
void registerFontengineForCleanup(QFontEngine *engine);
+ virtual QRhi *rhi() const;
+
Q_SIGNALS:
void initialized();
void invalidated();
diff --git a/src/quick/scenegraph/qsgdefaultcontext.cpp b/src/quick/scenegraph/qsgdefaultcontext.cpp
index 70b74b8877..ea01b0a3b4 100644
--- a/src/quick/scenegraph/qsgdefaultcontext.cpp
+++ b/src/quick/scenegraph/qsgdefaultcontext.cpp
@@ -45,8 +45,9 @@
#include <QtQuick/private/qsgdefaultglyphnode_p.h>
#include <QtQuick/private/qsgdistancefieldglyphnode_p.h>
#include <QtQuick/private/qsgdistancefieldglyphnode_p_p.h>
-#include <QtQuick/private/qsgrenderloop_p.h>
-#include <QtQuick/private/qsgdefaultlayer_p.h>
+#include <QtQuick/private/qsgopengllayer_p.h>
+#include <QtQuick/private/qsgrhisupport_p.h>
+#include <QtQuick/private/qsgrhilayer_p.h>
#include <QtQuick/private/qsgdefaultrendercontext_p.h>
#include <QtQuick/private/qsgdefaultrectanglenode_p.h>
#include <QtQuick/private/qsgdefaultimagenode_p.h>
@@ -54,25 +55,29 @@
#if QT_CONFIG(quick_sprite)
#include <QtQuick/private/qsgdefaultspritenode_p.h>
#endif
+#include <QtQuick/private/qsgrhishadereffectnode_p.h>
#include <QtGui/QOpenGLContext>
#include <QtGui/QOpenGLFramebufferObject>
-#include <QtQuick/QQuickWindow>
+#include <QtQuick/private/qquickwindow_p.h>
#include <private/qqmlglobal_p.h>
#include <algorithm>
+#include <QtGui/private/qrhi_p.h>
+#include <QtGui/private/qrhigles2_p.h>
+
QT_BEGIN_NAMESPACE
namespace QSGMultisampleAntialiasing {
class ImageNode : public QSGDefaultInternalImageNode {
public:
+ ImageNode(QSGDefaultRenderContext *rc) : QSGDefaultInternalImageNode(rc) { }
void setAntialiasing(bool) override { }
};
-
class RectangleNode : public QSGDefaultInternalRectangleNode {
public:
void setAntialiasing(bool) override { }
@@ -118,7 +123,7 @@ void QSGDefaultContext::renderContextInitialized(QSGRenderContext *renderContext
{
m_mutex.lock();
- auto openglRenderContext = static_cast<const QSGDefaultRenderContext *>(renderContext);
+ auto rc = static_cast<const QSGDefaultRenderContext *>(renderContext);
if (m_antialiasingMethod == UndecidedAntialiasing) {
if (Q_UNLIKELY(qEnvironmentVariableIsSet("QSG_ANTIALIASING_METHOD"))) {
const QByteArray aaType = qgetenv("QSG_ANTIALIASING_METHOD");
@@ -127,12 +132,8 @@ void QSGDefaultContext::renderContextInitialized(QSGRenderContext *renderContext
else if (aaType == "vertex")
m_antialiasingMethod = VertexAntialiasing;
}
- if (m_antialiasingMethod == UndecidedAntialiasing) {
- if (openglRenderContext->openglContext()->format().samples() > 0)
- m_antialiasingMethod = MsaaAntialiasing;
- else
- m_antialiasingMethod = VertexAntialiasing;
- }
+ if (m_antialiasingMethod == UndecidedAntialiasing)
+ m_antialiasingMethod = rc->msaaSampleCount() > 1 ? MsaaAntialiasing : VertexAntialiasing;
}
// With OpenGL ES, except for Angle on Windows, use GrayAntialiasing, unless
@@ -141,15 +142,23 @@ void QSGDefaultContext::renderContextInitialized(QSGRenderContext *renderContext
if (!m_distanceFieldAntialiasingDecided) {
m_distanceFieldAntialiasingDecided = true;
#ifndef Q_OS_WIN32
- if (openglRenderContext->openglContext()->isOpenGLES())
- m_distanceFieldAntialiasing = QSGGlyphNode::GrayAntialiasing;
+ if (rc->rhi()) {
+ if (rc->rhi()->backend() == QRhi::OpenGLES2
+ && static_cast<const QRhiGles2NativeHandles *>(rc->rhi()->nativeHandles())->context->isOpenGLES())
+ {
+ m_distanceFieldAntialiasing = QSGGlyphNode::GrayAntialiasing;
+ }
+ } else {
+ if (rc->openglContext()->isOpenGLES())
+ m_distanceFieldAntialiasing = QSGGlyphNode::GrayAntialiasing;
+ }
#endif
}
static bool dumped = false;
- if (!dumped && QSG_LOG_INFO().isDebugEnabled()) {
+ if (!dumped && QSG_LOG_INFO().isDebugEnabled() && !rc->rhi()) {
dumped = true;
- QSurfaceFormat format = openglRenderContext->openglContext()->format();
+ QSurfaceFormat format = rc->openglContext()->format();
QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
qCDebug(QSG_LOG_INFO, "R/G/B/A Buffers: %d %d %d %d", format.redBufferSize(),
format.greenBufferSize(), format.blueBufferSize(), format.alphaBufferSize());
@@ -160,10 +169,10 @@ void QSGDefaultContext::renderContextInitialized(QSGRenderContext *renderContext
qCDebug(QSG_LOG_INFO, "GL_RENDERER: %s",
(const char*)funcs->glGetString(GL_RENDERER));
qCDebug(QSG_LOG_INFO, "GL_VERSION: %s", (const char*)funcs->glGetString(GL_VERSION));
- QByteArrayList exts = openglRenderContext->openglContext()->extensions().toList();
+ QByteArrayList exts = rc->openglContext()->extensions().values();
std::sort(exts.begin(), exts.end());
qCDebug(QSG_LOG_INFO, "GL_EXTENSIONS: %s", exts.join(' ').constData());
- qCDebug(QSG_LOG_INFO, "Max Texture Size: %d", openglRenderContext->maxTextureSize());
+ qCDebug(QSG_LOG_INFO, "Max Texture Size: %d", rc->maxTextureSize());
qCDebug(QSG_LOG_INFO, "Debug context: %s",
format.testOption(QSurfaceFormat::DebugContext) ? "true" : "false");
}
@@ -187,11 +196,11 @@ QSGInternalRectangleNode *QSGDefaultContext::createInternalRectangleNode()
: new QSGDefaultInternalRectangleNode;
}
-QSGInternalImageNode *QSGDefaultContext::createInternalImageNode()
+QSGInternalImageNode *QSGDefaultContext::createInternalImageNode(QSGRenderContext *renderContext)
{
return m_antialiasingMethod == MsaaAntialiasing
- ? new QSGMultisampleAntialiasing::ImageNode
- : new QSGDefaultInternalImageNode;
+ ? new QSGMultisampleAntialiasing::ImageNode(static_cast<QSGDefaultRenderContext *>(renderContext))
+ : new QSGDefaultInternalImageNode(static_cast<QSGDefaultRenderContext *>(renderContext));
}
QSGPainterNode *QSGDefaultContext::createPainterNode(QQuickPaintedItem *item)
@@ -202,7 +211,7 @@ QSGPainterNode *QSGDefaultContext::createPainterNode(QQuickPaintedItem *item)
QSGGlyphNode *QSGDefaultContext::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode)
{
if (m_distanceFieldDisabled || preferNativeGlyphNode) {
- return new QSGDefaultGlyphNode;
+ return new QSGDefaultGlyphNode(rc);
} else {
QSGDistanceFieldGlyphNode *node = new QSGDistanceFieldGlyphNode(rc);
node->setPreferredAntialiasingMode(m_distanceFieldAntialiasing);
@@ -212,7 +221,11 @@ QSGGlyphNode *QSGDefaultContext::createGlyphNode(QSGRenderContext *rc, bool pref
QSGLayer *QSGDefaultContext::createLayer(QSGRenderContext *renderContext)
{
- return new QSGDefaultLayer(renderContext);
+ auto rc = static_cast<const QSGDefaultRenderContext *>(renderContext);
+ if (rc->rhi())
+ return new QSGRhiLayer(renderContext);
+ else
+ return new QSGOpenGLLayer(renderContext);
}
QSurfaceFormat QSGDefaultContext::defaultSurfaceFormat() const
@@ -275,24 +288,73 @@ QSGSpriteNode *QSGDefaultContext::createSpriteNode()
}
#endif
+QSGGuiThreadShaderEffectManager *QSGDefaultContext::createGuiThreadShaderEffectManager()
+{
+ if (QSGRhiSupport::instance()->isRhiEnabled())
+ return new QSGRhiGuiThreadShaderEffectManager;
+
+ return nullptr;
+}
+
+QSGShaderEffectNode *QSGDefaultContext::createShaderEffectNode(QSGRenderContext *renderContext,
+ QSGGuiThreadShaderEffectManager *mgr)
+{
+ if (QSGRhiSupport::instance()->isRhiEnabled()) {
+ return new QSGRhiShaderEffectNode(static_cast<QSGDefaultRenderContext *>(renderContext),
+ static_cast<QSGRhiGuiThreadShaderEffectManager *>(mgr));
+ }
+
+ return nullptr;
+}
+
QSGRendererInterface::GraphicsApi QSGDefaultContext::graphicsApi() const
{
- return OpenGL;
+ return QSGRhiSupport::instance()->graphicsApi();
+}
+
+void *QSGDefaultContext::getResource(QQuickWindow *window, Resource resource) const
+{
+ if (!window)
+ return nullptr;
+
+ // Unlike the graphicsApi and shaderType and similar queries, getting a
+ // native resource is only possible when there is an initialized
+ // rendercontext, or rather, only within rendering a frame, as per
+ // QSGRendererInterface docs. This is good since getting some things is
+ // only possible within a beginFrame - endFrame with the RHI.
+
+ const QSGDefaultRenderContext *rc = static_cast<const QSGDefaultRenderContext *>(
+ QQuickWindowPrivate::get(window)->context);
+ QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
+
+ switch (resource) {
+ case OpenGLContextResource:
+ if (rhiSupport->graphicsApi() == OpenGL)
+ return rc->openglContext();
+ else
+ return const_cast<void *>(rhiSupport->rifResource(resource, rc));
+#if QT_CONFIG(vulkan)
+ case VulkanInstanceResource:
+ return window->vulkanInstance();
+#endif
+ default:
+ return const_cast<void *>(rhiSupport->rifResource(resource, rc));
+ }
}
QSGRendererInterface::ShaderType QSGDefaultContext::shaderType() const
{
- return GLSL;
+ return QSGRhiSupport::instance()->isRhiEnabled() ? RhiShader : GLSL;
}
QSGRendererInterface::ShaderCompilationTypes QSGDefaultContext::shaderCompilationType() const
{
- return RuntimeCompilation;
+ return QSGRhiSupport::instance()->isRhiEnabled() ? OfflineCompilation : RuntimeCompilation;
}
QSGRendererInterface::ShaderSourceTypes QSGDefaultContext::shaderSourceType() const
{
- return ShaderSourceString | ShaderSourceFile;
+ return QSGRhiSupport::instance()->isRhiEnabled() ? ShaderSourceFile : (ShaderSourceString | ShaderSourceFile);
}
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultcontext_p.h b/src/quick/scenegraph/qsgdefaultcontext_p.h
index 6dfd197cf6..8fdd29caee 100644
--- a/src/quick/scenegraph/qsgdefaultcontext_p.h
+++ b/src/quick/scenegraph/qsgdefaultcontext_p.h
@@ -67,7 +67,7 @@ public:
void renderContextInvalidated(QSGRenderContext *) override;
QSGRenderContext *createRenderContext() override;
QSGInternalRectangleNode *createInternalRectangleNode() override;
- QSGInternalImageNode *createInternalImageNode() override;
+ QSGInternalImageNode *createInternalImageNode(QSGRenderContext *renderContext) override;
QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override;
QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) override;
QSGLayer *createLayer(QSGRenderContext *renderContext) override;
@@ -79,11 +79,15 @@ public:
#if QT_CONFIG(quick_sprite)
QSGSpriteNode *createSpriteNode() override;
#endif
+ QSGGuiThreadShaderEffectManager *createGuiThreadShaderEffectManager() override;
+ QSGShaderEffectNode *createShaderEffectNode(QSGRenderContext *renderContext,
+ QSGGuiThreadShaderEffectManager *mgr) override;
void setDistanceFieldEnabled(bool enabled);
bool isDistanceFieldEnabled() const;
GraphicsApi graphicsApi() const override;
+ void *getResource(QQuickWindow *window, Resource resource) const override;
ShaderType shaderType() const override;
ShaderCompilationTypes shaderCompilationType() const override;
ShaderSourceTypes shaderSourceType() const override;
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode.cpp b/src/quick/scenegraph/qsgdefaultglyphnode.cpp
index cae0eda3f4..5bd5cc4891 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode.cpp
@@ -42,8 +42,9 @@
QT_BEGIN_NAMESPACE
-QSGDefaultGlyphNode::QSGDefaultGlyphNode()
- : m_glyphNodeType(RootGlyphNode)
+QSGDefaultGlyphNode::QSGDefaultGlyphNode(QSGRenderContext *context)
+ : m_context(context)
+ , m_glyphNodeType(RootGlyphNode)
, m_dirtyGeometry(false)
{
setFlag(UsePreprocess);
@@ -75,14 +76,14 @@ void QSGDefaultGlyphNode::update()
QMargins margins(0, 0, 0, 0);
if (m_style == QQuickText::Normal) {
- m_material = new QSGTextMaskMaterial(QVector4D(m_color.redF(), m_color.greenF(), m_color.blueF(), m_color.alphaF()), font);
+ m_material = new QSGTextMaskMaterial(m_context, QVector4D(m_color.redF(), m_color.greenF(), m_color.blueF(), m_color.alphaF()), font);
} else if (m_style == QQuickText::Outline) {
- QSGOutlinedTextMaterial *material = new QSGOutlinedTextMaterial(font);
+ QSGOutlinedTextMaterial *material = new QSGOutlinedTextMaterial(m_context, font);
material->setStyleColor(m_styleColor);
m_material = material;
margins = QMargins(1, 1, 1, 1);
} else {
- QSGStyledTextMaterial *material = new QSGStyledTextMaterial(font);
+ QSGStyledTextMaterial *material = new QSGStyledTextMaterial(m_context, font);
if (m_style == QQuickText::Sunken) {
material->setStyleShift(QVector2D(0, -1));
margins.setTop(1);
@@ -158,7 +159,7 @@ void QSGDefaultGlyphNode::updateGeometry()
subNodeGlyphRun.setGlyphIndexes(glyphInfo.indexes);
subNodeGlyphRun.setPositions(glyphInfo.positions);
- QSGDefaultGlyphNode *subNode = new QSGDefaultGlyphNode();
+ QSGDefaultGlyphNode *subNode = new QSGDefaultGlyphNode(m_context);
subNode->setGlyphNodeType(SubGlyphNode);
subNode->setColor(m_color);
subNode->setStyle(m_style);
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
index b9a22dd44b..be6ef25feb 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
@@ -71,6 +71,28 @@ static inline QVector4D qsg_premultiply(const QVector4D &c, float globalOpacity)
return QVector4D(c.x() * o, c.y() * o, c.z() * o, o);
}
+static inline qreal qt_sRGB_to_linear_RGB(qreal f)
+{
+ return f > 0.04045 ? qPow((f + 0.055) / 1.055, 2.4) : f / 12.92;
+}
+
+static inline QVector4D qt_sRGB_to_linear_RGB(const QVector4D &color)
+{
+ return QVector4D(qt_sRGB_to_linear_RGB(color.x()),
+ qt_sRGB_to_linear_RGB(color.y()),
+ qt_sRGB_to_linear_RGB(color.z()),
+ color.w());
+}
+
+static inline qreal fontSmoothingGamma()
+{
+ static qreal fontSmoothingGamma = QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FontSmoothingGamma).toReal();
+ return fontSmoothingGamma;
+}
+
+
+// ***** legacy (GL) material shader implementations
+
static inline qreal qsg_device_pixel_ratio(QOpenGLContext *ctx)
{
qreal devicePixelRatio = 1;
@@ -122,12 +144,6 @@ QSGTextMaskShader::QSGTextMaskShader(QFontEngine::GlyphFormat glyphFormat)
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/textmask.frag"));
}
-static inline qreal fontSmoothingGamma()
-{
- static qreal fontSmoothingGamma = QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FontSmoothingGamma).toReal();
- return fontSmoothingGamma;
-}
-
void QSGTextMaskShader::initialize()
{
m_matrix_id = program()->uniformLocation("matrix");
@@ -149,8 +165,8 @@ void QSGTextMaskShader::updateState(const RenderState &state, QSGMaterial *newEf
if (updated
|| oldMaterial == nullptr
|| oldMaterial->texture()->textureId() != material->texture()->textureId()) {
- program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->cacheTextureWidth(),
- 1.0 / material->cacheTextureHeight()));
+ program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->openglGlyphCache()->width(),
+ 1.0 / material->openglGlyphCache()->height()));
QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
funcs->glBindTexture(GL_TEXTURE_2D, material->texture()->textureId());
@@ -263,19 +279,6 @@ void QSG24BitTextMaskShader::deactivate()
funcs->glDisable(GL_FRAMEBUFFER_SRGB);
}
-static inline qreal qt_sRGB_to_linear_RGB(qreal f)
-{
- return f > 0.04045 ? qPow((f + 0.055) / 1.055, 2.4) : f / 12.92;
-}
-
-static inline QVector4D qt_sRGB_to_linear_RGB(const QVector4D &color)
-{
- return QVector4D(qt_sRGB_to_linear_RGB(color.x()),
- qt_sRGB_to_linear_RGB(color.y()),
- qt_sRGB_to_linear_RGB(color.z()),
- color.w());
-}
-
void QSG24BitTextMaskShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
{
QSGTextMaskShader::updateState(state, newEffect, oldEffect);
@@ -371,12 +374,12 @@ void QSGStyledTextShader::updateState(const RenderState &state,
if (updated
|| oldMaterial == nullptr
|| oldMaterial->texture()->textureId() != material->texture()->textureId()) {
- program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->cacheTextureWidth(),
- 1.0 / material->cacheTextureHeight()));
+ program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->openglGlyphCache()->width(),
+ 1.0 / material->openglGlyphCache()->height()));
QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
funcs->glBindTexture(GL_TEXTURE_2D, material->texture()->textureId());
- // Set the mag/min filters to be linear. We only need to do this when the texture
+ // Set the mag/min filters to be nearest. We only need to do this when the texture
// has been recreated.
if (updated) {
funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -399,8 +402,296 @@ public:
}
};
-QSGTextMaskMaterial::QSGTextMaskMaterial(const QVector4D &color, const QRawFont &font, QFontEngine::GlyphFormat glyphFormat)
- : m_texture(nullptr)
+
+// ***** RHI shader implementations
+
+class QSGTextMaskRhiShader : public QSGMaterialRhiShader
+{
+public:
+ QSGTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat);
+
+ bool updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+ void updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+
+protected:
+ QFontEngine::GlyphFormat m_glyphFormat;
+};
+
+QSGTextMaskRhiShader::QSGTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat)
+ : m_glyphFormat(glyphFormat)
+{
+ setShaderFileName(VertexStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/textmask.vert.qsb"));
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/textmask.frag.qsb"));
+}
+
+bool QSGTextMaskRhiShader::updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
+ QSGTextMaskMaterial *mat = static_cast<QSGTextMaskMaterial *>(newMaterial);
+ QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial);
+
+ // updateUniformData() is called before updateSampledImage() by the
+ // renderer. Hence updating the glyph cache stuff here.
+ const bool updated = mat->ensureUpToDate();
+ Q_ASSERT(mat->texture());
+ Q_ASSERT(oldMat == nullptr || oldMat->texture());
+
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 92);
+
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ changed = true;
+ }
+
+ QRhiTexture *oldRtex = oldMat ? QSGTexturePrivate::get(oldMat->texture())->rhiTexture() : nullptr;
+ QRhiTexture *newRtex = QSGTexturePrivate::get(mat->texture())->rhiTexture();
+ if (updated || !oldMat || oldRtex != newRtex) {
+ const QVector2D textureScale = QVector2D(1.0f / mat->rhiGlyphCache()->width(),
+ 1.0f / mat->rhiGlyphCache()->height());
+ memcpy(buf->data() + 64 + 16, &textureScale, 8);
+ changed = true;
+ }
+
+ if (!oldMat) {
+ float dpr = state.devicePixelRatio();
+ memcpy(buf->data() + 64 + 16 + 8, &dpr, 4);
+ }
+
+ // move texture uploads/copies onto the renderer's soon-to-be-committed list
+ mat->rhiGlyphCache()->commitResourceUpdates(state.resourceUpdateBatch());
+
+ return changed;
+}
+
+void QSGTextMaskRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *)
+{
+ Q_UNUSED(state);
+ if (binding != 1)
+ return;
+
+ QSGTextMaskMaterial *mat = static_cast<QSGTextMaskMaterial *>(newMaterial);
+ QSGTexture *t = mat->texture();
+ t->setFiltering(QSGTexture::Nearest);
+ *texture = t;
+}
+
+class QSG8BitTextMaskRhiShader : public QSGTextMaskRhiShader
+{
+public:
+ QSG8BitTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat, bool alphaTexture)
+ : QSGTextMaskRhiShader(glyphFormat)
+ {
+ if (alphaTexture)
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/8bittextmask_a.frag.qsb"));
+ else
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/8bittextmask.frag.qsb"));
+ }
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+bool QSG8BitTextMaskRhiShader::updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = QSGTextMaskRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+
+ QSGTextMaskMaterial *mat = static_cast<QSGTextMaskMaterial *>(newMaterial);
+ QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial);
+
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 80);
+
+ if (oldMat == nullptr || mat->color() != oldMat->color() || state.isOpacityDirty()) {
+ const QVector4D color = qsg_premultiply(mat->color(), state.opacity());
+ memcpy(buf->data() + 64, &color, 16);
+ changed = true;
+ }
+
+ return changed;
+}
+
+class QSG24BitTextMaskRhiShader : public QSGTextMaskRhiShader
+{
+public:
+ QSG24BitTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat)
+ : QSGTextMaskRhiShader(glyphFormat)
+ {
+ setFlag(UpdatesGraphicsPipelineState, true);
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/24bittextmask.frag.qsb"));
+ }
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+ bool updateGraphicsPipelineState(RenderState &state, GraphicsPipelineState *ps,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+// ### gamma correction (sRGB) Unsurprisingly, the GL approach is not portable
+// to anything else - it just does not work that way, there is no opt-in/out
+// switch and magic winsys-provided maybe-sRGB buffers. When requesting an sRGB
+// QRhiSwapChain (which we do not do), it is full sRGB, with the sRGB
+// framebuffer update and blending always on... Could we do gamma correction in
+// the shader for text? (but that's bad for blending?)
+
+bool QSG24BitTextMaskRhiShader::updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = QSGTextMaskRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+
+ QSGTextMaskMaterial *mat = static_cast<QSGTextMaskMaterial *>(newMaterial);
+ QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial);
+
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 92);
+
+ if (oldMat == nullptr || mat->color() != oldMat->color() || state.isOpacityDirty()) {
+ // shader takes vec4 but uses alpha only; coloring happens via the blend constant
+ const QVector4D color = qsg_premultiply(mat->color(), state.opacity());
+ memcpy(buf->data() + 64, &color, 16);
+ changed = true;
+ }
+
+ return changed;
+}
+
+bool QSG24BitTextMaskRhiShader::updateGraphicsPipelineState(RenderState &state, GraphicsPipelineState *ps,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(state);
+ Q_UNUSED(oldMaterial);
+ QSGTextMaskMaterial *mat = static_cast<QSGTextMaskMaterial *>(newMaterial);
+
+ ps->blendEnable = true;
+ ps->srcColor = GraphicsPipelineState::ConstantColor;
+ ps->dstColor = GraphicsPipelineState::OneMinusSrcColor;
+
+ QVector4D color = qsg_premultiply(mat->color(), state.opacity());
+ // if (useSRGB())
+ // color = qt_sRGB_to_linear_RGB(color);
+
+ // this is dynamic state but it's - magic! - taken care of by the renderer
+ ps->blendConstant = QColor::fromRgbF(color.x(), color.y(), color.z(), color.w());
+
+ return true;
+}
+
+class QSG32BitColorTextRhiShader : public QSGTextMaskRhiShader
+{
+public:
+ QSG32BitColorTextRhiShader(QFontEngine::GlyphFormat glyphFormat)
+ : QSGTextMaskRhiShader(glyphFormat)
+ {
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/32bitcolortext.frag.qsb"));
+ }
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+bool QSG32BitColorTextRhiShader::updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = QSGTextMaskRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+
+ QSGTextMaskMaterial *mat = static_cast<QSGTextMaskMaterial *>(newMaterial);
+ QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial);
+
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 92);
+
+ if (oldMat == nullptr || mat->color() != oldMat->color() || state.isOpacityDirty()) {
+ // shader takes vec4 but uses alpha only
+ const QVector4D color(0, 0, 0, mat->color().w() * state.opacity());
+ memcpy(buf->data() + 64, &color, 16);
+ changed = true;
+ }
+
+ return changed;
+}
+
+class QSGStyledTextRhiShader : public QSG8BitTextMaskRhiShader
+{
+public:
+ QSGStyledTextRhiShader(QFontEngine::GlyphFormat glyphFormat, bool alphaTexture)
+ : QSG8BitTextMaskRhiShader(glyphFormat, alphaTexture)
+ {
+ setShaderFileName(VertexStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/styledtext.vert.qsb"));
+ if (alphaTexture)
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/styledtext_a.frag.qsb"));
+ else
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/styledtext.frag.qsb"));
+ }
+
+ bool updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+bool QSGStyledTextRhiShader::updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = QSG8BitTextMaskRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+
+ QSGStyledTextMaterial *mat = static_cast<QSGStyledTextMaterial *>(newMaterial);
+ QSGStyledTextMaterial *oldMat = static_cast<QSGStyledTextMaterial *>(oldMaterial);
+
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 120);
+
+ // matrix..dpr + 1 float padding (vec4 must be aligned to 16)
+ const int startOffset = 64 + 16 + 8 + 4 + 4;
+
+ if (oldMat == nullptr || mat->styleColor() != oldMat->styleColor() || state.isOpacityDirty()) {
+ const QVector4D styleColor = qsg_premultiply(mat->styleColor(), state.opacity());
+ memcpy(buf->data() + startOffset, &styleColor, 16);
+ changed = true;
+ }
+
+ if (oldMat == nullptr || oldMat->styleShift() != mat->styleShift()) {
+ const QVector2D v = mat->styleShift();
+ memcpy(buf->data() + startOffset + 16, &v, 8);
+ changed = true;
+ }
+
+ return changed;
+}
+
+class QSGOutlinedTextRhiShader : public QSGStyledTextRhiShader
+{
+public:
+ QSGOutlinedTextRhiShader(QFontEngine::GlyphFormat glyphFormat, bool alphaTexture)
+ : QSGStyledTextRhiShader(glyphFormat, alphaTexture)
+ {
+ setShaderFileName(VertexStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/outlinedtext.vert.qsb"));
+ if (alphaTexture)
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/outlinedtext_a.frag.qsb"));
+ else
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/outlinedtext.frag.qsb"));
+ }
+};
+
+
+// ***** common material stuff
+
+QSGTextMaskMaterial::QSGTextMaskMaterial(QSGRenderContext *rc, const QVector4D &color, const QRawFont &font, QFontEngine::GlyphFormat glyphFormat)
+ : m_rc(qobject_cast<QSGDefaultRenderContext *>(rc))
+ , m_texture(nullptr)
, m_glyphCache(nullptr)
, m_font(font)
, m_color(color)
@@ -430,16 +721,17 @@ void QSGTextMaskMaterial::init(QFontEngine::GlyphFormat glyphFormat)
{
Q_ASSERT(m_font.isValid());
+ setFlag(SupportsRhiShader, true);
setFlag(Blending, true);
+ Q_ASSERT(m_rc);
+ m_rhi = m_rc->rhi();
+
updateCache(glyphFormat);
}
void QSGTextMaskMaterial::updateCache(QFontEngine::GlyphFormat glyphFormat)
{
- QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
- Q_ASSERT(ctx != nullptr);
-
// The following piece of code will read/write to the font engine's caches,
// potentially from different threads. However, this is safe because this
// code is only called from QQuickItem::updatePaintNode() which is called
@@ -454,23 +746,38 @@ void QSGTextMaskMaterial::updateCache(QFontEngine::GlyphFormat glyphFormat)
: QFontEngine::Format_A32;
}
- qreal devicePixelRatio = qsg_device_pixel_ratio(ctx);
+ QOpenGLContext *ctx = nullptr;
+ qreal devicePixelRatio;
+ void *cacheKey;
+ if (m_rhi) {
+ cacheKey = m_rhi;
+ // Get the dpr the modern way. This value retrieved via the
+ // rendercontext matches what RenderState::devicePixelRatio()
+ // exposes to the material shaders later on.
+ devicePixelRatio = m_rc->currentDevicePixelRatio();
+ } else {
+ ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
+ Q_ASSERT(ctx != nullptr);
+ cacheKey = ctx;
+ devicePixelRatio = qsg_device_pixel_ratio(ctx); // this is technically incorrect, see other branch above
+ }
QTransform glyphCacheTransform = QTransform::fromScale(devicePixelRatio, devicePixelRatio);
if (!fontEngine->supportsTransformation(glyphCacheTransform))
glyphCacheTransform = QTransform();
QColor color = glyphFormat == QFontEngine::Format_ARGB ? QColor::fromRgbF(m_color.x(), m_color.y(), m_color.z(), m_color.w()) : QColor();
- m_glyphCache = fontEngine->glyphCache(ctx, glyphFormat, glyphCacheTransform, color);
+ m_glyphCache = fontEngine->glyphCache(cacheKey, glyphFormat, glyphCacheTransform, color);
if (!m_glyphCache || int(m_glyphCache->glyphFormat()) != glyphFormat) {
- m_glyphCache = new QOpenGLTextureGlyphCache(glyphFormat, glyphCacheTransform, color);
- fontEngine->setGlyphCache(ctx, m_glyphCache.data());
- auto sg = QSGDefaultRenderContext::from(ctx);
- Q_ASSERT(sg);
- sg->registerFontengineForCleanup(fontEngine);
+ if (m_rhi)
+ m_glyphCache = new QSGRhiTextureGlyphCache(m_rhi, glyphFormat, glyphCacheTransform, color);
+ else
+ m_glyphCache = new QOpenGLTextureGlyphCache(glyphFormat, glyphCacheTransform, color);
+
+ fontEngine->setGlyphCache(cacheKey, m_glyphCache.data());
+ m_rc->registerFontengineForCleanup(fontEngine);
}
}
-
}
void QSGTextMaskMaterial::populate(const QPointF &p,
@@ -578,21 +885,45 @@ QSGMaterialType *QSGTextMaskMaterial::type() const
}
}
-QOpenGLTextureGlyphCache *QSGTextMaskMaterial::glyphCache() const
+QTextureGlyphCache *QSGTextMaskMaterial::glyphCache() const
{
- return static_cast<QOpenGLTextureGlyphCache*>(m_glyphCache.data());
+ return static_cast<QTextureGlyphCache *>(m_glyphCache.data());
+}
+
+QOpenGLTextureGlyphCache *QSGTextMaskMaterial::openglGlyphCache() const
+{
+ return static_cast<QOpenGLTextureGlyphCache *>(glyphCache());
+}
+
+QSGRhiTextureGlyphCache *QSGTextMaskMaterial::rhiGlyphCache() const
+{
+ return static_cast<QSGRhiTextureGlyphCache *>(glyphCache());
}
QSGMaterialShader *QSGTextMaskMaterial::createShader() const
{
- switch (QFontEngine::GlyphFormat glyphFormat = glyphCache()->glyphFormat()) {
- case QFontEngine::Format_ARGB:
- return new QSG32BitColorTextShader(glyphFormat);
- case QFontEngine::Format_A32:
- return new QSG24BitTextMaskShader(glyphFormat);
- case QFontEngine::Format_A8:
- default:
- return new QSG8BitTextMaskShader(glyphFormat);
+ if (flags().testFlag(RhiShaderWanted)) {
+ QSGRhiTextureGlyphCache *gc = rhiGlyphCache();
+ const QFontEngine::GlyphFormat glyphFormat = gc->glyphFormat();
+ switch (glyphFormat) {
+ case QFontEngine::Format_ARGB:
+ return new QSG32BitColorTextRhiShader(glyphFormat);
+ case QFontEngine::Format_A32:
+ return new QSG24BitTextMaskRhiShader(glyphFormat);
+ case QFontEngine::Format_A8:
+ default:
+ return new QSG8BitTextMaskRhiShader(glyphFormat, gc->eightBitFormatIsAlphaSwizzled());
+ }
+ } else {
+ switch (QFontEngine::GlyphFormat glyphFormat = glyphCache()->glyphFormat()) {
+ case QFontEngine::Format_ARGB:
+ return new QSG32BitColorTextShader(glyphFormat);
+ case QFontEngine::Format_A32:
+ return new QSG24BitTextMaskShader(glyphFormat);
+ case QFontEngine::Format_A8:
+ default:
+ return new QSG8BitTextMaskShader(glyphFormat);
+ }
}
}
@@ -620,36 +951,40 @@ int QSGTextMaskMaterial::compare(const QSGMaterial *o) const
bool QSGTextMaskMaterial::ensureUpToDate()
{
- QSize glyphCacheSize(glyphCache()->width(), glyphCache()->height());
- if (glyphCacheSize != m_size) {
- if (m_texture)
- delete m_texture;
- m_texture = new QSGPlainTexture();
- m_texture->setTextureId(glyphCache()->texture());
- m_texture->setTextureSize(QSize(glyphCache()->width(), glyphCache()->height()));
- m_texture->setOwnsTexture(false);
-
- m_size = glyphCacheSize;
+ if (m_rhi) {
+ QSGRhiTextureGlyphCache *gc = rhiGlyphCache();
+ QSize glyphCacheSize(gc->width(), gc->height());
+ if (glyphCacheSize != m_size) {
+ if (m_texture)
+ delete m_texture;
+ m_texture = new QSGPlainTexture;
+ m_texture->setTexture(gc->texture());
+ m_texture->setTextureSize(QSize(gc->width(), gc->height()));
+ m_texture->setOwnsTexture(false);
+ m_size = glyphCacheSize;
+ return true;
+ }
+ return false;
- return true;
} else {
+ QSize glyphCacheSize(openglGlyphCache()->width(), openglGlyphCache()->height());
+ if (glyphCacheSize != m_size) {
+ if (m_texture)
+ delete m_texture;
+ m_texture = new QSGPlainTexture();
+ m_texture->setTextureId(openglGlyphCache()->texture());
+ m_texture->setTextureSize(QSize(openglGlyphCache()->width(), openglGlyphCache()->height()));
+ m_texture->setOwnsTexture(false);
+ m_size = glyphCacheSize;
+ return true;
+ }
return false;
}
}
-int QSGTextMaskMaterial::cacheTextureWidth() const
-{
- return glyphCache()->width();
-}
-int QSGTextMaskMaterial::cacheTextureHeight() const
-{
- return glyphCache()->height();
-}
-
-
-QSGStyledTextMaterial::QSGStyledTextMaterial(const QRawFont &font)
- : QSGTextMaskMaterial(QVector4D(), font, QFontEngine::Format_A8)
+QSGStyledTextMaterial::QSGStyledTextMaterial(QSGRenderContext *rc, const QRawFont &font)
+ : QSGTextMaskMaterial(rc, QVector4D(), font, QFontEngine::Format_A8)
{
}
@@ -661,7 +996,12 @@ QSGMaterialType *QSGStyledTextMaterial::type() const
QSGMaterialShader *QSGStyledTextMaterial::createShader() const
{
- return new QSGStyledTextShader(glyphCache()->glyphFormat());
+ if (flags().testFlag(RhiShaderWanted)) {
+ QSGRhiTextureGlyphCache *gc = rhiGlyphCache();
+ return new QSGStyledTextRhiShader(gc->glyphFormat(), gc->eightBitFormatIsAlphaSwizzled());
+ } else {
+ return new QSGStyledTextShader(glyphCache()->glyphFormat());
+ }
}
int QSGStyledTextMaterial::compare(const QSGMaterial *o) const
@@ -678,8 +1018,8 @@ int QSGStyledTextMaterial::compare(const QSGMaterial *o) const
}
-QSGOutlinedTextMaterial::QSGOutlinedTextMaterial(const QRawFont &font)
- : QSGStyledTextMaterial(font)
+QSGOutlinedTextMaterial::QSGOutlinedTextMaterial(QSGRenderContext *rc, const QRawFont &font)
+ : QSGStyledTextMaterial(rc, font)
{
}
@@ -691,7 +1031,12 @@ QSGMaterialType *QSGOutlinedTextMaterial::type() const
QSGMaterialShader *QSGOutlinedTextMaterial::createShader() const
{
- return new QSGOutlinedTextShader(glyphCache()->glyphFormat());
+ if (flags().testFlag(RhiShaderWanted)) {
+ QSGRhiTextureGlyphCache *gc = rhiGlyphCache();
+ return new QSGOutlinedTextRhiShader(gc->glyphFormat(), gc->eightBitFormatIsAlphaSwizzled());
+ } else {
+ return new QSGOutlinedTextShader(glyphCache()->glyphFormat());
+ }
}
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p.h
index 37a89c70b9..4cff2d3d24 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.h
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.h
@@ -53,13 +53,14 @@
#include <private/qsgadaptationlayer_p.h>
#include <private/qsgbasicglyphnode_p.h>
+#include <qlinkedlist.h>
QT_BEGIN_NAMESPACE
class QSGDefaultGlyphNode : public QSGBasicGlyphNode
{
public:
- QSGDefaultGlyphNode();
+ QSGDefaultGlyphNode(QSGRenderContext *context);
~QSGDefaultGlyphNode();
void setMaterialColor(const QColor &color) override;
void setGlyphs(const QPointF &position, const QGlyphRun &glyphs) override;
@@ -75,8 +76,9 @@ private:
void setGlyphNodeType(DefaultGlyphNodeType type) { m_glyphNodeType = type; }
+ QSGRenderContext *m_context;
DefaultGlyphNodeType m_glyphNodeType;
- QLinkedList<QSGNode *> m_nodesToDelete;
+ QVector<QSGNode *> m_nodesToDelete;
struct GlyphInfo {
QVector<quint32> indexes;
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h
index 56084dea96..7d2635794d 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h
@@ -57,7 +57,8 @@
#include <QtQuick/qsgtexture.h>
#include <QtQuick/qsggeometry.h>
#include <qshareddata.h>
-#include <QtQuick/private/qsgtexture_p.h>
+#include <QtQuick/private/qsgplaintexture_p.h>
+#include <QtQuick/private/qsgrhitextureglyphcache_p.h>
#include <qrawfont.h>
#include <qmargins.h>
@@ -65,10 +66,13 @@ QT_BEGIN_NAMESPACE
class QFontEngine;
class Geometry;
+class QSGRenderContext;
+class QSGDefaultRenderContext;
+
class QSGTextMaskMaterial: public QSGMaterial
{
public:
- QSGTextMaskMaterial(const QVector4D &color, const QRawFont &font, QFontEngine::GlyphFormat glyphFormat = QFontEngine::Format_None);
+ QSGTextMaskMaterial(QSGRenderContext *rc, const QVector4D &color, const QRawFont &font, QFontEngine::GlyphFormat glyphFormat = QFontEngine::Format_None);
virtual ~QSGTextMaskMaterial();
QSGMaterialType *type() const override;
@@ -81,12 +85,12 @@ public:
QSGTexture *texture() const { return m_texture; }
- int cacheTextureWidth() const;
- int cacheTextureHeight() const;
-
bool ensureUpToDate();
- QOpenGLTextureGlyphCache *glyphCache() const;
+ QTextureGlyphCache *glyphCache() const;
+ QOpenGLTextureGlyphCache *openglGlyphCache() const;
+ QSGRhiTextureGlyphCache *rhiGlyphCache() const;
+
void populate(const QPointF &position,
const QVector<quint32> &glyphIndexes, const QVector<QPointF> &glyphPositions,
QSGGeometry *geometry, QRectF *boundingRect, QPointF *baseLine,
@@ -96,9 +100,11 @@ private:
void init(QFontEngine::GlyphFormat glyphFormat);
void updateCache(QFontEngine::GlyphFormat glyphFormat);
+ QSGDefaultRenderContext *m_rc;
QSGPlainTexture *m_texture;
QExplicitlySharedDataPointer<QFontEngineGlyphCache> m_glyphCache;
QRawFont m_font;
+ QRhi *m_rhi;
QVector4D m_color;
QSize m_size;
};
@@ -106,7 +112,7 @@ private:
class QSGStyledTextMaterial : public QSGTextMaskMaterial
{
public:
- QSGStyledTextMaterial(const QRawFont &font);
+ QSGStyledTextMaterial(QSGRenderContext *rc, const QRawFont &font);
virtual ~QSGStyledTextMaterial() { }
void setStyleShift(const QVector2D &shift) { m_styleShift = shift; }
@@ -118,7 +124,6 @@ public:
QSGMaterialType *type() const override;
QSGMaterialShader *createShader() const override;
-
int compare(const QSGMaterial *other) const override;
private:
@@ -129,7 +134,7 @@ private:
class QSGOutlinedTextMaterial : public QSGStyledTextMaterial
{
public:
- QSGOutlinedTextMaterial(const QRawFont &font);
+ QSGOutlinedTextMaterial(QSGRenderContext *rc, const QRawFont &font);
~QSGOutlinedTextMaterial() { }
QSGMaterialType *type() const override;
diff --git a/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp b/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp
index 5dd6eaa4ca..500d4e6e95 100644
--- a/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp
+++ b/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp
@@ -38,10 +38,12 @@
****************************************************************************/
#include "qsgdefaultinternalimagenode_p.h"
+#include <private/qsgdefaultrendercontext_p.h>
#include <private/qsgmaterialshader_p.h>
#include <private/qsgtexturematerial_p.h>
#include <QtGui/qopenglfunctions.h>
#include <QtCore/qmath.h>
+#include <QtGui/private/qrhi_p.h>
QT_BEGIN_NAMESPACE
@@ -59,9 +61,18 @@ protected:
int m_pixelSizeLoc;
};
+class SmoothTextureMaterialRhiShader : public QSGTextureMaterialRhiShader
+{
+public:
+ SmoothTextureMaterialRhiShader();
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
QSGSmoothTextureMaterial::QSGSmoothTextureMaterial()
{
+ setFlag(SupportsRhiShader, true);
setFlag(RequiresFullMatrixExceptTranslate, true);
setFlag(Blending, true);
}
@@ -79,7 +90,10 @@ QSGMaterialType *QSGSmoothTextureMaterial::type() const
QSGMaterialShader *QSGSmoothTextureMaterial::createShader() const
{
- return new SmoothTextureMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new SmoothTextureMaterialRhiShader;
+ else
+ return new SmoothTextureMaterialShader;
}
SmoothTextureMaterialShader::SmoothTextureMaterialShader()
@@ -116,7 +130,33 @@ void SmoothTextureMaterialShader::initialize()
QSGTextureMaterialShader::initialize();
}
-QSGDefaultInternalImageNode::QSGDefaultInternalImageNode()
+SmoothTextureMaterialRhiShader::SmoothTextureMaterialRhiShader()
+{
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/smoothtexture.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/smoothtexture.frag.qsb"));
+}
+
+bool SmoothTextureMaterialRhiShader::updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+
+ if (!oldMaterial) {
+ // The viewport is constant, so set the pixel size uniform only once (per batches with the same material).
+ const QRect r = state.viewportRect();
+ const QVector2D v(2.0f / r.width(), 2.0f / r.height());
+ memcpy(buf->data() + 64 + 8, &v, 8);
+ changed = true;
+ }
+
+ changed |= QSGTextureMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+
+ return changed;
+}
+
+
+QSGDefaultInternalImageNode::QSGDefaultInternalImageNode(QSGDefaultRenderContext *rc)
+ : m_rc(rc)
{
setMaterial(&m_materialO);
setOpaqueMaterial(&m_material);
@@ -209,14 +249,19 @@ bool QSGDefaultInternalImageNode::supportsWrap(const QSize &size) const
{
bool wrapSupported = true;
- QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ if (m_rc->rhi()) {
+ wrapSupported = m_rc->rhi()->isFeatureSupported(QRhi::NPOTTextureRepeat)
+ || (isPowerOfTwo(size.width()) && isPowerOfTwo(size.height()));
+ } else {
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
#ifndef QT_OPENGL_ES_2
- if (ctx->isOpenGLES())
+ if (ctx->isOpenGLES())
#endif
- {
- bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
- const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
- wrapSupported = npotSupported || !isNpot;
+ {
+ bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
+ const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
+ wrapSupported = npotSupported || !isNpot;
+ }
}
return wrapSupported;
diff --git a/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h b/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h
index 1fc7834bd1..b4cfbd9ece 100644
--- a/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h
+++ b/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h
@@ -58,6 +58,8 @@
QT_BEGIN_NAMESPACE
+class QSGDefaultRenderContext;
+
class Q_QUICK_PRIVATE_EXPORT QSGSmoothTextureMaterial : public QSGTextureMaterial
{
public:
@@ -73,7 +75,7 @@ protected:
class Q_QUICK_PRIVATE_EXPORT QSGDefaultInternalImageNode : public QSGBasicInternalImageNode
{
public:
- QSGDefaultInternalImageNode();
+ QSGDefaultInternalImageNode(QSGDefaultRenderContext *rc);
void setMipmapFiltering(QSGTexture::Filtering filtering) override;
void setFiltering(QSGTexture::Filtering filtering) override;
@@ -87,6 +89,7 @@ public:
bool supportsWrap(const QSize &size) const override;
private:
+ QSGDefaultRenderContext *m_rc;
QSGOpaqueTextureMaterial m_material;
QSGTextureMaterial m_materialO;
QSGSmoothTextureMaterial m_smoothMaterial;
diff --git a/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp
index fd0dcebd57..5e4affbf90 100644
--- a/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp
+++ b/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp
@@ -105,14 +105,61 @@ void SmoothColorMaterialShader::initialize()
m_pixelSizeLoc = program()->uniformLocation("pixelSize");
}
+
+class SmoothColorMaterialRhiShader : public QSGMaterialRhiShader
+{
+public:
+ SmoothColorMaterialRhiShader();
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+SmoothColorMaterialRhiShader::SmoothColorMaterialRhiShader()
+{
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/smoothcolor.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/smoothcolor.frag.qsb"));
+}
+
+bool SmoothColorMaterialRhiShader::updateUniformData(RenderState &state, QSGMaterial *, QSGMaterial *oldMaterial)
+{
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ changed = true;
+ }
+
+ if (oldMaterial == nullptr) {
+ // The viewport is constant, so set the pixel size uniform only once.
+ const QRect r = state.viewportRect();
+ const QVector2D v(2.0f / r.width(), 2.0f / r.height());
+ Q_ASSERT(sizeof(v) == 8);
+ memcpy(buf->data() + 64, &v, 8);
+ changed = true;
+ }
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(buf->data() + 72, &opacity, 4);
+ changed = true;
+ }
+
+ return changed;
+}
+
+
QSGSmoothColorMaterial::QSGSmoothColorMaterial()
{
setFlag(RequiresFullMatrixExceptTranslate, true);
setFlag(Blending, true);
+ setFlag(SupportsRhiShader, true);
}
int QSGSmoothColorMaterial::compare(const QSGMaterial *) const
{
+ // all state in vertex attributes -> all smoothcolor materials are equal
return 0;
}
@@ -124,7 +171,10 @@ QSGMaterialType *QSGSmoothColorMaterial::type() const
QSGMaterialShader *QSGSmoothColorMaterial::createShader() const
{
- return new SmoothColorMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new SmoothColorMaterialRhiShader;
+ else
+ return new SmoothColorMaterialShader;
}
QSGDefaultInternalRectangleNode::QSGDefaultInternalRectangleNode()
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
index 73b79c6300..2bf4a83a8e 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp
+++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
@@ -44,9 +44,13 @@
#include <QtQuick/private/qsgbatchrenderer_p.h>
#include <QtQuick/private/qsgrenderer_p.h>
-#include <QtQuick/private/qsgatlastexture_p.h>
+#include <QtQuick/private/qsgrhiatlastexture_p.h>
+#include <QtQuick/private/qsgrhidistancefieldglyphcache_p.h>
+#include <QtQuick/private/qsgmaterialrhishader_p.h>
+
+#include <QtQuick/private/qsgopenglatlastexture_p.h>
#include <QtQuick/private/qsgcompressedtexture_p.h>
-#include <QtQuick/private/qsgdefaultdistancefieldglyphcache_p.h>
+#include <QtQuick/private/qsgopengldistancefieldglyphcache_p.h>
QT_BEGIN_NAMESPACE
@@ -54,68 +58,82 @@ QT_BEGIN_NAMESPACE
QSGDefaultRenderContext::QSGDefaultRenderContext(QSGContext *context)
: QSGRenderContext(context)
+ , m_rhi(nullptr)
, m_gl(nullptr)
, m_depthStencilManager(nullptr)
, m_maxTextureSize(0)
, m_brokenIBOs(false)
, m_serializedRender(false)
, m_attachToGLContext(true)
- , m_atlasManager(nullptr)
+ , m_glAtlasManager(nullptr)
+ , m_rhiAtlasManager(nullptr)
+ , m_currentFrameCommandBuffer(nullptr)
+ , m_currentFrameRenderPass(nullptr)
{
-
}
/*!
Initializes the scene graph render context with the GL context \a context. This also
emits the ready() signal so that the QML graph can start building scene graph nodes.
*/
-void QSGDefaultRenderContext::initialize(void *context)
+void QSGDefaultRenderContext::initialize(const QSGRenderContext::InitParams *params)
{
if (!m_sg)
return;
- QOpenGLContext *openglContext = static_cast<QOpenGLContext *>(context);
-
- QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
- funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
+ const InitParams *initParams = static_cast<const InitParams *>(params);
+ if (initParams->sType != INIT_PARAMS_MAGIC)
+ qFatal("QSGDefaultRenderContext: Invalid parameters passed to initialize()");
- // Sanity check the surface format, in case it was overridden by the application
- QSurfaceFormat requested = m_sg->defaultSurfaceFormat();
- QSurfaceFormat actual = openglContext->format();
- if (requested.depthBufferSize() > 0 && actual.depthBufferSize() <= 0)
- qWarning("QSGContext::initialize: depth buffer support missing, expect rendering errors");
- if (requested.stencilBufferSize() > 0 && actual.stencilBufferSize() <= 0)
- qWarning("QSGContext::initialize: stencil buffer support missing, expect rendering errors");
+ m_initParams = *initParams;
- if (!m_atlasManager)
- m_atlasManager = new QSGAtlasTexture::Manager();
+ m_rhi = m_initParams.rhi;
+ if (m_rhi) {
+ m_maxTextureSize = m_rhi->resourceLimit(QRhi::TextureSizeMax);
+ if (!m_rhiAtlasManager)
+ m_rhiAtlasManager = new QSGRhiAtlasTexture::Manager(this, m_initParams.initialSurfacePixelSize, m_initParams.maybeSurface);
+ } else {
+ QOpenGLFunctions *funcs = m_rhi ? nullptr : QOpenGLContext::currentContext()->functions();
+ funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
- Q_ASSERT_X(!m_gl, "QSGRenderContext::initialize", "already initialized!");
- m_gl = openglContext;
- if (m_attachToGLContext) {
- Q_ASSERT(!openglContext->property(QSG_RENDERCONTEXT_PROPERTY).isValid());
- openglContext->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant::fromValue(this));
- }
- m_sg->renderContextInitialized(this);
+ // Sanity check the surface format, in case it was overridden by the application
+ QSurfaceFormat requested = m_sg->defaultSurfaceFormat();
+ QSurfaceFormat actual = m_initParams.openGLContext->format();
+ if (requested.depthBufferSize() > 0 && actual.depthBufferSize() <= 0)
+ qWarning("QSGContext::initialize: depth buffer support missing, expect rendering errors");
+ if (requested.stencilBufferSize() > 0 && actual.stencilBufferSize() <= 0)
+ qWarning("QSGContext::initialize: stencil buffer support missing, expect rendering errors");
#ifdef Q_OS_LINUX
- const char *vendor = (const char *) funcs->glGetString(GL_VENDOR);
- if (vendor && strstr(vendor, "nouveau"))
- m_brokenIBOs = true;
- const char *renderer = (const char *) funcs->glGetString(GL_RENDERER);
- if (renderer && strstr(renderer, "llvmpipe"))
- m_serializedRender = true;
- if (vendor && renderer && strstr(vendor, "Hisilicon Technologies") && strstr(renderer, "Immersion.16"))
- m_brokenIBOs = true;
+ const char *vendor = (const char *) funcs->glGetString(GL_VENDOR);
+ if (vendor && strstr(vendor, "nouveau"))
+ m_brokenIBOs = true;
+ const char *renderer = (const char *) funcs->glGetString(GL_RENDERER);
+ if (renderer && strstr(renderer, "llvmpipe"))
+ m_serializedRender = true;
+ if (vendor && renderer && strstr(vendor, "Hisilicon Technologies") && strstr(renderer, "Immersion.16"))
+ m_brokenIBOs = true;
#endif
+ Q_ASSERT_X(!m_gl, "QSGRenderContext::initialize", "already initialized!");
+ m_gl = m_initParams.openGLContext;
+ if (m_attachToGLContext) {
+ Q_ASSERT(!m_gl->property(QSG_RENDERCONTEXT_PROPERTY).isValid());
+ m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant::fromValue(this));
+ }
+
+ if (!m_glAtlasManager)
+ m_glAtlasManager = new QSGOpenGLAtlasTexture::Manager(m_initParams.initialSurfacePixelSize);
+ }
+
+ m_sg->renderContextInitialized(this);
+
emit initialized();
}
-
void QSGDefaultRenderContext::invalidate()
{
- if (!m_gl)
+ if (!m_gl && !m_rhi)
return;
qDeleteAll(m_texturesToDelete);
@@ -138,11 +156,18 @@ void QSGDefaultRenderContext::invalidate()
deferred deleted last.
Another alternative would be to use a QPointer in
- QSGAtlasTexture::Texture, but this seemed simpler.
+ QSGOpenGLAtlasTexture::Texture, but this seemed simpler.
*/
- m_atlasManager->invalidate();
- m_atlasManager->deleteLater();
- m_atlasManager = nullptr;
+ if (m_glAtlasManager) {
+ m_glAtlasManager->invalidate();
+ m_glAtlasManager->deleteLater();
+ m_glAtlasManager = nullptr;
+ }
+ if (m_rhiAtlasManager) {
+ m_rhiAtlasManager->invalidate();
+ m_rhiAtlasManager->deleteLater();
+ m_rhiAtlasManager = nullptr;
+ }
// The following piece of code will read/write to the font engine's caches,
// potentially from different threads. However, this is safe because this
@@ -151,7 +176,7 @@ void QSGDefaultRenderContext::invalidate()
// sequence. (see qsgdefaultglyphnode_p.cpp's init())
for (QSet<QFontEngine *>::const_iterator it = m_fontEnginesToClean.constBegin(),
end = m_fontEnginesToClean.constEnd(); it != end; ++it) {
- (*it)->clearGlyphCache(m_gl);
+ (*it)->clearGlyphCache(m_gl ? (void *) m_gl : (void *) m_rhi);
if (!(*it)->ref.deref())
delete *it;
}
@@ -163,17 +188,33 @@ void QSGDefaultRenderContext::invalidate()
qDeleteAll(m_glyphCaches);
m_glyphCaches.clear();
- if (m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(this))
+ if (m_gl && m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(this))
m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant());
+
m_gl = nullptr;
+ m_rhi = nullptr;
if (m_sg)
m_sg->renderContextInvalidated(this);
+
emit invalidated();
}
+void QSGDefaultRenderContext::prepareSync(qreal devicePixelRatio)
+{
+ m_currentDevicePixelRatio = devicePixelRatio;
+}
+
static QBasicMutex qsg_framerender_mutex;
+void QSGDefaultRenderContext::beginNextFrame(QSGRenderer *renderer,
+ RenderPassCallback mainPassRecordingStart,
+ RenderPassCallback mainPassRecordingEnd,
+ void *callbackUserData)
+{
+ renderer->setRenderPassRecordingCallbacks(mainPassRecordingStart, mainPassRecordingEnd, callbackUserData);
+}
+
void QSGDefaultRenderContext::renderNextFrame(QSGRenderer *renderer, uint fboId)
{
if (m_serializedRender)
@@ -185,6 +226,40 @@ void QSGDefaultRenderContext::renderNextFrame(QSGRenderer *renderer, uint fboId)
qsg_framerender_mutex.unlock();
}
+void QSGDefaultRenderContext::endNextFrame(QSGRenderer *renderer)
+{
+ Q_UNUSED(renderer);
+}
+
+void QSGDefaultRenderContext::beginNextRhiFrame(QSGRenderer *renderer, QRhiRenderTarget *rt, QRhiRenderPassDescriptor *rp,
+ QRhiCommandBuffer *cb,
+ RenderPassCallback mainPassRecordingStart,
+ RenderPassCallback mainPassRecordingEnd,
+ void *callbackUserData)
+{
+ Q_ASSERT(!m_currentFrameCommandBuffer);
+
+ renderer->setRenderTarget(rt);
+ renderer->setRenderPassDescriptor(rp);
+ renderer->setCommandBuffer(cb);
+ renderer->setRenderPassRecordingCallbacks(mainPassRecordingStart, mainPassRecordingEnd, callbackUserData);
+
+ m_currentFrameCommandBuffer = cb;
+ m_currentFrameRenderPass = rp;
+}
+
+void QSGDefaultRenderContext::renderNextRhiFrame(QSGRenderer *renderer)
+{
+ renderer->renderScene();
+}
+
+void QSGDefaultRenderContext::endNextRhiFrame(QSGRenderer *renderer)
+{
+ Q_UNUSED(renderer);
+ m_currentFrameCommandBuffer = nullptr;
+ m_currentFrameRenderPass = nullptr;
+}
+
/*!
Returns a shared pointer to a depth stencil buffer that can be used with \a fbo.
*/
@@ -226,13 +301,21 @@ QSGTexture *QSGDefaultRenderContext::createTexture(const QImage &image, uint fla
// The atlas implementation is only supported from the render thread and
// does not support mipmaps.
- if (!mipmap && atlas && openglContext() && QThread::currentThread() == openglContext()->thread()) {
- QSGTexture *t = m_atlasManager->create(image, alpha);
- if (t)
- return t;
+ if (m_rhi) {
+ if (!mipmap && atlas && QThread::currentThread() == m_rhi->thread()) {
+ QSGTexture *t = m_rhiAtlasManager->create(image, alpha);
+ if (t)
+ return t;
+ }
+ } else {
+ if (!mipmap && atlas && openglContext() && QThread::currentThread() == openglContext()->thread()) {
+ QSGTexture *t = m_glAtlasManager->create(image, alpha);
+ if (t)
+ return t;
+ }
}
- QSGPlainTexture *texture = new QSGPlainTexture();
+ QSGPlainTexture *texture = new QSGPlainTexture;
texture->setImage(image);
if (texture->hasAlphaChannel() && !alpha)
texture->setHasAlphaChannel(false);
@@ -247,9 +330,15 @@ QSGRenderer *QSGDefaultRenderContext::createRenderer()
QSGTexture *QSGDefaultRenderContext::compressedTextureForFactory(const QSGCompressedTextureFactory *factory) const
{
- // The atlas implementation is only supported from the render thread
- if (openglContext() && QThread::currentThread() == openglContext()->thread())
- return m_atlasManager->create(factory);
+ // This is only used for atlasing compressed textures. Returning null implies no atlas.
+
+ if (m_rhi) {
+ // ###
+ } else if (openglContext() && QThread::currentThread() == openglContext()->thread()) {
+ // The atlas implementation is only supported from the render thread
+ return m_glAtlasManager->create(factory);
+ }
+
return nullptr;
}
@@ -307,6 +396,11 @@ void QSGDefaultRenderContext::initializeShader(QSGMaterialShader *shader)
shader->initialize();
}
+void QSGDefaultRenderContext::initializeRhiShader(QSGMaterialRhiShader *shader, QShader::Variant shaderVariant)
+{
+ QSGMaterialRhiShaderPrivate::get(shader)->prepare(shaderVariant);
+}
+
void QSGDefaultRenderContext::setAttachToGraphicsContext(bool attach)
{
Q_ASSERT(!isValid());
@@ -320,6 +414,9 @@ QSGDefaultRenderContext *QSGDefaultRenderContext::from(QOpenGLContext *context)
bool QSGDefaultRenderContext::separateIndexBuffer() const
{
+ if (m_rhi)
+ return true;
+
// WebGL: A given WebGLBuffer object may only be bound to one of
// the ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER target in its
// lifetime. An attempt to bind a buffer object to the other
@@ -335,7 +432,10 @@ QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(con
QString key = fontKey(font);
QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(key, 0);
if (!cache) {
- cache = new QSGDefaultDistanceFieldGlyphCache(openglContext(), font);
+ if (m_rhi)
+ cache = new QSGRhiDistanceFieldGlyphCache(m_rhi, font);
+ else
+ cache = new QSGOpenGLDistanceFieldGlyphCache(openglContext(), font);
m_glyphCaches.insert(key, cache);
}
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
index 57aa4b4c90..2fdb3a48dd 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext_p.h
+++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
@@ -52,15 +52,29 @@
//
#include <QtQuick/private/qsgcontext_p.h>
+#include <QtGui/private/qshader_p.h>
+
+#if QT_CONFIG(opengl)
#include <QtQuick/private/qsgdepthstencilbuffer_p.h>
+#endif
QT_BEGIN_NAMESPACE
+class QRhi;
+class QRhiCommandBuffer;
+class QRhiRenderPassDescriptor;
class QOpenGLContext;
class QSGMaterialShader;
+class QSGMaterialRhiShader;
class QOpenGLFramebufferObject;
+class QSGDepthStencilBufferManager;
+class QSGDepthStencilBuffer;
+
+namespace QSGOpenGLAtlasTexture {
+ class Manager;
+}
-namespace QSGAtlasTexture {
+namespace QSGRhiAtlasTexture {
class Manager;
}
@@ -70,12 +84,41 @@ class Q_QUICK_PRIVATE_EXPORT QSGDefaultRenderContext : public QSGRenderContext
public:
QSGDefaultRenderContext(QSGContext *context);
+ QRhi *rhi() const override { return m_rhi; }
QOpenGLContext *openglContext() const { return m_gl; }
- bool isValid() const override { return m_gl; }
-
- void initialize(void *context) override;
+ bool isValid() const override { return m_gl || m_rhi; }
+
+ static const int INIT_PARAMS_MAGIC = 0x50E;
+ struct InitParams : public QSGRenderContext::InitParams {
+ int sType = INIT_PARAMS_MAGIC; // help discovering broken code passing something else as 'context'
+ QRhi *rhi = nullptr;
+ int sampleCount = 1; // 1, 4, 8, ...
+ QOpenGLContext *openGLContext = nullptr; // ### Qt 6: remove
+ // only used as a hint f.ex. in the texture atlas init
+ QSize initialSurfacePixelSize;
+ // The first window that will be used with this rc, if available.
+ // Only a hint, to help picking better values for atlases.
+ QSurface *maybeSurface = nullptr;
+ };
+
+ void initialize(const QSGRenderContext::InitParams *params) override;
void invalidate() override;
+
+ void prepareSync(qreal devicePixelRatio) override;
+ void beginNextFrame(QSGRenderer *renderer,
+ RenderPassCallback mainPassRecordingStart,
+ RenderPassCallback mainPassRecordingEnd,
+ void *callbackUserData) override;
void renderNextFrame(QSGRenderer *renderer, uint fboId) override;
+ void endNextFrame(QSGRenderer *renderer) override;
+
+ void beginNextRhiFrame(QSGRenderer *renderer,
+ QRhiRenderTarget *rt, QRhiRenderPassDescriptor *rp, QRhiCommandBuffer *cb,
+ RenderPassCallback mainPassRecordingStart,
+ RenderPassCallback mainPassRecordingEnd,
+ void *callbackUserData) override;
+ void renderNextRhiFrame(QSGRenderer *renderer) override;
+ void endNextRhiFrame(QSGRenderer *renderer) override;
QSGDistanceFieldGlyphCache *distanceFieldGlyphCache(const QRawFont &font) override;
@@ -88,6 +131,7 @@ public:
virtual void compileShader(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode = nullptr, const char *fragmentCode = nullptr);
virtual void initializeShader(QSGMaterialShader *shader);
+ virtual void initializeRhiShader(QSGMaterialRhiShader *shader, QShader::Variant shaderVariant);
void setAttachToGraphicsContext(bool attach) override;
@@ -97,16 +141,44 @@ public:
int maxTextureSize() const override { return m_maxTextureSize; }
bool separateIndexBuffer() const;
+ int msaaSampleCount() const { return m_initParams.sampleCount; }
+
+ QRhiCommandBuffer *currentFrameCommandBuffer() const {
+ // may be null if not in an active frame, but returning null is valid then
+ return m_currentFrameCommandBuffer;
+ }
+ QRhiRenderPassDescriptor *currentFrameRenderPass() const {
+ // may be null if not in an active frame, but returning null is valid then
+ return m_currentFrameRenderPass;
+ }
+
+ qreal currentDevicePixelRatio() const
+ {
+ // Valid starting from QQuickWindow::syncSceneGraph(). This takes the
+ // redirections, e.g. QQuickWindow::setRenderTarget(), into account.
+ // This calculation logic matches what the renderer does, so this is
+ // the same value that gets exposed in RenderState::devicePixelRatio()
+ // to material shaders. This getter is useful to perform dpr-related
+ // operations in the sync phase (in updatePaintNode()).
+ return m_currentDevicePixelRatio;
+ }
+
protected:
static QString fontKey(const QRawFont &font);
+ InitParams m_initParams;
+ QRhi *m_rhi;
QOpenGLContext *m_gl;
QSGDepthStencilBufferManager *m_depthStencilManager;
int m_maxTextureSize;
bool m_brokenIBOs;
bool m_serializedRender;
bool m_attachToGLContext;
- QSGAtlasTexture::Manager *m_atlasManager;
+ QSGOpenGLAtlasTexture::Manager *m_glAtlasManager;
+ QSGRhiAtlasTexture::Manager *m_rhiAtlasManager;
+ QRhiCommandBuffer *m_currentFrameCommandBuffer;
+ QRhiRenderPassDescriptor *m_currentFrameRenderPass;
+ qreal m_currentDevicePixelRatio;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultspritenode.cpp b/src/quick/scenegraph/qsgdefaultspritenode.cpp
index 8761d99c1f..6422a252d9 100644
--- a/src/quick/scenegraph/qsgdefaultspritenode.cpp
+++ b/src/quick/scenegraph/qsgdefaultspritenode.cpp
@@ -84,6 +84,7 @@ public:
QQuickSpriteMaterial::QQuickSpriteMaterial()
{
setFlag(Blending, true);
+ setFlag(SupportsRhiShader, true);
}
QQuickSpriteMaterial::~QQuickSpriteMaterial()
@@ -91,10 +92,10 @@ QQuickSpriteMaterial::~QQuickSpriteMaterial()
delete texture;
}
-class SpriteMaterialData : public QSGMaterialShader
+class SpriteMaterialShader : public QSGMaterialShader
{
public:
- SpriteMaterialData()
+ SpriteMaterialShader()
{
setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/sprite.vert"));
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/sprite.frag"));
@@ -135,9 +136,79 @@ public:
int m_animPos_id;
};
+class SpriteMaterialRhiShader : public QSGMaterialRhiShader
+{
+public:
+ SpriteMaterialRhiShader();
+
+ bool updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+ void updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+SpriteMaterialRhiShader::SpriteMaterialRhiShader()
+{
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/sprite.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/sprite.frag.qsb"));
+}
+
+bool SpriteMaterialRhiShader::updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+#ifdef QT_NO_DEBUG
+ Q_UNUSED(oldMaterial);
+#endif
+ Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
+ QQuickSpriteMaterial *mat = static_cast<QQuickSpriteMaterial *>(newMaterial);
+
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 96);
+
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ changed = true;
+ }
+
+ float animPosAndData[7] = { mat->animX1, mat->animY1, mat->animX2, mat->animY2,
+ mat->animW, mat->animH, mat->animT };
+ memcpy(buf->data() + 64, animPosAndData, 28);
+ changed = true;
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(buf->data() + 92, &opacity, 4);
+ changed = true;
+ }
+
+ return changed;
+}
+
+void SpriteMaterialRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ if (binding != 1)
+ return;
+
+#ifdef QT_NO_DEBUG
+ Q_UNUSED(oldMaterial);
+#endif
+ Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
+ QQuickSpriteMaterial *mat = static_cast<QQuickSpriteMaterial *>(newMaterial);
+
+ QSGTexture *t = mat->texture;
+ t->updateRhiTexture(state.rhi(), state.resourceUpdateBatch());
+ *texture = t;
+}
+
QSGMaterialShader *QQuickSpriteMaterial::createShader() const
{
- return new SpriteMaterialData;
+ if (flags().testFlag(RhiShaderWanted))
+ return new SpriteMaterialRhiShader;
+ else
+ return new SpriteMaterialShader;
}
static QSGGeometry::Attribute Sprite_Attributes[] = {
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
index ae6336718e..0fa680a244 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
@@ -224,15 +224,19 @@ void QSGDistanceFieldGlyphNode::updateGeometry()
const QPointF position = positions.at(i);
const QSGDistanceFieldGlyphCache::Texture *texture = m_glyph_cache->glyphTexture(glyphIndex);
- if (texture->textureId && !m_texture)
+ if ((!texture->rhiBased && texture->textureId && !m_texture)
+ || (texture->rhiBased && texture->texture && !m_texture))
+ {
m_texture = texture;
+ }
// As we use UNSIGNED_SHORT indexing in the geometry, we overload the
- // "glyphsInOtherTextures" concept as overflow for if there are more than
- // 65536 vertices to render which would otherwise exceed the maximum index
- // size. This will cause sub-nodes to be recursively created to handle any
- // number of glyphs.
- if (m_texture != texture || vp.size() >= 65536) {
+ // "glyphsInOtherTextures" concept as overflow for if there are more
+ // than 65535 vertices to render which would otherwise exceed the
+ // maximum index size. (leave 0xFFFF unused in order not to clash with
+ // primitive restart) This will cause sub-nodes to be recursively
+ // created to handle any number of glyphs.
+ if (m_texture != texture || vp.size() >= 65535) {
if (texture->textureId) {
GlyphInfo &glyphInfo = glyphsInOtherTextures[texture];
glyphInfo.indexes.append(glyphIndex);
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp
index aa58218505..e8e9f76d04 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -38,7 +38,7 @@
****************************************************************************/
#include "qsgdistancefieldglyphnode_p_p.h"
-#include <QtQuick/private/qsgtexture_p.h>
+#include "qsgrhidistancefieldglyphcache_p.h"
#include <QtGui/qopenglfunctions.h>
#include <QtGui/qsurface.h>
#include <QtGui/qwindow.h>
@@ -46,6 +46,30 @@
QT_BEGIN_NAMESPACE
+static float qt_sg_envFloat(const char *name, float defaultValue)
+{
+ if (Q_LIKELY(!qEnvironmentVariableIsSet(name)))
+ return defaultValue;
+ bool ok = false;
+ const float value = qgetenv(name).toFloat(&ok);
+ return ok ? value : defaultValue;
+}
+
+static float thresholdFunc(float glyphScale)
+{
+ static const float base = qt_sg_envFloat("QT_DF_BASE", 0.5f);
+ static const float baseDev = qt_sg_envFloat("QT_DF_BASEDEVIATION", 0.065f);
+ static const float devScaleMin = qt_sg_envFloat("QT_DF_SCALEFORMAXDEV", 0.15f);
+ static const float devScaleMax = qt_sg_envFloat("QT_DF_SCALEFORNODEV", 0.3f);
+ return base - ((qBound(devScaleMin, glyphScale, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin) * -baseDev + baseDev);
+}
+
+static float spreadFunc(float glyphScale)
+{
+ static const float range = qt_sg_envFloat("QT_DF_RANGE", 0.06f);
+ return range / glyphScale;
+}
+
class QSGDistanceFieldTextMaterialShader : public QSGMaterialShader
{
public:
@@ -87,30 +111,6 @@ QSGDistanceFieldTextMaterialShader::QSGDistanceFieldTextMaterialShader()
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldtext.frag"));
}
-static float qt_sg_envFloat(const char *name, float defaultValue)
-{
- if (Q_LIKELY(!qEnvironmentVariableIsSet(name)))
- return defaultValue;
- bool ok = false;
- const float value = qgetenv(name).toFloat(&ok);
- return ok ? value : defaultValue;
-}
-
-static float thresholdFunc(float glyphScale)
-{
- static const float base = qt_sg_envFloat("QT_DF_BASE", 0.5f);
- static const float baseDev = qt_sg_envFloat("QT_DF_BASEDEVIATION", 0.065f);
- static const float devScaleMin = qt_sg_envFloat("QT_DF_SCALEFORMAXDEV", 0.15f);
- static const float devScaleMax = qt_sg_envFloat("QT_DF_SCALEFORNODEV", 0.3f);
- return base - ((qBound(devScaleMin, glyphScale, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin) * -baseDev + baseDev);
-}
-
-static float spreadFunc(float glyphScale)
-{
- static const float range = qt_sg_envFloat("QT_DF_RANGE", 0.06f);
- return range / glyphScale;
-}
-
void QSGDistanceFieldTextMaterialShader::updateAlphaRange()
{
float combinedScale = m_fontScale * m_matrixScale;
@@ -207,16 +207,115 @@ void QSGDistanceFieldTextMaterialShader::updateState(const RenderState &state, Q
}
}
+class QSGDistanceFieldTextMaterialRhiShader : public QSGMaterialRhiShader
+{
+public:
+ QSGDistanceFieldTextMaterialRhiShader(bool alphaTexture);
+
+ bool updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+
+ void updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+
+protected:
+ float m_fontScale = 1.0;
+ float m_matrixScale = 1.0;
+};
+
+QSGDistanceFieldTextMaterialRhiShader::QSGDistanceFieldTextMaterialRhiShader(bool alphaTexture)
+{
+ setShaderFileName(VertexStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext.vert.qsb"));
+ if (alphaTexture)
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext_a.frag.qsb"));
+ else
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext.frag.qsb"));
+}
+
+bool QSGDistanceFieldTextMaterialRhiShader::updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
+ QSGDistanceFieldTextMaterial *mat = static_cast<QSGDistanceFieldTextMaterial *>(newMaterial);
+ QSGDistanceFieldTextMaterial *oldMat = static_cast<QSGDistanceFieldTextMaterial *>(oldMaterial);
+
+ // updateUniformData() is called before updateSampledImage() by the
+ // renderer. Hence updating the glyph cache stuff here.
+ const bool textureUpdated = mat->updateTextureSizeAndWrapper();
+ Q_ASSERT(mat->wrapperTexture());
+ Q_ASSERT(oldMat == nullptr || oldMat->texture());
+
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 104);
+
+ bool updateRange = false;
+ if (!oldMat || mat->fontScale() != oldMat->fontScale()) {
+ m_fontScale = mat->fontScale();
+ updateRange = true;
+ }
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ changed = true;
+ m_matrixScale = qSqrt(qAbs(state.determinant())) * state.devicePixelRatio();
+ updateRange = true;
+ }
+ if (textureUpdated || !oldMat || oldMat->texture()->texture != mat->texture()->texture) {
+ const QVector2D ts(1.0f / mat->textureSize().width(), 1.0f / mat->textureSize().height());
+ Q_ASSERT(sizeof(ts) == 8);
+ memcpy(buf->data() + 64, &ts, 8);
+ changed = true;
+ }
+ if (!oldMat || mat->color() != oldMat->color() || state.isOpacityDirty()) {
+ const QVector4D color = mat->color() * state.opacity();
+ Q_ASSERT(sizeof(color) == 16);
+ memcpy(buf->data() + 80, &color, 16);
+ changed = true;
+ }
+ if (updateRange) { // deferred because depends on m_fontScale and m_matrixScale
+ const float combinedScale = m_fontScale * m_matrixScale;
+ const float base = thresholdFunc(combinedScale);
+ const float range = spreadFunc(combinedScale);
+ const QVector2D alphaMinMax(qMax(0.0f, base - range), qMin(base + range, 1.0f));
+ memcpy(buf->data() + 96, &alphaMinMax, 8);
+ changed = true;
+ }
+
+ // move texture uploads/copies onto the renderer's soon-to-be-committed list
+ static_cast<QSGRhiDistanceFieldGlyphCache *>(mat->glyphCache())->commitResourceUpdates(state.resourceUpdateBatch());
+
+ return changed;
+}
+
+void QSGDistanceFieldTextMaterialRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *)
+{
+ Q_UNUSED(state);
+ if (binding != 1)
+ return;
+
+ QSGDistanceFieldTextMaterial *mat = static_cast<QSGDistanceFieldTextMaterial *>(newMaterial);
+ QSGTexture *t = mat->wrapperTexture();
+ t->setFiltering(QSGTexture::Linear);
+ *texture = t;
+}
+
QSGDistanceFieldTextMaterial::QSGDistanceFieldTextMaterial()
: m_glyph_cache(nullptr)
, m_texture(nullptr)
, m_fontScale(1.0)
+ , m_sgTexture(nullptr)
{
- setFlag(Blending | RequiresDeterminant, true);
+ setFlag(Blending | RequiresDeterminant | SupportsRhiShader, true);
}
QSGDistanceFieldTextMaterial::~QSGDistanceFieldTextMaterial()
{
+ delete m_sgTexture;
}
QSGMaterialType *QSGDistanceFieldTextMaterial::type() const
@@ -235,7 +334,10 @@ void QSGDistanceFieldTextMaterial::setColor(const QColor &color)
QSGMaterialShader *QSGDistanceFieldTextMaterial::createShader() const
{
- return new QSGDistanceFieldTextMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new QSGDistanceFieldTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ else
+ return new QSGDistanceFieldTextMaterialShader;
}
bool QSGDistanceFieldTextMaterial::updateTextureSize()
@@ -246,9 +348,26 @@ bool QSGDistanceFieldTextMaterial::updateTextureSize()
if (m_texture->size != m_size) {
m_size = m_texture->size;
return true;
- } else {
- return false;
}
+
+ return false;
+}
+
+// When using the RHI we need a QSGTexture wrapping the QRhiTexture, just
+// exposing a QRhiTexture * (which would be the equivalent of GLuint textureId)
+// is not sufficient to play nice with the material.
+bool QSGDistanceFieldTextMaterial::updateTextureSizeAndWrapper()
+{
+ bool updated = updateTextureSize();
+ if (updated) {
+ if (m_sgTexture)
+ delete m_sgTexture;
+ m_sgTexture = new QSGPlainTexture;
+ m_sgTexture->setTexture(m_texture->texture);
+ m_sgTexture->setTextureSize(m_size);
+ m_sgTexture->setOwnsTexture(false);
+ }
+ return updated;
}
int QSGDistanceFieldTextMaterial::compare(const QSGMaterial *o) const
@@ -262,8 +381,8 @@ int QSGDistanceFieldTextMaterial::compare(const QSGMaterial *o) const
}
if (m_color != other->m_color)
return &m_color < &other->m_color ? -1 : 1;
- int t0 = m_texture ? m_texture->textureId : 0;
- int t1 = other->m_texture ? other->m_texture->textureId : 0;
+ int t0 = m_texture ? (m_texture->rhiBased ? qintptr(m_texture->texture) : m_texture->textureId) : 0;
+ int t1 = other->m_texture ? (other->m_texture->rhiBased ? qintptr(other->m_texture->texture) : other->m_texture->textureId) : 0;
return t0 - t1;
}
@@ -308,6 +427,39 @@ void DistanceFieldStyledTextMaterialShader::updateState(const RenderState &state
}
}
+class DistanceFieldStyledTextMaterialRhiShader : public QSGDistanceFieldTextMaterialRhiShader
+{
+public:
+ DistanceFieldStyledTextMaterialRhiShader(bool alphaTexture);
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+DistanceFieldStyledTextMaterialRhiShader::DistanceFieldStyledTextMaterialRhiShader(bool alphaTexture)
+ : QSGDistanceFieldTextMaterialRhiShader(alphaTexture)
+{
+}
+
+bool DistanceFieldStyledTextMaterialRhiShader::updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = QSGDistanceFieldTextMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+ QSGDistanceFieldStyledTextMaterial *mat = static_cast<QSGDistanceFieldStyledTextMaterial *>(newMaterial);
+ QSGDistanceFieldStyledTextMaterial *oldMat = static_cast<QSGDistanceFieldStyledTextMaterial *>(oldMaterial);
+
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 128);
+
+ if (!oldMat || mat->styleColor() != oldMat->styleColor() || state.isOpacityDirty()) {
+ QVector4D styleColor = mat->styleColor();
+ styleColor *= state.opacity();
+ memcpy(buf->data() + 112, &styleColor, 16);
+ changed = true;
+ }
+
+ return changed;
+}
+
QSGDistanceFieldStyledTextMaterial::QSGDistanceFieldStyledTextMaterial()
: QSGDistanceFieldTextMaterial()
{
@@ -391,6 +543,53 @@ void DistanceFieldOutlineTextMaterialShader::updateState(const RenderState &stat
updateOutlineAlphaRange(material->glyphCache()->distanceFieldRadius());
}
+class DistanceFieldOutlineTextMaterialRhiShader : public DistanceFieldStyledTextMaterialRhiShader
+{
+public:
+ DistanceFieldOutlineTextMaterialRhiShader(bool alphaTexture);
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+DistanceFieldOutlineTextMaterialRhiShader::DistanceFieldOutlineTextMaterialRhiShader(bool alphaTexture)
+ : DistanceFieldStyledTextMaterialRhiShader(alphaTexture)
+{
+ setShaderFileName(VertexStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext.vert.qsb"));
+ if (alphaTexture)
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag.qsb"));
+ else
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext.frag.qsb"));
+}
+
+bool DistanceFieldOutlineTextMaterialRhiShader::updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = DistanceFieldStyledTextMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+ QSGDistanceFieldOutlineTextMaterial *mat = static_cast<QSGDistanceFieldOutlineTextMaterial *>(newMaterial);
+ QSGDistanceFieldOutlineTextMaterial *oldMat = static_cast<QSGDistanceFieldOutlineTextMaterial *>(oldMaterial);
+
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 136);
+
+ if (!oldMat || mat->fontScale() != oldMat->fontScale() || state.isMatrixDirty()) {
+ float dfRadius = mat->glyphCache()->distanceFieldRadius();
+ float combinedScale = m_fontScale * m_matrixScale;
+ float base = thresholdFunc(combinedScale);
+ float range = spreadFunc(combinedScale);
+ float outlineLimit = qMax(0.2f, base - 0.5f / dfRadius / m_fontScale);
+ float alphaMin = qMax(0.0f, base - range);
+ float styleAlphaMin0 = qMax(0.0f, outlineLimit - range);
+ float styleAlphaMin1 = qMin(outlineLimit + range, alphaMin);
+ memcpy(buf->data() + 128, &styleAlphaMin0, 4);
+ memcpy(buf->data() + 132, &styleAlphaMin1, 4);
+ changed = true;
+ }
+
+ return changed;
+}
QSGDistanceFieldOutlineTextMaterial::QSGDistanceFieldOutlineTextMaterial()
: QSGDistanceFieldStyledTextMaterial()
@@ -409,7 +608,10 @@ QSGMaterialType *QSGDistanceFieldOutlineTextMaterial::type() const
QSGMaterialShader *QSGDistanceFieldOutlineTextMaterial::createShader() const
{
- return new DistanceFieldOutlineTextMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new DistanceFieldOutlineTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ else
+ return new DistanceFieldOutlineTextMaterialShader;
}
@@ -463,6 +665,49 @@ void DistanceFieldShiftedStyleTextMaterialShader::updateShift(qreal fontScale, c
program()->setUniformValue(m_shift_id, texel);
}
+class DistanceFieldShiftedStyleTextMaterialRhiShader : public DistanceFieldStyledTextMaterialRhiShader
+{
+public:
+ DistanceFieldShiftedStyleTextMaterialRhiShader(bool alphaTexture);
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+DistanceFieldShiftedStyleTextMaterialRhiShader::DistanceFieldShiftedStyleTextMaterialRhiShader(bool alphaTexture)
+ : DistanceFieldStyledTextMaterialRhiShader(alphaTexture)
+{
+ setShaderFileName(VertexStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext.vert.qsb"));
+ if (alphaTexture)
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag.qsb"));
+ else
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext.frag.qsb"));
+}
+
+bool DistanceFieldShiftedStyleTextMaterialRhiShader::updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = DistanceFieldStyledTextMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+ QSGDistanceFieldShiftedStyleTextMaterial *mat = static_cast<QSGDistanceFieldShiftedStyleTextMaterial *>(newMaterial);
+ QSGDistanceFieldShiftedStyleTextMaterial *oldMat = static_cast<QSGDistanceFieldShiftedStyleTextMaterial *>(oldMaterial);
+
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 136);
+
+ if (!oldMat || oldMat->fontScale() != mat->fontScale() || oldMat->shift() != mat->shift()
+ || oldMat->textureSize() != mat->textureSize())
+ {
+ QPointF shift(1.0 / mat->fontScale() * mat->shift().x(),
+ 1.0 / mat->fontScale() * mat->shift().y());
+ memcpy(buf->data() + 128, &shift, 8);
+ changed = true;
+ }
+
+ return changed;
+}
+
QSGDistanceFieldShiftedStyleTextMaterial::QSGDistanceFieldShiftedStyleTextMaterial()
: QSGDistanceFieldStyledTextMaterial()
{
@@ -480,7 +725,10 @@ QSGMaterialType *QSGDistanceFieldShiftedStyleTextMaterial::type() const
QSGMaterialShader *QSGDistanceFieldShiftedStyleTextMaterial::createShader() const
{
- return new DistanceFieldShiftedStyleTextMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new DistanceFieldShiftedStyleTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ else
+ return new DistanceFieldShiftedStyleTextMaterialShader;
}
int QSGDistanceFieldShiftedStyleTextMaterial::compare(const QSGMaterial *o) const
@@ -491,6 +739,7 @@ int QSGDistanceFieldShiftedStyleTextMaterial::compare(const QSGMaterial *o) cons
return QSGDistanceFieldStyledTextMaterial::compare(o);
}
+
class QSGHiQSubPixelDistanceFieldTextMaterialShader : public QSGDistanceFieldTextMaterialShader
{
public:
@@ -555,6 +804,75 @@ void QSGHiQSubPixelDistanceFieldTextMaterialShader::updateState(const RenderStat
QSGDistanceFieldTextMaterialShader::updateState(state, newEffect, oldEffect);
}
+class QSGHiQSubPixelDistanceFieldTextMaterialRhiShader : public QSGDistanceFieldTextMaterialRhiShader
+{
+public:
+ QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture);
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+ bool updateGraphicsPipelineState(RenderState &state, GraphicsPipelineState *ps,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+QSGHiQSubPixelDistanceFieldTextMaterialRhiShader::QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture)
+ : QSGDistanceFieldTextMaterialRhiShader(alphaTexture)
+{
+ setFlag(UpdatesGraphicsPipelineState, true);
+
+ setShaderFileName(VertexStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert.qsb"));
+ if (alphaTexture)
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag.qsb"));
+ else
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag.qsb"));
+}
+
+bool QSGHiQSubPixelDistanceFieldTextMaterialRhiShader::updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = QSGDistanceFieldTextMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+ QSGHiQSubPixelDistanceFieldTextMaterial *mat = static_cast<QSGHiQSubPixelDistanceFieldTextMaterial *>(newMaterial);
+ QSGHiQSubPixelDistanceFieldTextMaterial *oldMat = static_cast<QSGHiQSubPixelDistanceFieldTextMaterial *>(oldMaterial);
+
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 128);
+
+ if (!oldMat || mat->fontScale() != oldMat->fontScale()) {
+ float fontScale = mat->fontScale();
+ memcpy(buf->data() + 104, &fontScale, 4);
+ changed = true;
+ }
+
+ if (!oldMat || state.isMatrixDirty()) {
+ int viewportWidth = state.viewportRect().width();
+ QMatrix4x4 mat = state.combinedMatrix().inverted();
+ QVector4D vecDelta = mat.column(0) * (qreal(2) / viewportWidth);
+ memcpy(buf->data() + 112, &vecDelta, 16);
+ }
+
+ return changed;
+}
+
+bool QSGHiQSubPixelDistanceFieldTextMaterialRhiShader::updateGraphicsPipelineState(RenderState &state, GraphicsPipelineState *ps,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(state);
+ Q_UNUSED(oldMaterial);
+ QSGHiQSubPixelDistanceFieldTextMaterial *mat = static_cast<QSGHiQSubPixelDistanceFieldTextMaterial *>(newMaterial);
+
+ ps->blendEnable = true;
+ ps->srcColor = GraphicsPipelineState::ConstantColor;
+ ps->dstColor = GraphicsPipelineState::OneMinusSrcColor;
+
+ const QVector4D color = mat->color();
+ // this is dynamic state but it's - magic! - taken care of by the renderer
+ ps->blendConstant = QColor::fromRgbF(color.x(), color.y(), color.z(), 1.0f);
+
+ return true;
+}
+
QSGMaterialType *QSGHiQSubPixelDistanceFieldTextMaterial::type() const
{
static QSGMaterialType type;
@@ -563,7 +881,10 @@ QSGMaterialType *QSGHiQSubPixelDistanceFieldTextMaterial::type() const
QSGMaterialShader *QSGHiQSubPixelDistanceFieldTextMaterial::createShader() const
{
- return new QSGHiQSubPixelDistanceFieldTextMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ else
+ return new QSGHiQSubPixelDistanceFieldTextMaterialShader;
}
@@ -580,6 +901,25 @@ QSGLoQSubPixelDistanceFieldTextMaterialShader::QSGLoQSubPixelDistanceFieldTextMa
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/loqsubpixeldistancefieldtext.frag"));
}
+class QSGLoQSubPixelDistanceFieldTextMaterialRhiShader : public QSGHiQSubPixelDistanceFieldTextMaterialRhiShader
+{
+public:
+ QSGLoQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture);
+};
+
+QSGLoQSubPixelDistanceFieldTextMaterialRhiShader::QSGLoQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture)
+ : QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(alphaTexture)
+{
+ setShaderFileName(VertexStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert.qsb"));
+ if (alphaTexture)
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag.qsb"));
+ else
+ setShaderFileName(FragmentStage,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag.qsb"));
+}
+
QSGMaterialType *QSGLoQSubPixelDistanceFieldTextMaterial::type() const
{
static QSGMaterialType type;
@@ -588,7 +928,10 @@ QSGMaterialType *QSGLoQSubPixelDistanceFieldTextMaterial::type() const
QSGMaterialShader *QSGLoQSubPixelDistanceFieldTextMaterial::createShader() const
{
- return new QSGLoQSubPixelDistanceFieldTextMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new QSGLoQSubPixelDistanceFieldTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
+ else
+ return new QSGLoQSubPixelDistanceFieldTextMaterialShader;
}
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
index 7008f20925..7b6be29bcb 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
@@ -60,7 +60,8 @@ QT_BEGIN_NAMESPACE
class QSGRenderContext;
class QSGDistanceFieldTextMaterial;
-class QSGDistanceFieldGlyphNode: public QSGGlyphNode, public QSGDistanceFieldGlyphConsumer
+
+class QSGDistanceFieldGlyphNode : public QSGGlyphNode, public QSGDistanceFieldGlyphConsumer
{
public:
QSGDistanceFieldGlyphNode(QSGRenderContext *context);
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h
index c13a0898eb..ee1ed8f337 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h
@@ -52,11 +52,14 @@
//
#include <QtQuick/qsgmaterial.h>
+#include <QtQuick/private/qsgplaintexture_p.h>
#include "qsgdistancefieldglyphnode_p.h"
#include "qsgadaptationlayer_p.h"
QT_BEGIN_NAMESPACE
+class QSGPlainTexture;
+
class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldTextMaterial: public QSGMaterial
{
public:
@@ -82,6 +85,8 @@ public:
QSize textureSize() const { return m_size; }
bool updateTextureSize();
+ bool updateTextureSizeAndWrapper();
+ QSGTexture *wrapperTexture() const { return m_sgTexture; }
protected:
QSize m_size;
@@ -89,6 +94,7 @@ protected:
QSGDistanceFieldGlyphCache *m_glyph_cache;
const QSGDistanceFieldGlyphCache::Texture *m_texture;
qreal m_fontScale;
+ QSGPlainTexture *m_sgTexture;
};
class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldStyledTextMaterial : public QSGDistanceFieldTextMaterial
diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgopengldistancefieldglyphcache.cpp
index c51358df7c..b6b6f3b057 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
+++ b/src/quick/scenegraph/qsgopengldistancefieldglyphcache.cpp
@@ -37,10 +37,11 @@
**
****************************************************************************/
-#include "qsgdefaultdistancefieldglyphcache_p.h"
+#include "qsgopengldistancefieldglyphcache_p.h"
#include <QtCore/qelapsedtimer.h>
#include <QtCore/qbuffer.h>
+#include <QtCore/qendian.h>
#include <QtQml/qqmlfile.h>
#include <QtGui/private/qdistancefield_p.h>
@@ -61,12 +62,12 @@ QT_BEGIN_NAMESPACE
DEFINE_BOOL_CONFIG_OPTION(qmlUseGlyphCacheWorkaround, QML_USE_GLYPHCACHE_WORKAROUND)
DEFINE_BOOL_CONFIG_OPTION(qsgPreferFullSizeGlyphCacheTextures, QSG_PREFER_FULLSIZE_GLYPHCACHE_TEXTURES)
-#if !defined(QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING)
-# define QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING 2
+#if !defined(QSG_OPENGL_DISTANCEFIELD_GLYPH_CACHE_PADDING)
+# define QSG_OPENGL_DISTANCEFIELD_GLYPH_CACHE_PADDING 2
#endif
-QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c,
- const QRawFont &font)
+QSGOpenGLDistanceFieldGlyphCache::QSGOpenGLDistanceFieldGlyphCache(QOpenGLContext *c,
+ const QRawFont &font)
: QSGDistanceFieldGlyphCache(font)
, m_maxTextureWidth(0)
, m_maxTextureHeight(0)
@@ -96,7 +97,7 @@ QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLCont
loadPregeneratedCache(font);
}
-QSGDefaultDistanceFieldGlyphCache::~QSGDefaultDistanceFieldGlyphCache()
+QSGOpenGLDistanceFieldGlyphCache::~QSGOpenGLDistanceFieldGlyphCache()
{
for (int i = 0; i < m_textures.count(); ++i)
m_funcs->glDeleteTextures(1, &m_textures[i].texture);
@@ -108,12 +109,12 @@ QSGDefaultDistanceFieldGlyphCache::~QSGDefaultDistanceFieldGlyphCache()
delete m_areaAllocator;
}
-void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
+void QSGOpenGLDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
{
QList<GlyphPosition> glyphPositions;
QVector<glyph_t> glyphsToRender;
- const int padding = QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING;
+ const int padding = QSG_OPENGL_DISTANCEFIELD_GLYPH_CACHE_PADDING;
const qreal scaleFactor = qreal(1) / QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution);
if (m_maxTextureHeight == 0) {
@@ -182,7 +183,7 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyph
markGlyphsToRender(glyphsToRender);
}
-void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QList<QDistanceField> &glyphs)
+void QSGOpenGLDistanceFieldGlyphCache::storeGlyphs(const QList<QDistanceField> &glyphs)
{
typedef QHash<TextureInfo *, QVector<glyph_t> > GlyphTextureHash;
typedef GlyphTextureHash::const_iterator GlyphTextureHashConstIt;
@@ -248,21 +249,22 @@ void QSGDefaultDistanceFieldGlyphCache::storeGlyphs(const QList<QDistanceField>
Texture t;
t.textureId = i.key()->texture;
t.size = i.key()->size;
+ t.rhiBased = false;
setGlyphsTexture(i.value(), t);
}
}
-void QSGDefaultDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
+void QSGOpenGLDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
{
m_unusedGlyphs -= glyphs;
}
-void QSGDefaultDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
+void QSGOpenGLDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
{
m_unusedGlyphs += glyphs;
}
-void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
+void QSGOpenGLDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
int width,
int height)
{
@@ -270,7 +272,7 @@ void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
createTexture(texInfo, width, height, zeroBuf.constData());
}
-void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
+void QSGOpenGLDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
int width,
int height,
const void *pixels)
@@ -317,7 +319,7 @@ static void freeFramebufferFunc(QOpenGLFunctions *funcs, GLuint id)
funcs->glDeleteFramebuffers(1, &id);
}
-void QSGDefaultDistanceFieldGlyphCache::resizeTexture(TextureInfo *texInfo, int width, int height)
+void QSGOpenGLDistanceFieldGlyphCache::resizeTexture(TextureInfo *texInfo, int width, int height)
{
QOpenGLContext *ctx = QOpenGLContext::currentContext();
Q_ASSERT(ctx);
@@ -520,7 +522,7 @@ void QSGDefaultDistanceFieldGlyphCache::resizeTexture(TextureInfo *texInfo, int
m_vao.release();
}
-bool QSGDefaultDistanceFieldGlyphCache::useTextureResizeWorkaround() const
+bool QSGOpenGLDistanceFieldGlyphCache::useTextureResizeWorkaround() const
{
static bool set = false;
static bool useWorkaround = false;
@@ -533,7 +535,7 @@ bool QSGDefaultDistanceFieldGlyphCache::useTextureResizeWorkaround() const
return useWorkaround;
}
-bool QSGDefaultDistanceFieldGlyphCache::useTextureUploadWorkaround() const
+bool QSGOpenGLDistanceFieldGlyphCache::useTextureUploadWorkaround() const
{
static bool set = false;
static bool useWorkaround = false;
@@ -545,7 +547,7 @@ bool QSGDefaultDistanceFieldGlyphCache::useTextureUploadWorkaround() const
return useWorkaround;
}
-bool QSGDefaultDistanceFieldGlyphCache::createFullSizeTextures() const
+bool QSGOpenGLDistanceFieldGlyphCache::createFullSizeTextures() const
{
return qsgPreferFullSizeGlyphCacheTextures() && glyphCount() > QT_DISTANCEFIELD_HIGHGLYPHCOUNT();
}
@@ -602,7 +604,7 @@ namespace {
};
}
-bool QSGDefaultDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
+bool QSGOpenGLDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
{
// The pregenerated data must be loaded first, otherwise the area allocator
// will be wrong
@@ -672,11 +674,11 @@ bool QSGDefaultDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &fo
m_maxTextureWidth);
}
- if (padding != QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING) {
+ if (padding != QSG_OPENGL_DISTANCEFIELD_GLYPH_CACHE_PADDING) {
qWarning("Padding mismatch in '%s'. Font requires %d, but Qt is compiled with %d.",
qPrintable(font.familyName()),
padding,
- QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING);
+ QSG_OPENGL_DISTANCEFIELD_GLYPH_CACHE_PADDING);
}
m_referenceFont.setPixelSize(pixelSize);
@@ -787,6 +789,7 @@ bool QSGDefaultDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &fo
Texture t;
t.textureId = texInfo->texture;
t.size = texInfo->size;
+ t.rhiBased = false;
setGlyphsTexture(glyphs, t);
diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgopengldistancefieldglyphcache_p.h
index 0420ef251e..66d1b52f86 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
+++ b/src/quick/scenegraph/qsgopengldistancefieldglyphcache_p.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QSGDEFAULTDISTANCEFIELDGLYPHCACHE_H
-#define QSGDEFAULTDISTANCEFIELDGLYPHCACHE_H
+#ifndef QSGOPENGLDISTANCEFIELDGLYPHCACHE_H
+#define QSGOPENGLDISTANCEFIELDGLYPHCACHE_H
//
// W A R N I N G
@@ -66,11 +66,11 @@ class QOpenGLSharedResourceGuard;
class QOpenGLFunctions_3_2_Core;
#endif
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultDistanceFieldGlyphCache : public QSGDistanceFieldGlyphCache
+class Q_QUICK_PRIVATE_EXPORT QSGOpenGLDistanceFieldGlyphCache : public QSGDistanceFieldGlyphCache
{
public:
- QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font);
- virtual ~QSGDefaultDistanceFieldGlyphCache();
+ QSGOpenGLDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font);
+ virtual ~QSGOpenGLDistanceFieldGlyphCache();
void requestGlyphs(const QSet<glyph_t> &glyphs) override;
void storeGlyphs(const QList<QDistanceField> &glyphs) override;
@@ -84,6 +84,7 @@ public:
void setMaxTextureCount(int max) { m_maxTextureCount = max; }
int maxTextureCount() const { return m_maxTextureCount; }
+ bool eightBitFormatIsAlphaSwizzled() const override { return !m_coreProfile; }
private:
bool loadPregeneratedCache(const QRawFont &font);
@@ -160,4 +161,4 @@ private:
QT_END_NAMESPACE
-#endif // QSGDEFAULTDISTANCEFIELDGLYPHCACHE_H
+#endif // QSGOPENGLDISTANCEFIELDGLYPHCACHE_H
diff --git a/src/quick/scenegraph/qsgdefaultlayer.cpp b/src/quick/scenegraph/qsgopengllayer.cpp
index b2b123912f..ae5032231d 100644
--- a/src/quick/scenegraph/qsgdefaultlayer.cpp
+++ b/src/quick/scenegraph/qsgopengllayer.cpp
@@ -36,7 +36,7 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-#include "qsgdefaultlayer_p.h"
+#include "qsgopengllayer_p.h"
#include <private/qqmlglobal_p.h>
#include <private/qsgrenderer_p.h>
@@ -88,8 +88,8 @@ namespace
}
}
-QSGDefaultLayer::QSGDefaultLayer(QSGRenderContext *context)
- : QSGLayer()
+QSGOpenGLLayer::QSGOpenGLLayer(QSGRenderContext *context)
+ : QSGLayer(*(new QSGOpenGLLayerPrivate))
, m_item(nullptr)
, m_device_pixel_ratio(1)
, m_format(GL_RGBA)
@@ -114,12 +114,12 @@ QSGDefaultLayer::QSGDefaultLayer(QSGRenderContext *context)
m_context = static_cast<QSGDefaultRenderContext *>(context);
}
-QSGDefaultLayer::~QSGDefaultLayer()
+QSGOpenGLLayer::~QSGOpenGLLayer()
{
invalidated();
}
-void QSGDefaultLayer::invalidated()
+void QSGOpenGLLayer::invalidated()
{
delete m_renderer;
m_renderer = nullptr;
@@ -136,23 +136,29 @@ void QSGDefaultLayer::invalidated()
}
}
-int QSGDefaultLayer::textureId() const
+int QSGOpenGLLayer::textureId() const
{
return m_fbo ? m_fbo->texture() : 0;
}
-bool QSGDefaultLayer::hasAlphaChannel() const
+int QSGOpenGLLayerPrivate::comparisonKey() const
+{
+ Q_Q(const QSGOpenGLLayer);
+ return q->m_fbo ? q->m_fbo->texture() : 0;
+}
+
+bool QSGOpenGLLayer::hasAlphaChannel() const
{
return m_format != GL_RGB;
}
-bool QSGDefaultLayer::hasMipmaps() const
+bool QSGOpenGLLayer::hasMipmaps() const
{
return m_mipmap;
}
-void QSGDefaultLayer::bind()
+void QSGOpenGLLayer::bind()
{
#ifndef QT_NO_DEBUG
if (!m_recursive && m_fbo && ((m_multisampling && m_secondaryFbo->isBound()) || m_fbo->isBound()))
@@ -174,7 +180,7 @@ void QSGDefaultLayer::bind()
}
}
-bool QSGDefaultLayer::updateTexture()
+bool QSGOpenGLLayer::updateTexture()
{
bool doGrab = (m_live || m_grab) && m_dirtyTexture;
if (doGrab)
@@ -185,7 +191,7 @@ bool QSGDefaultLayer::updateTexture()
return doGrab;
}
-void QSGDefaultLayer::setHasMipmaps(bool mipmap)
+void QSGOpenGLLayer::setHasMipmaps(bool mipmap)
{
if (mipmap == m_mipmap)
return;
@@ -195,7 +201,7 @@ void QSGDefaultLayer::setHasMipmaps(bool mipmap)
}
-void QSGDefaultLayer::setItem(QSGNode *item)
+void QSGOpenGLLayer::setItem(QSGNode *item)
{
if (item == m_item)
return;
@@ -211,7 +217,7 @@ void QSGDefaultLayer::setItem(QSGNode *item)
markDirtyTexture();
}
-void QSGDefaultLayer::setRect(const QRectF &rect)
+void QSGOpenGLLayer::setRect(const QRectF &rect)
{
if (rect == m_rect)
return;
@@ -219,7 +225,7 @@ void QSGDefaultLayer::setRect(const QRectF &rect)
markDirtyTexture();
}
-void QSGDefaultLayer::setSize(const QSize &size)
+void QSGOpenGLLayer::setSize(const QSize &size)
{
if (size == m_size)
return;
@@ -235,7 +241,7 @@ void QSGDefaultLayer::setSize(const QSize &size)
markDirtyTexture();
}
-void QSGDefaultLayer::setFormat(GLenum format)
+void QSGOpenGLLayer::setFormat(GLenum format)
{
if (format == m_format)
return;
@@ -243,7 +249,7 @@ void QSGDefaultLayer::setFormat(GLenum format)
markDirtyTexture();
}
-void QSGDefaultLayer::setLive(bool live)
+void QSGOpenGLLayer::setLive(bool live)
{
if (live == m_live)
return;
@@ -259,7 +265,7 @@ void QSGDefaultLayer::setLive(bool live)
markDirtyTexture();
}
-void QSGDefaultLayer::scheduleUpdate()
+void QSGOpenGLLayer::scheduleUpdate()
{
if (m_grab)
return;
@@ -268,29 +274,29 @@ void QSGDefaultLayer::scheduleUpdate()
emit updateRequested();
}
-void QSGDefaultLayer::setRecursive(bool recursive)
+void QSGOpenGLLayer::setRecursive(bool recursive)
{
m_recursive = recursive;
}
-void QSGDefaultLayer::setMirrorHorizontal(bool mirror)
+void QSGOpenGLLayer::setMirrorHorizontal(bool mirror)
{
m_mirrorHorizontal = mirror;
}
-void QSGDefaultLayer::setMirrorVertical(bool mirror)
+void QSGOpenGLLayer::setMirrorVertical(bool mirror)
{
m_mirrorVertical = mirror;
}
-void QSGDefaultLayer::markDirtyTexture()
+void QSGOpenGLLayer::markDirtyTexture()
{
m_dirtyTexture = true;
if (m_live || m_grab)
emit updateRequested();
}
-void QSGDefaultLayer::grab()
+void QSGOpenGLLayer::grab()
{
if (!m_item || m_size.isNull()) {
delete m_fbo;
@@ -457,7 +463,7 @@ void QSGDefaultLayer::grab()
markDirtyTexture(); // Continuously update if 'live' and 'recursive'.
}
-QImage QSGDefaultLayer::toImage() const
+QImage QSGOpenGLLayer::toImage() const
{
if (m_fbo)
return m_fbo->toImage();
@@ -465,7 +471,7 @@ QImage QSGDefaultLayer::toImage() const
return QImage();
}
-QRectF QSGDefaultLayer::normalizedTextureSubRect() const
+QRectF QSGOpenGLLayer::normalizedTextureSubRect() const
{
return QRectF(m_mirrorHorizontal ? 1 : 0,
m_mirrorVertical ? 0 : 1,
@@ -473,4 +479,4 @@ QRectF QSGDefaultLayer::normalizedTextureSubRect() const
m_mirrorVertical ? 1 : -1);
}
-#include "moc_qsgdefaultlayer_p.cpp"
+#include "moc_qsgopengllayer_p.cpp"
diff --git a/src/quick/scenegraph/qsgdefaultlayer_p.h b/src/quick/scenegraph/qsgopengllayer_p.h
index 06355e0c21..c6246843e2 100644
--- a/src/quick/scenegraph/qsgdefaultlayer_p.h
+++ b/src/quick/scenegraph/qsgopengllayer_p.h
@@ -36,8 +36,8 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-#ifndef QSGDEFAULTLAYER_P_H
-#define QSGDEFAULTLAYER_P_H
+#ifndef QSGOPENGLLAYER_P_H
+#define QSGOPENGLLAYER_P_H
//
// W A R N I N G
@@ -52,6 +52,7 @@
#include <private/qsgadaptationlayer_p.h>
#include <private/qsgcontext_p.h>
+#include <private/qsgtexture_p.h>
#include <qsgsimplerectnode.h>
QT_BEGIN_NAMESPACE
@@ -61,13 +62,15 @@ QT_BEGIN_NAMESPACE
class QOpenGLFramebufferObject;
class QSGDepthStencilBuffer;
class QSGDefaultRenderContext;
+class QSGOpenGLLayerPrivate;
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultLayer : public QSGLayer
+class Q_QUICK_PRIVATE_EXPORT QSGOpenGLLayer : public QSGLayer
{
+ Q_DECLARE_PRIVATE(QSGOpenGLLayer)
Q_OBJECT
public:
- QSGDefaultLayer(QSGRenderContext *context);
- ~QSGDefaultLayer();
+ QSGOpenGLLayer(QSGRenderContext *context);
+ ~QSGOpenGLLayer();
bool updateTexture() override;
@@ -154,6 +157,13 @@ private:
uint m_mirrorVertical : 1;
};
+class QSGOpenGLLayerPrivate : public QSGTexturePrivate
+{
+ Q_DECLARE_PUBLIC(QSGOpenGLLayer)
+public:
+ int comparisonKey() const override;
+};
+
QT_END_NAMESPACE
-#endif // QSGDEFAULTLAYER_P_H
+#endif // QSGOPENGLLAYER_P_H
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index 2e91bafa7c..f609055677 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -40,6 +40,7 @@
#include "qsgrenderloop_p.h"
#include "qsgthreadedrenderloop_p.h"
#include "qsgwindowsrenderloop_p.h"
+#include "qsgrhisupport_p.h"
#include <private/qquickanimatorcontroller_p.h>
#include <QtCore/QCoreApplication>
@@ -50,6 +51,7 @@
#include <QtGui/QOffscreenSurface>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
+#include <QPlatformSurfaceEvent>
#include <QtQml/private/qqmlglobal_p.h>
@@ -59,23 +61,35 @@
#include <QtQuick/private/qsgrenderer_p.h>
#include <private/qquickprofiler_p.h>
+#include <private/qsgrhishadereffectnode_p.h>
+
#if QT_CONFIG(opengl)
-# include <QtGui/QOpenGLContext>
-# include <private/qsgdefaultrendercontext_p.h>
+#include <QtGui/QOpenGLContext>
#if QT_CONFIG(quick_shadereffect)
-# include <private/qquickopenglshadereffectnode_p.h>
+#include <private/qquickopenglshadereffectnode_p.h>
#endif
+#include <private/qsgdefaultrendercontext_p.h>
#endif
#ifdef Q_OS_WIN
-# include <QtCore/qt_windows.h>
+#include <QtCore/qt_windows.h>
#endif
QT_BEGIN_NAMESPACE
extern bool qsg_useConsistentTiming();
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
-#if QT_CONFIG(opengl)
+
+// ### We do not yet support using Qt Quick with QRhi (and Vulkan, D3D
+// or Metal) in -no-opengl builds as of Qt 5.14. This is due to to the
+// widespread direct OpenGL usage all over the place in qsgdefault*
+// and the related classes. To be cleaned up in Qt 6 when the direct
+// GL code path is removed.
+
+#if QT_CONFIG(opengl) /* || QT_CONFIG(vulkan) || defined(Q_OS_WIN) || defined(Q_OS_DARWIN) */
+
+#define ENABLE_DEFAULT_BACKEND
+
/*
expectations for this manager to work:
- one opengl context to render multiple windows
@@ -88,17 +102,13 @@ extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_
DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP);
DEFINE_BOOL_CONFIG_OPTION(qmlForceThreadedRenderer, QML_FORCE_THREADED_RENDERER); // Might trigger graphics driver threading bugs, use at own risk
#endif
+
QSGRenderLoop *QSGRenderLoop::s_instance = nullptr;
QSGRenderLoop::~QSGRenderLoop()
{
}
-QSurface::SurfaceType QSGRenderLoop::windowSurfaceType() const
-{
- return QSurface::OpenGLSurface;
-}
-
void QSGRenderLoop::cleanup()
{
if (!s_instance)
@@ -112,18 +122,36 @@ void QSGRenderLoop::cleanup()
}
delete s_instance;
s_instance = nullptr;
+
+#ifdef ENABLE_DEFAULT_BACKEND
+ QSGRhiSupport::instance()->cleanup();
+ QSGRhiProfileConnection::instance()->cleanup();
+#endif
+}
+
+QSurface::SurfaceType QSGRenderLoop::windowSurfaceType() const
+{
+#ifdef ENABLE_DEFAULT_BACKEND
+ return QSGRhiSupport::instance()->windowSurfaceType();
+#else
+ return QSurface::RasterSurface;
+#endif
}
-/*!
- * Non-threaded render loops immediately run the job if there is a context.
- */
void QSGRenderLoop::postJob(QQuickWindow *window, QRunnable *job)
{
Q_ASSERT(job);
-#if QT_CONFIG(opengl)
+#ifdef ENABLE_DEFAULT_BACKEND
Q_ASSERT(window);
- if (window->openglContext()) {
- window->openglContext()->makeCurrent(window);
+ if (!QSGRhiSupport::instance()->isRhiEnabled()) {
+ if (window->openglContext()) {
+ window->openglContext()->makeCurrent(window);
+ job->run();
+ }
+ } else {
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
+ if (cd->rhi)
+ cd->rhi->makeThreadLocalNativeContextCurrent();
job->run();
}
#else
@@ -132,7 +160,8 @@ void QSGRenderLoop::postJob(QQuickWindow *window, QRunnable *job)
#endif
delete job;
}
-#if QT_CONFIG(opengl)
+
+#ifdef ENABLE_DEFAULT_BACKEND
class QSGGuiThreadRenderLoop : public QSGRenderLoop
{
Q_OBJECT
@@ -160,6 +189,11 @@ public:
QSGContext *sceneGraphContext() const override;
QSGRenderContext *createRenderContext(QSGContext *) const override { return rc; }
+ void releaseSwapchain(QQuickWindow *window);
+ void handleDeviceLoss();
+
+ bool eventFilter(QObject *watched, QEvent *event) override;
+
struct WindowData {
bool updatePending : 1;
bool grabOnly : 1;
@@ -167,44 +201,76 @@ public:
QHash<QQuickWindow *, WindowData> m_windows;
- QOpenGLContext *gl;
+ QOpenGLContext *gl = nullptr;
+ QOffscreenSurface *offscreenSurface = nullptr;
+ QRhi *rhi = nullptr;
QSGContext *sg;
QSGRenderContext *rc;
QImage grabContent;
};
#endif
+
QSGRenderLoop *QSGRenderLoop::instance()
{
if (!s_instance) {
- // For compatibility with 5.3 and earlier's QSG_INFO environment variables
- if (qEnvironmentVariableIsSet("QSG_INFO"))
- const_cast<QLoggingCategory &>(QSG_LOG_INFO()).setEnabled(QtDebugMsg, true);
+ QSGRhiSupport::checkEnvQSgInfo();
s_instance = QSGContext::createWindowManager();
-#if QT_CONFIG(opengl)
+#ifdef ENABLE_DEFAULT_BACKEND
if (!s_instance) {
+ QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
- enum RenderLoopType {
- BasicRenderLoop,
- ThreadedRenderLoop,
- WindowsRenderLoop
- };
-
- RenderLoopType loopType = BasicRenderLoop;
-
-#ifdef Q_OS_WIN
- // With desktop OpenGL (opengl32.dll), use threaded. Otherwise (ANGLE) use windows.
- if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL
- && QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
+ QSGRenderLoopType loopType;
+ if (rhiSupport->isRhiEnabled() && rhiSupport->rhiBackend() != QRhi::OpenGLES2) {
loopType = ThreadedRenderLoop;
- else
- loopType = WindowsRenderLoop;
+ } else {
+ loopType = BasicRenderLoop;
+#ifdef Q_OS_WIN
+ // With desktop OpenGL (opengl32.dll), use threaded. Otherwise (ANGLE) use windows.
+ if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL
+ && QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
+ {
+ loopType = ThreadedRenderLoop;
+ } else {
+ loopType = WindowsRenderLoop;
+ }
#else
- if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
- loopType = ThreadedRenderLoop;
+ if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL))
+ loopType = ThreadedRenderLoop;
#endif
+ }
+
+ if (rhiSupport->isRhiEnabled()) {
+ switch (rhiSupport->rhiBackend()) {
+ case QRhi::Null:
+ loopType = BasicRenderLoop;
+ break;
+
+ case QRhi::D3D11:
+ // The threaded loop's model may not be suitable for DXGI
+ // due to the possibility of having the main thread (with
+ // the Windows message pump) blocked while issuing a
+ // Present on the render thread. However, according to the
+ // docs this can be a problem for fullscreen swapchains
+ // only. So leave threaded enabled by default for now and
+ // revisit later if there are problems.
+ break;
+
+ default:
+ break;
+ }
+
+ // no 'windows' because that's not yet ported to the rhi
+ if (loopType == WindowsRenderLoop)
+ loopType = BasicRenderLoop;
+ }
+
+ // The environment variables can always override. This is good
+ // because in some situations it makes perfect sense to try out a
+ // render loop that is otherwise disabled by default.
+
if (qmlNoThreadedRenderer())
loopType = BasicRenderLoop;
else if (qmlForceThreadedRenderer())
@@ -275,14 +341,15 @@ void QSGRenderLoop::handleContextCreationFailure(QQuickWindow *window,
if (!signalEmitted)
qFatal("%s", qPrintable(untranslatedMessage));
}
-#if QT_CONFIG(opengl)
+
+#ifdef ENABLE_DEFAULT_BACKEND
QSGGuiThreadRenderLoop::QSGGuiThreadRenderLoop()
- : gl(nullptr)
{
if (qsg_useConsistentTiming()) {
QUnifiedTimer::instance(true)->setConsistentTiming(true);
qCDebug(QSG_LOG_INFO, "using fixed animation steps");
}
+
sg = QSGContext::createDefaultContext();
rc = sg->createRenderContext();
}
@@ -318,36 +385,108 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window)
QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
bool current = false;
- QScopedPointer<QOffscreenSurface> offscreenSurface;
- if (gl) {
- QSurface *surface = window;
- // There may be no platform window if the window got closed.
- if (!window->handle()) {
- offscreenSurface.reset(new QOffscreenSurface);
- offscreenSurface->setFormat(gl->format());
- offscreenSurface->create();
- surface = offscreenSurface.data();
+ if (gl || rhi) {
+ if (rhi) {
+ // Direct OpenGL calls in user code need a current context, like
+ // when rendering; ensure this (no-op when not running on GL).
+ // Also works when there is no handle() anymore.
+ rhi->makeThreadLocalNativeContextCurrent();
+ current = true;
+ } else {
+ QSurface *surface = window;
+ // There may be no platform window if the window got closed.
+ if (!window->handle())
+ surface = offscreenSurface;
+ current = gl->makeCurrent(surface);
}
- current = gl->makeCurrent(surface);
+ if (Q_UNLIKELY(!current))
+ qCDebug(QSG_LOG_RENDERLOOP, "cleanup without an OpenGL context");
}
- if (Q_UNLIKELY(!current))
- qCDebug(QSG_LOG_RENDERLOOP, "cleanup without an OpenGL context");
-#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl)
- if (current)
- QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+#if QT_CONFIG(quick_shadereffect)
+ QSGRhiShaderEffectNode::cleanupMaterialTypeCache();
+#if QT_CONFIG(opengl)
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+#endif
#endif
+ if (d->swapchain) {
+ if (window->handle()) {
+ // We get here when exiting via QCoreApplication::quit() instead of
+ // through QWindow::close().
+ releaseSwapchain(window);
+ } else {
+ qWarning("QSGGuiThreadRenderLoop cleanup with QQuickWindow %p swapchain %p still alive, this should not happen.",
+ window, d->swapchain);
+ }
+ }
+
d->cleanupNodesOnShutdown();
if (m_windows.size() == 0) {
rc->invalidate();
+ d->rhi = nullptr;
+ delete rhi;
+ rhi = nullptr;
delete gl;
gl = nullptr;
+ delete offscreenSurface;
+ offscreenSurface = nullptr;
} else if (gl && window == gl->surface() && current) {
- gl->doneCurrent();
+ if (!rhi)
+ gl->doneCurrent();
}
- delete d->animationController;
+ d->animationController.reset();
+}
+
+void QSGGuiThreadRenderLoop::handleDeviceLoss()
+{
+ if (!rhi || !rhi->isDeviceLost())
+ return;
+
+ qWarning("Graphics device lost, cleaning up scenegraph and releasing RHI");
+
+ for (auto it = m_windows.constBegin(), itEnd = m_windows.constEnd(); it != itEnd; ++it)
+ QQuickWindowPrivate::get(it.key())->cleanupNodesOnShutdown();
+
+ rc->invalidate();
+
+ for (auto it = m_windows.constBegin(), itEnd = m_windows.constEnd(); it != itEnd; ++it)
+ releaseSwapchain(it.key());
+
+ delete rhi;
+ rhi = nullptr;
+}
+
+void QSGGuiThreadRenderLoop::releaseSwapchain(QQuickWindow *window)
+{
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ delete wd->rpDescForSwapchain;
+ wd->rpDescForSwapchain = nullptr;
+ delete wd->swapchain;
+ wd->swapchain = nullptr;
+ delete wd->depthStencilForSwapchain;
+ wd->depthStencilForSwapchain = nullptr;
+ wd->hasActiveSwapchain = wd->hasRenderableSwapchain = wd->swapchainJustBecameRenderable = false;
+}
+
+bool QSGGuiThreadRenderLoop::eventFilter(QObject *watched, QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::PlatformSurface:
+ // this is the proper time to tear down the swapchain (while the native window and surface are still around)
+ if (static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
+ QQuickWindow *w = qobject_cast<QQuickWindow *>(watched);
+ if (w) {
+ releaseSwapchain(w);
+ w->removeEventFilter(this);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return QObject::eventFilter(watched, event);
}
void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
@@ -364,8 +503,39 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
return;
bool current = false;
+ QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
+ int rhiSampleCount = 1;
+ const bool enableRhi = rhiSupport->isRhiEnabled();
+
+ if (enableRhi && !rhi) {
+ if (!offscreenSurface)
+ offscreenSurface = rhiSupport->maybeCreateOffscreenSurface(window);
+
+ rhi = rhiSupport->createRhi(window, offscreenSurface);
+
+ if (rhi) {
+ if (rhiSupport->isProfilingRequested())
+ QSGRhiProfileConnection::instance()->initialize(rhi);
+
+ current = true;
+ rhi->makeThreadLocalNativeContextCurrent();
+
+ // The sample count cannot vary between windows as we use the same
+ // rendercontext for all of them. Decide it here and now.
+ rhiSampleCount = rhiSupport->chooseSampleCountForWindowWithRhi(window, rhi);
- if (!gl) {
+ cd->rhi = rhi; // set this early in case something hooked up to rc initialized() accesses it
+
+ QSGDefaultRenderContext::InitParams rcParams;
+ rcParams.rhi = rhi;
+ rcParams.sampleCount = rhiSampleCount;
+ rcParams.initialSurfacePixelSize = window->size() * window->effectiveDevicePixelRatio();
+ rcParams.maybeSurface = window;
+ cd->context->initialize(&rcParams);
+ } else {
+ handleContextCreationFailure(window, false);
+ }
+ } else if (!enableRhi && !gl) {
gl = new QOpenGLContext();
gl->setFormat(window->requestedFormat());
gl->setScreen(window->screen());
@@ -377,15 +547,66 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
gl = nullptr;
handleContextCreationFailure(window, isEs);
} else {
+ if (!offscreenSurface) {
+ offscreenSurface = new QOffscreenSurface;
+ offscreenSurface->setFormat(gl->format());
+ offscreenSurface->create();
+ }
cd->fireOpenGLContextCreated(gl);
current = gl->makeCurrent(window);
}
if (current) {
- auto openglRenderContext = static_cast<QSGDefaultRenderContext *>(cd->context);
- openglRenderContext->initialize(gl);
+ QSGDefaultRenderContext::InitParams rcParams;
+ rcParams.sampleCount = qMax(1, gl->format().samples());
+ rcParams.openGLContext = gl;
+ rcParams.initialSurfacePixelSize = window->size() * window->effectiveDevicePixelRatio();
+ rcParams.maybeSurface = window;
+ cd->context->initialize(&rcParams);
}
} else {
- current = gl->makeCurrent(window);
+ if (rhi) {
+ current = true;
+ // With the rhi making the (OpenGL) context current serves only one
+ // purpose: to enable external OpenGL rendering connected to one of
+ // the QQuickWindow signals (beforeSynchronizing, beforeRendering,
+ // etc.) to function like it did on the direct OpenGL path. For our
+ // own rendering this call would not be necessary.
+ rhi->makeThreadLocalNativeContextCurrent();
+ } else {
+ current = gl->makeCurrent(window);
+ }
+ }
+
+ if (enableRhi && rhi && !cd->swapchain) {
+ // if it's not the first window then the rhi is not yet stored to
+ // QQuickWindowPrivate, do it now
+ cd->rhi = rhi;
+
+ QRhiSwapChain::Flags flags = QRhiSwapChain::UsedAsTransferSource; // may be used in a grab
+
+ // QQ is always premul alpha. Decide based on alphaBufferSize in
+ // requestedFormat(). (the platform plugin can override format() but
+ // what matters here is what the application wanted, hence using the
+ // requested one)
+ const bool alpha = window->requestedFormat().alphaBufferSize() > 0;
+ if (alpha)
+ flags |= QRhiSwapChain::SurfaceHasPreMulAlpha;
+
+ cd->swapchain = rhi->newSwapChain();
+ cd->depthStencilForSwapchain = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
+ QSize(),
+ rhiSampleCount,
+ QRhiRenderBuffer::UsedWithSwapChainOnly);
+ cd->swapchain->setWindow(window);
+ cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain);
+ qCDebug(QSG_LOG_INFO, "MSAA sample count for the swapchain is %d. Alpha channel requested = %s",
+ rhiSampleCount, alpha ? "yes" : "no");
+ cd->swapchain->setSampleCount(rhiSampleCount);
+ cd->swapchain->setFlags(flags);
+ cd->rpDescForSwapchain = cd->swapchain->newCompatibleRenderPassDescriptor();
+ cd->swapchain->setRenderPassDescriptor(cd->rpDescForSwapchain);
+
+ window->installEventFilter(this);
}
bool lastDirtyWindow = true;
@@ -398,6 +619,24 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
i++;
}
+ // Check for context loss.
+ if (!current && !rhi && !gl->isValid()) {
+ for (auto it = m_windows.constBegin() ; it != m_windows.constEnd(); it++) {
+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(it.key());
+ windowPrivate->cleanupNodesOnShutdown();
+ }
+ rc->invalidate();
+ current = gl->create() && gl->makeCurrent(window);
+ if (current) {
+ QSGDefaultRenderContext::InitParams rcParams;
+ rcParams.sampleCount = qMax(1, gl->format().samples());
+ rcParams.openGLContext = gl;
+ rcParams.initialSurfacePixelSize = window->size() * window->effectiveDevicePixelRatio();
+ rcParams.maybeSurface = window;
+ rc->initialize(&rcParams);
+ }
+ }
+
if (!current)
return;
@@ -407,6 +646,17 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
if (!m_windows.contains(window))
return;
}
+
+ QSize effectiveOutputSize; // always prefer what the surface tells us, not the QWindow
+ if (cd->swapchain) {
+ effectiveOutputSize = cd->swapchain->surfacePixelSize();
+ // An update request could still be delivered right before we get an
+ // unexpose. With Vulkan on Windows for example attempting to render
+ // leads to failures at this stage since the surface size is already 0.
+ if (effectiveOutputSize.isEmpty())
+ return;
+ }
+
QElapsedTimer renderTimer;
qint64 renderTime = 0, syncTime = 0, polishTime = 0;
bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
@@ -424,6 +674,51 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
emit window->afterAnimating();
+ // Begin the frame before syncing -> sync is where we may invoke
+ // updatePaintNode() on the items and they may want to do resource updates.
+ // Also relevant for applications that connect to the before/afterSynchronizing
+ // signals and want to do graphics stuff already there.
+ if (cd->swapchain) {
+ Q_ASSERT(!effectiveOutputSize.isEmpty());
+ const QSize previousOutputSize = cd->swapchain->currentPixelSize();
+ if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable) {
+ if (cd->swapchainJustBecameRenderable)
+ qCDebug(QSG_LOG_RENDERLOOP, "just became exposed");
+
+ cd->hasActiveSwapchain = cd->swapchain->buildOrResize();
+ if (!cd->hasActiveSwapchain && rhi->isDeviceLost()) {
+ handleDeviceLoss();
+ return;
+ }
+
+ cd->swapchainJustBecameRenderable = false;
+ cd->hasRenderableSwapchain = cd->hasActiveSwapchain;
+
+ if (cd->hasActiveSwapchain) {
+ // surface size atomicity: now that buildOrResize() succeeded,
+ // query the size that was used in there by the swapchain, and
+ // that is the size we will use while preparing the next frame.
+ effectiveOutputSize = cd->swapchain->currentPixelSize();
+ qCDebug(QSG_LOG_RENDERLOOP) << "rhi swapchain size" << effectiveOutputSize;
+ } else {
+ qWarning("Failed to build or resize swapchain");
+ }
+ }
+
+ Q_ASSERT(rhi == cd->rhi);
+ // ### the flag should only be set when the app requests it, but there's no way to do that right now
+ QRhi::BeginFrameFlags frameFlags = QRhi::ExternalContentsInPass;
+ QRhi::FrameOpResult frameResult = rhi->beginFrame(cd->swapchain, frameFlags);
+ if (frameResult != QRhi::FrameOpSuccess) {
+ if (frameResult == QRhi::FrameOpDeviceLost)
+ handleDeviceLoss();
+ else if (frameResult == QRhi::FrameOpError)
+ qWarning("Failed to start frame");
+ // out of date is not worth warning about - it may happen even during resizing on some platforms
+ return;
+ }
+ }
+
cd->syncSceneGraph();
if (lastDirtyWindow)
rc->endSync();
@@ -433,7 +728,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopSync);
- cd->renderSceneGraph(window->size());
+ cd->renderSceneGraph(window->size(), effectiveOutputSize);
if (profileFrames)
renderTime = renderTimer.nsecsElapsed();
@@ -441,17 +736,33 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
QQuickProfiler::SceneGraphRenderLoopRender);
if (data.grabOnly) {
- bool alpha = window->format().alphaBufferSize() > 0 && window->color().alpha() != 255;
- grabContent = qt_gl_read_framebuffer(window->size() * window->effectiveDevicePixelRatio(), alpha, alpha);
+ const bool alpha = window->format().alphaBufferSize() > 0 && window->color().alpha() != 255;
+ if (cd->swapchain)
+ grabContent = rhiSupport->grabAndBlockInCurrentFrame(rhi, cd->swapchain);
+ else
+ grabContent = qt_gl_read_framebuffer(window->size() * window->effectiveDevicePixelRatio(), alpha, alpha);
grabContent.setDevicePixelRatio(window->effectiveDevicePixelRatio());
data.grabOnly = false;
}
- if (alsoSwap && window->isVisible()) {
+ const bool needsPresent = alsoSwap && window->isVisible();
+ if (cd->swapchain) {
+ QRhi::EndFrameFlags flags = 0;
+ if (!needsPresent)
+ flags |= QRhi::SkipPresent;
+ QRhi::FrameOpResult frameResult = rhi->endFrame(cd->swapchain, flags);
+ if (frameResult != QRhi::FrameOpSuccess) {
+ if (frameResult == QRhi::FrameOpDeviceLost)
+ handleDeviceLoss();
+ else if (frameResult == QRhi::FrameOpError)
+ qWarning("Failed to end frame");
+ }
+ } else if (needsPresent) {
if (!cd->customRenderStage || !cd->customRenderStage->swap())
gl->swapBuffers(window);
- cd->fireFrameSwapped();
}
+ if (needsPresent)
+ cd->fireFrameSwapped();
qint64 swapTime = 0;
if (profileFrames)
@@ -472,6 +783,8 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
lastFrameTime = QTime::currentTime();
}
+ QSGRhiProfileConnection::instance()->send(rhi);
+
// Might have been set during syncSceneGraph()
if (data.updatePending)
maybeUpdate(window);
@@ -479,7 +792,26 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
void QSGGuiThreadRenderLoop::exposureChanged(QQuickWindow *window)
{
- if (window->isExposed()) {
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+
+ // This is tricker than used to be. We want to detect having an empty
+ // surface size (which may be the case even when window->size() is
+ // non-empty, on some platforms with some graphics APIs!) as well as the
+ // case when the window just became "newly exposed" (e.g. after a
+ // minimize-restore on Windows, or when switching between fully obscured -
+ // not fully obscured on macOS)
+
+ if (!window->isExposed() || (wd->hasActiveSwapchain && wd->swapchain->surfacePixelSize().isEmpty()))
+ wd->hasRenderableSwapchain = false;
+
+ if (window->isExposed() && !wd->hasRenderableSwapchain && wd->hasActiveSwapchain
+ && !wd->swapchain->surfacePixelSize().isEmpty())
+ {
+ wd->hasRenderableSwapchain = true;
+ wd->swapchainJustBecameRenderable = true;
+ }
+
+ if (window->isExposed() && (!rhi || !wd->hasActiveSwapchain || wd->hasRenderableSwapchain)) {
m_windows[window].updatePending = true;
renderWindow(window);
}
@@ -527,7 +859,7 @@ void QSGGuiThreadRenderLoop::handleUpdateRequest(QQuickWindow *window)
renderWindow(window);
}
-#endif
+#endif // ENABLE_DEFAULT_BACKEND
#include "qsgrenderloop.moc"
#include "moc_qsgrenderloop_p.cpp"
diff --git a/src/quick/scenegraph/qsgrenderloop_p.h b/src/quick/scenegraph/qsgrenderloop_p.h
index 7bf40ecac4..02d0b84de1 100644
--- a/src/quick/scenegraph/qsgrenderloop_p.h
+++ b/src/quick/scenegraph/qsgrenderloop_p.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QSGRenderLoop_P_H
-#define QSGRenderLoop_P_H
+#ifndef QSGRENDERLOOP_P_H
+#define QSGRENDERLOOP_P_H
//
// W A R N I N G
@@ -124,6 +124,13 @@ private:
QSet<QQuickWindow *> m_windows;
};
+enum QSGRenderLoopType
+{
+ BasicRenderLoop,
+ ThreadedRenderLoop,
+ WindowsRenderLoop
+};
+
QT_END_NAMESPACE
-#endif // QSGRenderLoop_P_H
+#endif // QSGRENDERLOOP_P_H
diff --git a/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
new file mode 100644
index 0000000000..53b6fe117f
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
@@ -0,0 +1,570 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "qsgrhidistancefieldglyphcache_p.h"
+#include "qsgcontext_p.h"
+#include <QtGui/private/qdistancefield_p.h>
+#include <QtCore/qelapsedtimer.h>
+#include <QtQml/private/qqmlglobal_p.h>
+#include <qmath.h>
+#include <qendian.h>
+
+QT_BEGIN_NAMESPACE
+
+DEFINE_BOOL_CONFIG_OPTION(qmlUseGlyphCacheWorkaround, QML_USE_GLYPHCACHE_WORKAROUND)
+DEFINE_BOOL_CONFIG_OPTION(qsgPreferFullSizeGlyphCacheTextures, QSG_PREFER_FULLSIZE_GLYPHCACHE_TEXTURES)
+
+#if !defined(QSG_RHI_DISTANCEFIELD_GLYPH_CACHE_PADDING)
+# define QSG_RHI_DISTANCEFIELD_GLYPH_CACHE_PADDING 2
+#endif
+
+QSGRhiDistanceFieldGlyphCache::QSGRhiDistanceFieldGlyphCache(QRhi *rhi, const QRawFont &font)
+ : QSGDistanceFieldGlyphCache(font)
+ , m_rhi(rhi)
+{
+ // Load a pregenerated cache if the font contains one
+ loadPregeneratedCache(font);
+}
+
+QSGRhiDistanceFieldGlyphCache::~QSGRhiDistanceFieldGlyphCache()
+{
+ for (int i = 0; i < m_textures.count(); ++i)
+ delete m_textures[i].texture;
+
+ delete m_areaAllocator;
+
+ // should be empty, but just in case
+ qDeleteAll(m_pendingDispose);
+}
+
+void QSGRhiDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
+{
+ QList<GlyphPosition> glyphPositions;
+ QVector<glyph_t> glyphsToRender;
+
+ if (m_areaAllocator == nullptr)
+ m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), m_maxTextureCount * maxTextureSize()));
+
+ for (QSet<glyph_t>::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) {
+ glyph_t glyphIndex = *it;
+
+ int padding = QSG_RHI_DISTANCEFIELD_GLYPH_CACHE_PADDING;
+ QRectF boundingRect = glyphData(glyphIndex).boundingRect;
+ int glyphWidth = qCeil(boundingRect.width()) + distanceFieldRadius() * 2;
+ int glyphHeight = qCeil(boundingRect.height()) + distanceFieldRadius() * 2;
+ QSize glyphSize(glyphWidth + padding * 2, glyphHeight + padding * 2);
+ QRect alloc = m_areaAllocator->allocate(glyphSize);
+
+ if (alloc.isNull()) {
+ // Unallocate unused glyphs until we can allocated the new glyph
+ while (alloc.isNull() && !m_unusedGlyphs.isEmpty()) {
+ glyph_t unusedGlyph = *m_unusedGlyphs.constBegin();
+
+ TexCoord unusedCoord = glyphTexCoord(unusedGlyph);
+ QRectF unusedGlyphBoundingRect = glyphData(unusedGlyph).boundingRect;
+ int unusedGlyphWidth = qCeil(unusedGlyphBoundingRect.width()) + distanceFieldRadius() * 2;
+ int unusedGlyphHeight = qCeil(unusedGlyphBoundingRect.height()) + distanceFieldRadius() * 2;
+ m_areaAllocator->deallocate(QRect(unusedCoord.x - padding,
+ unusedCoord.y - padding,
+ padding * 2 + unusedGlyphWidth,
+ padding * 2 + unusedGlyphHeight));
+
+ m_unusedGlyphs.remove(unusedGlyph);
+ m_glyphsTexture.remove(unusedGlyph);
+ removeGlyph(unusedGlyph);
+
+ alloc = m_areaAllocator->allocate(glyphSize);
+ }
+
+ // Not enough space left for this glyph... skip to the next one
+ if (alloc.isNull())
+ continue;
+ }
+
+ TextureInfo *tex = textureInfo(alloc.y() / maxTextureSize());
+ alloc = QRect(alloc.x(), alloc.y() % maxTextureSize(), alloc.width(), alloc.height());
+
+ tex->allocatedArea |= alloc;
+ Q_ASSERT(tex->padding == padding || tex->padding < 0);
+ tex->padding = padding;
+
+ GlyphPosition p;
+ p.glyph = glyphIndex;
+ p.position = alloc.topLeft() + QPoint(padding, padding);
+
+ glyphPositions.append(p);
+ glyphsToRender.append(glyphIndex);
+ m_glyphsTexture.insert(glyphIndex, tex);
+ }
+
+ setGlyphsPosition(glyphPositions);
+ markGlyphsToRender(glyphsToRender);
+}
+
+void QSGRhiDistanceFieldGlyphCache::storeGlyphs(const QList<QDistanceField> &glyphs)
+{
+ typedef QHash<TextureInfo *, QVector<glyph_t> > GlyphTextureHash;
+ typedef GlyphTextureHash::const_iterator GlyphTextureHashConstIt;
+
+ GlyphTextureHash glyphTextures;
+
+ QVarLengthArray<QRhiTextureUploadEntry, 32> uploads;
+ for (int i = 0; i < glyphs.size(); ++i) {
+ QDistanceField glyph = glyphs.at(i);
+ glyph_t glyphIndex = glyph.glyph();
+ TexCoord c = glyphTexCoord(glyphIndex);
+ TextureInfo *texInfo = m_glyphsTexture.value(glyphIndex);
+
+ resizeTexture(texInfo, texInfo->allocatedArea.width(), texInfo->allocatedArea.height());
+
+ glyphTextures[texInfo].append(glyphIndex);
+
+ int padding = texInfo->padding;
+ int expectedWidth = qCeil(c.width + c.xMargin * 2);
+ glyph = glyph.copy(-padding, -padding,
+ expectedWidth + padding * 2, glyph.height() + padding * 2);
+
+ if (useTextureResizeWorkaround()) {
+ uchar *inBits = glyph.scanLine(0);
+ uchar *outBits = texInfo->image.scanLine(int(c.y) - padding) + int(c.x) - padding;
+ for (int y = 0; y < glyph.height(); ++y) {
+ memcpy(outBits, inBits, glyph.width());
+ inBits += glyph.width();
+ outBits += texInfo->image.width();
+ }
+ }
+
+ QRhiTextureSubresourceUploadDescription subresDesc(glyph.constBits(), glyph.width() * glyph.height());
+ subresDesc.setSourceSize(QSize(glyph.width(), glyph.height()));
+ subresDesc.setDestinationTopLeft(QPoint(c.x - padding, c.y - padding));
+ texInfo->uploads.append(QRhiTextureUploadEntry(0, 0, subresDesc));
+ }
+
+ if (!m_resourceUpdates)
+ m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
+
+ for (int i = 0; i < glyphs.size(); ++i) {
+ TextureInfo *texInfo = m_glyphsTexture.value(glyphs.at(i).glyph());
+ if (!texInfo->uploads.isEmpty()) {
+ QRhiTextureUploadDescription desc;
+ desc.setEntries(texInfo->uploads.cbegin(), texInfo->uploads.cend());
+ m_resourceUpdates->uploadTexture(texInfo->texture, desc);
+ texInfo->uploads.clear();
+ }
+ }
+
+ for (GlyphTextureHashConstIt i = glyphTextures.constBegin(), cend = glyphTextures.constEnd(); i != cend; ++i) {
+ Texture t;
+ t.texture = i.key()->texture;
+ t.size = i.key()->size;
+ t.rhiBased = true;
+ setGlyphsTexture(i.value(), t);
+ }
+}
+
+void QSGRhiDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
+{
+ m_unusedGlyphs -= glyphs;
+}
+
+void QSGRhiDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
+{
+ m_unusedGlyphs += glyphs;
+}
+
+void QSGRhiDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
+ int width,
+ int height)
+{
+ QByteArray zeroBuf(width * height, 0);
+ createTexture(texInfo, width, height, zeroBuf.constData());
+}
+
+void QSGRhiDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
+ int width,
+ int height,
+ const void *pixels)
+{
+ if (useTextureResizeWorkaround() && texInfo->image.isNull()) {
+ texInfo->image = QDistanceField(width, height);
+ memcpy(texInfo->image.bits(), pixels, width * height);
+ }
+
+ texInfo->texture = m_rhi->newTexture(QRhiTexture::RED_OR_ALPHA8, QSize(width, height), 1, QRhiTexture::UsedAsTransferSource);
+ if (texInfo->texture->build()) {
+ if (!m_resourceUpdates)
+ m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
+
+ QRhiTextureSubresourceUploadDescription subresDesc(pixels, width * height);
+ subresDesc.setSourceSize(QSize(width, height));
+ m_resourceUpdates->uploadTexture(texInfo->texture, QRhiTextureUploadEntry(0, 0, subresDesc));
+ } else {
+ qWarning("Failed to create distance field glyph cache");
+ }
+
+ texInfo->size = QSize(width, height);
+}
+
+void QSGRhiDistanceFieldGlyphCache::resizeTexture(TextureInfo *texInfo, int width, int height)
+{
+ int oldWidth = texInfo->size.width();
+ int oldHeight = texInfo->size.height();
+ if (width == oldWidth && height == oldHeight)
+ return;
+
+ QRhiTexture *oldTexture = texInfo->texture;
+ createTexture(texInfo, width, height);
+
+ if (!oldTexture)
+ return;
+
+ updateRhiTexture(oldTexture, texInfo->texture, texInfo->size);
+
+ if (!m_resourceUpdates)
+ m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
+
+ if (useTextureResizeWorkaround()) {
+ QRhiTextureSubresourceUploadDescription subresDesc(texInfo->image.constBits(),
+ oldWidth * oldHeight);
+ subresDesc.setSourceSize(QSize(oldWidth, oldHeight));
+ m_resourceUpdates->uploadTexture(texInfo->texture, QRhiTextureUploadEntry(0, 0, subresDesc));
+ texInfo->image = texInfo->image.copy(0, 0, width, height);
+ } else {
+ m_resourceUpdates->copyTexture(texInfo->texture, oldTexture);
+ }
+
+ m_pendingDispose.insert(oldTexture);
+}
+
+bool QSGRhiDistanceFieldGlyphCache::useTextureResizeWorkaround() const
+{
+ static bool set = false;
+ static bool useWorkaround = false;
+ if (!set) {
+ useWorkaround = m_rhi->backend() == QRhi::OpenGLES2 || qmlUseGlyphCacheWorkaround();
+ set = true;
+ }
+ return useWorkaround;
+}
+
+bool QSGRhiDistanceFieldGlyphCache::createFullSizeTextures() const
+{
+ return qsgPreferFullSizeGlyphCacheTextures() && glyphCount() > QT_DISTANCEFIELD_HIGHGLYPHCOUNT();
+}
+
+int QSGRhiDistanceFieldGlyphCache::maxTextureSize() const
+{
+ if (!m_maxTextureSize)
+ m_maxTextureSize = m_rhi->resourceLimit(QRhi::TextureSizeMax);
+ return m_maxTextureSize;
+}
+
+namespace {
+ struct Qtdf {
+ // We need these structs to be tightly packed, but some compilers we use do not
+ // support #pragma pack(1), so we need to hardcode the offsets/sizes in the
+ // file format
+ enum TableSize {
+ HeaderSize = 14,
+ GlyphRecordSize = 46,
+ TextureRecordSize = 17
+ };
+
+ enum Offset {
+ // Header
+ majorVersion = 0,
+ minorVersion = 1,
+ pixelSize = 2,
+ textureSize = 4,
+ flags = 8,
+ headerPadding = 9,
+ numGlyphs = 10,
+
+ // Glyph record
+ glyphIndex = 0,
+ textureOffsetX = 4,
+ textureOffsetY = 8,
+ textureWidth = 12,
+ textureHeight = 16,
+ xMargin = 20,
+ yMargin = 24,
+ boundingRectX = 28,
+ boundingRectY = 32,
+ boundingRectWidth = 36,
+ boundingRectHeight = 40,
+ textureIndex = 44,
+
+ // Texture record
+ allocatedX = 0,
+ allocatedY = 4,
+ allocatedWidth = 8,
+ allocatedHeight = 12,
+ texturePadding = 16
+
+ };
+
+ template <typename T>
+ static inline T fetch(const char *data, Offset offset)
+ {
+ return qFromBigEndian<T>(data + int(offset));
+ }
+ };
+}
+
+bool QSGRhiDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
+{
+ // The pregenerated data must be loaded first, otherwise the area allocator
+ // will be wrong
+ if (m_areaAllocator != nullptr) {
+ qWarning("Font cache must be loaded before cache is used");
+ return false;
+ }
+
+ static QElapsedTimer timer;
+
+ bool profile = QSG_LOG_TIME_GLYPH().isDebugEnabled();
+ if (profile)
+ timer.start();
+
+ QByteArray qtdfTable = font.fontTable("qtdf");
+ if (qtdfTable.isEmpty())
+ return false;
+
+ typedef QHash<TextureInfo *, QVector<glyph_t> > GlyphTextureHash;
+
+ GlyphTextureHash glyphTextures;
+
+ if (uint(qtdfTable.size()) < Qtdf::HeaderSize) {
+ qWarning("Invalid qtdf table in font '%s'",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ const char *qtdfTableStart = qtdfTable.constData();
+ const char *qtdfTableEnd = qtdfTableStart + qtdfTable.size();
+
+ int padding = 0;
+ int textureCount = 0;
+ {
+ quint8 majorVersion = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::majorVersion);
+ quint8 minorVersion = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::minorVersion);
+ if (majorVersion != 5 || minorVersion != 12) {
+ qWarning("Invalid version of qtdf table %d.%d in font '%s'",
+ majorVersion,
+ minorVersion,
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ qreal pixelSize = qreal(Qtdf::fetch<quint16>(qtdfTableStart, Qtdf::pixelSize));
+ m_maxTextureSize = Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::textureSize);
+ m_doubleGlyphResolution = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::flags) == 1;
+ padding = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::headerPadding);
+
+ if (pixelSize <= 0.0) {
+ qWarning("Invalid pixel size in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ if (m_maxTextureSize <= 0) {
+ qWarning("Invalid texture size in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ int systemMaxTextureSize = m_rhi->resourceLimit(QRhi::TextureSizeMax);
+
+ if (m_maxTextureSize > systemMaxTextureSize) {
+ qWarning("System maximum texture size is %d. This is lower than the value in '%s', which is %d",
+ systemMaxTextureSize,
+ qPrintable(font.familyName()),
+ m_maxTextureSize);
+ }
+
+ if (padding != QSG_RHI_DISTANCEFIELD_GLYPH_CACHE_PADDING) {
+ qWarning("Padding mismatch in '%s'. Font requires %d, but Qt is compiled with %d.",
+ qPrintable(font.familyName()),
+ padding,
+ QSG_RHI_DISTANCEFIELD_GLYPH_CACHE_PADDING);
+ }
+
+ m_referenceFont.setPixelSize(pixelSize);
+
+ quint32 glyphCount = Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::numGlyphs);
+ m_unusedGlyphs.reserve(glyphCount);
+
+ const char *allocatorData = qtdfTableStart + Qtdf::HeaderSize;
+ {
+ m_areaAllocator = new QSGAreaAllocator(QSize(0, 0));
+ allocatorData = m_areaAllocator->deserialize(allocatorData, qtdfTableEnd - allocatorData);
+ if (allocatorData == nullptr)
+ return false;
+ }
+
+ if (m_areaAllocator->size().height() % m_maxTextureSize != 0) {
+ qWarning("Area allocator size mismatch in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ textureCount = m_areaAllocator->size().height() / m_maxTextureSize;
+ m_maxTextureCount = qMax(m_maxTextureCount, textureCount);
+
+ const char *textureRecord = allocatorData;
+ for (int i = 0; i < textureCount; ++i, textureRecord += Qtdf::TextureRecordSize) {
+ if (textureRecord + Qtdf::TextureRecordSize > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ TextureInfo *tex = textureInfo(i);
+ tex->allocatedArea.setX(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedX));
+ tex->allocatedArea.setY(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedY));
+ tex->allocatedArea.setWidth(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedWidth));
+ tex->allocatedArea.setHeight(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedHeight));
+ tex->padding = Qtdf::fetch<quint8>(textureRecord, Qtdf::texturePadding);
+ }
+
+ const char *glyphRecord = textureRecord;
+ for (quint32 i = 0; i < glyphCount; ++i, glyphRecord += Qtdf::GlyphRecordSize) {
+ if (glyphRecord + Qtdf::GlyphRecordSize > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ glyph_t glyph = Qtdf::fetch<quint32>(glyphRecord, Qtdf::glyphIndex);
+ m_unusedGlyphs.insert(glyph);
+
+ GlyphData &glyphData = emptyData(glyph);
+
+#define FROM_FIXED_POINT(value) \
+(((qreal)value)/(qreal)65536)
+
+ glyphData.texCoord.x = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureOffsetX));
+ glyphData.texCoord.y = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureOffsetY));
+ glyphData.texCoord.width = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureWidth));
+ glyphData.texCoord.height = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureHeight));
+ glyphData.texCoord.xMargin = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::xMargin));
+ glyphData.texCoord.yMargin = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::yMargin));
+ glyphData.boundingRect.setX(FROM_FIXED_POINT(Qtdf::fetch<qint32>(glyphRecord, Qtdf::boundingRectX)));
+ glyphData.boundingRect.setY(FROM_FIXED_POINT(Qtdf::fetch<qint32>(glyphRecord, Qtdf::boundingRectY)));
+ glyphData.boundingRect.setWidth(FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::boundingRectWidth)));
+ glyphData.boundingRect.setHeight(FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::boundingRectHeight)));
+
+#undef FROM_FIXED_POINT
+
+ int textureIndex = Qtdf::fetch<quint16>(glyphRecord, Qtdf::textureIndex);
+ if (textureIndex < 0 || textureIndex >= textureCount) {
+ qWarning("Invalid texture index %d (texture count == %d) in '%s'",
+ textureIndex,
+ textureCount,
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+
+ TextureInfo *texInfo = textureInfo(textureIndex);
+ m_glyphsTexture.insert(glyph, texInfo);
+
+ glyphTextures[texInfo].append(glyph);
+ }
+
+ const uchar *textureData = reinterpret_cast<const uchar *>(glyphRecord);
+ for (int i = 0; i < textureCount; ++i) {
+
+ TextureInfo *texInfo = textureInfo(i);
+
+ int width = texInfo->allocatedArea.width();
+ int height = texInfo->allocatedArea.height();
+ qint64 size = width * height;
+ if (reinterpret_cast<const char *>(textureData + size) > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ createTexture(texInfo, width, height, textureData);
+
+ QVector<glyph_t> glyphs = glyphTextures.value(texInfo);
+
+ Texture t;
+ t.texture = texInfo->texture;
+ t.size = texInfo->size;
+ t.rhiBased = true;
+
+ setGlyphsTexture(glyphs, t);
+
+ textureData += size;
+ }
+ }
+
+ if (profile) {
+ quint64 now = timer.elapsed();
+ qCDebug(QSG_LOG_TIME_GLYPH,
+ "distancefield: %d pre-generated glyphs loaded in %dms",
+ m_unusedGlyphs.size(),
+ (int) now);
+ }
+
+ return true;
+}
+
+void QSGRhiDistanceFieldGlyphCache::commitResourceUpdates(QRhiResourceUpdateBatch *mergeInto)
+{
+ if (m_resourceUpdates) {
+ mergeInto->merge(m_resourceUpdates);
+ m_resourceUpdates->release();
+ m_resourceUpdates = nullptr;
+ }
+
+ // now let's assume the resource updates will be committed in this frame
+ for (QRhiTexture *t : m_pendingDispose)
+ t->releaseAndDestroyLater(); // will be releaseAndDestroyed after the frame is submitted -> safe
+
+ m_pendingDispose.clear();
+}
+
+bool QSGRhiDistanceFieldGlyphCache::eightBitFormatIsAlphaSwizzled() const
+{
+ // return true when the shaders for 8-bit formats need .a instead of .r
+ // when sampling the texture
+ return !m_rhi->isFeatureSupported(QRhi::RedOrAlpha8IsRed);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgrhidistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgrhidistancefieldglyphcache_p.h
new file mode 100644
index 0000000000..d43b0aa5d4
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhidistancefieldglyphcache_p.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 QSGRHIDISTANCEFIELDGLYPHCACHE_H
+#define QSGRHIDISTANCEFIELDGLYPHCACHE_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 "qsgadaptationlayer_p.h"
+#include <private/qsgareaallocator_p.h>
+#include <QtGui/private/qrhi_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QSGRhiDistanceFieldGlyphCache : public QSGDistanceFieldGlyphCache
+{
+public:
+ QSGRhiDistanceFieldGlyphCache(QRhi *rhi, const QRawFont &font);
+ virtual ~QSGRhiDistanceFieldGlyphCache();
+
+ void requestGlyphs(const QSet<glyph_t> &glyphs) override;
+ void storeGlyphs(const QList<QDistanceField> &glyphs) override;
+ void referenceGlyphs(const QSet<glyph_t> &glyphs) override;
+ void releaseGlyphs(const QSet<glyph_t> &glyphs) override;
+
+ bool useTextureResizeWorkaround() const;
+ bool createFullSizeTextures() const;
+ int maxTextureSize() const;
+
+ void setMaxTextureCount(int max) { m_maxTextureCount = max; }
+ int maxTextureCount() const { return m_maxTextureCount; }
+
+ void commitResourceUpdates(QRhiResourceUpdateBatch *mergeInto);
+
+ bool eightBitFormatIsAlphaSwizzled() const override;
+
+private:
+ bool loadPregeneratedCache(const QRawFont &font);
+
+ struct TextureInfo {
+ QRhiTexture *texture;
+ QSize size;
+ QRect allocatedArea;
+ QDistanceField image;
+ int padding = -1;
+ QVarLengthArray<QRhiTextureUploadEntry, 16> uploads;
+
+ TextureInfo(const QRect &preallocRect = QRect()) : texture(nullptr), allocatedArea(preallocRect) { }
+ };
+
+ void createTexture(TextureInfo *texInfo, int width, int height, const void *pixels);
+ void createTexture(TextureInfo *texInfo, int width, int height);
+ void resizeTexture(TextureInfo *texInfo, int width, int height);
+
+ TextureInfo *textureInfo(int index)
+ {
+ for (int i = m_textures.count(); i <= index; ++i) {
+ if (createFullSizeTextures())
+ m_textures.append(QRect(0, 0, maxTextureSize(), maxTextureSize()));
+ else
+ m_textures.append(TextureInfo());
+ }
+
+ return &m_textures[index];
+ }
+
+ QRhi *m_rhi;
+ mutable int m_maxTextureSize = 0;
+ int m_maxTextureCount = 3;
+ QSGAreaAllocator *m_areaAllocator = nullptr;
+ QRhiResourceUpdateBatch *m_resourceUpdates = nullptr;
+ QList<TextureInfo> m_textures;
+ QHash<glyph_t, TextureInfo *> m_glyphsTexture;
+ QSet<glyph_t> m_unusedGlyphs;
+ QSet<QRhiTexture *> m_pendingDispose;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGRHIDISTANCEFIELDGLYPHCACHE_H
diff --git a/src/quick/scenegraph/qsgrhilayer.cpp b/src/quick/scenegraph/qsgrhilayer.cpp
new file mode 100644
index 0000000000..757410eded
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhilayer.cpp
@@ -0,0 +1,474 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "qsgrhilayer_p.h"
+
+#include <private/qqmlglobal_p.h>
+#include <private/qsgrenderer_p.h>
+#include <private/qsgdefaultrendercontext_p.h>
+
+QSGRhiLayer::QSGRhiLayer(QSGRenderContext *context)
+ : QSGLayer(*(new QSGRhiLayerPrivate))
+ , m_mipmap(false)
+ , m_live(true)
+ , m_recursive(false)
+ , m_dirtyTexture(true)
+ , m_multisampling(false)
+ , m_grab(false)
+ , m_mirrorHorizontal(false)
+ , m_mirrorVertical(true)
+{
+ m_context = static_cast<QSGDefaultRenderContext *>(context);
+ m_rhi = m_context->rhi();
+ Q_ASSERT(m_rhi);
+}
+
+QSGRhiLayer::~QSGRhiLayer()
+{
+ invalidated();
+}
+
+void QSGRhiLayer::invalidated()
+{
+ releaseResources();
+
+ delete m_renderer;
+ m_renderer = nullptr;
+}
+
+int QSGRhiLayerPrivate::comparisonKey() const
+{
+ Q_Q(const QSGRhiLayer);
+ return int(qintptr(q->m_texture));
+}
+
+bool QSGRhiLayer::hasAlphaChannel() const
+{
+ return true;
+}
+
+bool QSGRhiLayer::hasMipmaps() const
+{
+ return m_mipmap;
+}
+
+int QSGRhiLayer::textureId() const
+{
+ Q_ASSERT_X(false, "QSGRhiLayer::textureId()", "Not implemented for RHI");
+ return 0;
+}
+
+void QSGRhiLayer::bind()
+{
+ Q_ASSERT_X(false, "QSGRhiLayer::bind()", "Not implemented for RHI");
+}
+
+QRhiTexture *QSGRhiLayerPrivate::rhiTexture() const
+{
+ Q_Q(const QSGRhiLayer);
+ return q->m_texture;
+}
+
+void QSGRhiLayerPrivate::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_UNUSED(rhi);
+ Q_UNUSED(resourceUpdates);
+}
+
+bool QSGRhiLayer::updateTexture()
+{
+ // called during the preprocess phase, outside of frame rendering -> good
+
+ bool doGrab = (m_live || m_grab) && m_dirtyTexture;
+ if (doGrab)
+ grab();
+
+ if (m_grab)
+ emit scheduledUpdateCompleted();
+
+ m_grab = false;
+ return doGrab;
+}
+
+void QSGRhiLayer::setHasMipmaps(bool mipmap)
+{
+ if (mipmap == m_mipmap)
+ return;
+
+ m_mipmap = mipmap;
+ if (m_mipmap && m_texture)
+ markDirtyTexture();
+}
+
+
+void QSGRhiLayer::setItem(QSGNode *item)
+{
+ if (item == m_item)
+ return;
+
+ m_item = item;
+
+ if (m_live && !m_item)
+ releaseResources();
+
+ markDirtyTexture();
+}
+
+void QSGRhiLayer::setRect(const QRectF &rect)
+{
+ if (rect == m_rect)
+ return;
+
+ m_rect = rect;
+ markDirtyTexture();
+}
+
+void QSGRhiLayer::setSize(const QSize &size)
+{
+ if (size == m_size)
+ return;
+
+ m_size = size;
+
+ if (m_live && m_size.isNull())
+ releaseResources();
+
+ markDirtyTexture();
+}
+
+void QSGRhiLayer::setFormat(uint format)
+{
+ Q_UNUSED(format);
+}
+
+void QSGRhiLayer::setLive(bool live)
+{
+ if (live == m_live)
+ return;
+
+ m_live = live;
+
+ if (m_live && (!m_item || m_size.isNull()))
+ releaseResources();
+
+ markDirtyTexture();
+}
+
+void QSGRhiLayer::scheduleUpdate()
+{
+ if (m_grab)
+ return;
+
+ m_grab = true;
+ if (m_dirtyTexture)
+ emit updateRequested();
+}
+
+void QSGRhiLayer::setRecursive(bool recursive)
+{
+ m_recursive = recursive;
+}
+
+void QSGRhiLayer::setMirrorHorizontal(bool mirror)
+{
+ m_mirrorHorizontal = mirror;
+}
+
+void QSGRhiLayer::setMirrorVertical(bool mirror)
+{
+ m_mirrorVertical = mirror;
+}
+
+void QSGRhiLayer::markDirtyTexture()
+{
+ m_dirtyTexture = true;
+ if (m_live || m_grab)
+ emit updateRequested();
+}
+
+void QSGRhiLayer::releaseResources()
+{
+ delete m_rt;
+ m_rt = nullptr;
+
+ delete m_rtRp;
+ m_rtRp = nullptr;
+
+ delete m_ds;
+ m_ds = nullptr;
+
+ delete m_msaaColorBuffer;
+ m_msaaColorBuffer = nullptr;
+
+ delete m_texture;
+ m_texture = nullptr;
+
+ delete m_secondaryTexture;
+ m_secondaryTexture = nullptr;
+}
+
+void QSGRhiLayer::grab()
+{
+ if (!m_item || m_size.isNull()) {
+ releaseResources();
+ m_dirtyTexture = false;
+ return;
+ }
+
+ int effectiveSamples = m_samples;
+ // if no layer.samples was provided use the window's msaa setting
+ if (effectiveSamples <= 1)
+ effectiveSamples = m_context->msaaSampleCount();
+
+ const bool needsNewRt = !m_rt || m_rt->pixelSize() != m_size || (m_recursive && !m_secondaryTexture);
+ const bool mipmapSettingChanged = m_texture && m_texture->flags().testFlag(QRhiTexture::MipMapped) != m_mipmap;
+ const bool msaaSettingChanged = (effectiveSamples > 1 && !m_msaaColorBuffer) || (effectiveSamples <= 1 && m_msaaColorBuffer);
+
+ if (needsNewRt ||mipmapSettingChanged || msaaSettingChanged) {
+ if (effectiveSamples <= 1) {
+ m_multisampling = false;
+ } else {
+ m_multisampling = m_rhi->isFeatureSupported(QRhi::MultisampleRenderBuffer);
+ if (!m_multisampling)
+ qWarning("Layer requested %d samples but multisample renderbuffers are not supported", effectiveSamples);
+ }
+
+ QRhiTexture::Flags textureFlags = QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource;
+ if (m_mipmap)
+ textureFlags |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
+
+ if (m_multisampling) {
+ releaseResources();
+ m_msaaColorBuffer = m_rhi->newRenderBuffer(QRhiRenderBuffer::Color, m_size, effectiveSamples);
+ if (!m_msaaColorBuffer->build()) {
+ qWarning("Failed to build multisample color buffer for layer of size %dx%d, sample count %d",
+ m_size.width(), m_size.height(), effectiveSamples);
+ releaseResources();
+ return;
+ }
+ m_texture = m_rhi->newTexture(m_format, m_size, 1, textureFlags);
+ if (!m_texture->build()) {
+ qWarning("Failed to build texture for layer of size %dx%d", m_size.width(), m_size.height());
+ releaseResources();
+ return;
+ }
+ m_ds = m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, m_size, effectiveSamples);
+ if (!m_ds->build()) {
+ qWarning("Failed to build depth-stencil buffer for layer");
+ releaseResources();
+ return;
+ }
+ QRhiTextureRenderTargetDescription desc;
+ QRhiColorAttachment color0(m_msaaColorBuffer);
+ color0.setResolveTexture(m_texture);
+ desc.setColorAttachments({ color0 });
+ desc.setDepthStencilBuffer(m_ds);
+ m_rt = m_rhi->newTextureRenderTarget(desc);
+ m_rtRp = m_rt->newCompatibleRenderPassDescriptor();
+ if (!m_rtRp) {
+ qWarning("Failed to build render pass descriptor for layer");
+ releaseResources();
+ return;
+ }
+ m_rt->setRenderPassDescriptor(m_rtRp);
+ if (!m_rt->build()) {
+ qWarning("Failed to build texture render target for layer");
+ releaseResources();
+ return;
+ }
+ } else {
+ releaseResources();
+ m_texture = m_rhi->newTexture(m_format, m_size, 1, textureFlags);
+ if (!m_texture->build()) {
+ qWarning("Failed to build texture for layer of size %dx%d", m_size.width(), m_size.height());
+ releaseResources();
+ return;
+ }
+ m_ds = m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, m_size);
+ if (!m_ds->build()) {
+ qWarning("Failed to build depth-stencil buffer for layer");
+ releaseResources();
+ return;
+ }
+ QRhiColorAttachment color0(m_texture);
+ if (m_recursive) {
+ // Here rt is associated with m_secondaryTexture instead of m_texture.
+ // We will issue a copy to m_texture afterwards.
+ m_secondaryTexture = m_rhi->newTexture(m_format, m_size, 1, textureFlags);
+ if (!m_secondaryTexture->build()) {
+ qWarning("Failed to build texture for layer of size %dx%d", m_size.width(), m_size.height());
+ releaseResources();
+ return;
+ }
+ color0.setTexture(m_secondaryTexture);
+ }
+ m_rt = m_rhi->newTextureRenderTarget({ color0, m_ds });
+ m_rtRp = m_rt->newCompatibleRenderPassDescriptor();
+ if (!m_rtRp) {
+ qWarning("Failed to build render pass descriptor for layer");
+ releaseResources();
+ return;
+ }
+ m_rt->setRenderPassDescriptor(m_rtRp);
+ if (!m_rt->build()) {
+ qWarning("Failed to build texture render target for layer");
+ releaseResources();
+ return;
+ }
+ }
+ }
+
+ QSGNode *root = m_item;
+ while (root->firstChild() && root->type() != QSGNode::RootNodeType)
+ root = root->firstChild();
+ if (root->type() != QSGNode::RootNodeType)
+ return;
+
+ if (!m_renderer) {
+ m_renderer = m_context->createRenderer();
+ connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture()));
+ }
+ m_renderer->setRootNode(static_cast<QSGRootNode *>(root));
+ root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip and opacity update.
+ m_renderer->nodeChanged(root, QSGNode::DirtyForceUpdate); // Force render list update.
+
+ // This must not be moved. The flag must be reset only after the
+ // nodeChanged otherwise we end up with constantly updating even when the
+ // layer contents do not change.
+ m_dirtyTexture = false;
+
+ m_renderer->setDevicePixelRatio(m_dpr);
+ m_renderer->setDeviceRect(m_size);
+ m_renderer->setViewportRect(m_size);
+ QRectF mirrored;
+ if (m_rhi->isYUpInFramebuffer()) {
+ mirrored = QRectF(m_mirrorHorizontal ? m_rect.right() : m_rect.left(),
+ m_mirrorVertical ? m_rect.bottom() : m_rect.top(),
+ m_mirrorHorizontal ? -m_rect.width() : m_rect.width(),
+ m_mirrorVertical ? -m_rect.height() : m_rect.height());
+ } else {
+ mirrored = QRectF(m_mirrorHorizontal ? m_rect.right() : m_rect.left(),
+ m_mirrorVertical ? m_rect.top() : m_rect.bottom(),
+ m_mirrorHorizontal ? -m_rect.width() : m_rect.width(),
+ m_mirrorVertical ? m_rect.height() : -m_rect.height());
+ }
+ QSGAbstractRenderer::MatrixTransformFlags matrixFlags = 0;
+ if (!m_rhi->isYUpInNDC())
+ matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY;
+ m_renderer->setProjectionMatrixToRect(mirrored, matrixFlags);
+ m_renderer->setClearColor(Qt::transparent);
+ m_renderer->setRenderTarget(m_rt);
+ m_renderer->setCommandBuffer(m_context->currentFrameCommandBuffer());
+ m_renderer->setRenderPassDescriptor(m_rtRp);
+
+ QRhiResourceUpdateBatch *resourceUpdates = nullptr;
+
+ // render with our own "sub-renderer" (this will just add a render pass to the command buffer)
+ if (m_multisampling) {
+ m_context->renderNextRhiFrame(m_renderer);
+ } else {
+ if (m_recursive) {
+ m_context->renderNextRhiFrame(m_renderer);
+ if (!resourceUpdates)
+ resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ resourceUpdates->copyTexture(m_texture, m_secondaryTexture);
+ } else {
+ m_context->renderNextRhiFrame(m_renderer);
+ }
+ }
+
+ if (m_mipmap) {
+ if (!resourceUpdates)
+ resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ // going to be expensive - if done every frame - but the user asked for it...
+ resourceUpdates->generateMips(m_texture);
+ }
+
+ // Do not defer committing the resource updates to the main pass - with
+ // multiple layers there can be dependencies, so the texture should be
+ // usable once we return.
+ m_context->currentFrameCommandBuffer()->resourceUpdate(resourceUpdates);
+
+ root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip, opacity and render list update.
+
+ if (m_recursive)
+ markDirtyTexture(); // Continuously update if 'live' and 'recursive'.
+}
+
+QImage QSGRhiLayer::toImage() const
+{
+ if (!m_texture)
+ return QImage();
+
+ QRhiCommandBuffer *cb = m_context->currentFrameCommandBuffer();
+ QRhiResourceUpdateBatch *resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ QRhiReadbackResult result;
+ QRhiReadbackDescription readbackDesc(m_texture);
+ resourceUpdates->readBackTexture(readbackDesc, &result);
+
+ cb->resourceUpdate(resourceUpdates);
+
+ // Inefficient but what can you do. We need the results right away. This is
+ // not something that occurs in a normal rendering process anyway. (used by
+ // QQuickItem's grabToImage).
+ m_rhi->finish();
+
+ if (result.data.isEmpty()) {
+ qWarning("Layer grab failed");
+ return QImage();
+ }
+
+ // There is no room for negotiation here, the texture is RGBA8, and the
+ // readback happens with GL_RGBA on GL, so RGBA8888 is the only option.
+ // Also, Quick is always premultiplied alpha.
+ const QImage::Format imageFormat = QImage::Format_RGBA8888_Premultiplied;
+
+ const uchar *p = reinterpret_cast<const uchar *>(result.data.constData());
+ return QImage(p, result.pixelSize.width(), result.pixelSize.height(), imageFormat).mirrored();
+}
+
+QRectF QSGRhiLayer::normalizedTextureSubRect() const
+{
+ return QRectF(m_mirrorHorizontal ? 1 : 0,
+ m_mirrorVertical ? 0 : 1,
+ m_mirrorHorizontal ? -1 : 1,
+ m_mirrorVertical ? 1 : -1);
+}
+
+#include "moc_qsgrhilayer_p.cpp"
diff --git a/src/quick/scenegraph/qsgrhilayer_p.h b/src/quick/scenegraph/qsgrhilayer_p.h
new file mode 100644
index 0000000000..6c4953ce17
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhilayer_p.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 QSGRHILAYER_P_H
+#define QSGRHILAYER_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/qsgadaptationlayer_p.h>
+#include <private/qsgcontext_p.h>
+#include <private/qsgtexture_p.h>
+#include <QtGui/private/qrhi_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGDefaultRenderContext;
+class QSGRhiLayerPrivate;
+
+class Q_QUICK_PRIVATE_EXPORT QSGRhiLayer : public QSGLayer
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QSGRhiLayer)
+public:
+ QSGRhiLayer(QSGRenderContext *context);
+ ~QSGRhiLayer();
+
+ bool updateTexture() override;
+
+ bool hasAlphaChannel() const override;
+ bool hasMipmaps() const override;
+ QSize textureSize() const override { return m_size; }
+
+ void bind() override;
+ int textureId() const override;
+
+ void setItem(QSGNode *item) override;
+ void setRect(const QRectF &rect) override;
+ void setSize(const QSize &size) override;
+ void setHasMipmaps(bool mipmap) override;
+ void setFormat(uint format) override;
+ void setLive(bool live) override;
+ void setRecursive(bool recursive) override;
+ void setDevicePixelRatio(qreal ratio) override { m_dpr = ratio; }
+ void setMirrorHorizontal(bool mirror) override;
+ void setMirrorVertical(bool mirror) override;
+ QRectF normalizedTextureSubRect() const override;
+ void setSamples(int samples) override { m_samples = samples; }
+
+ void scheduleUpdate() override;
+ QImage toImage() const override;
+
+public Q_SLOTS:
+ void markDirtyTexture() override;
+ void invalidated() override;
+
+private:
+ void grab();
+ void releaseResources();
+
+ QSGNode *m_item = nullptr;
+ QRectF m_rect;
+ QSize m_size;
+ qreal m_dpr = 1;
+ QRhiTexture::Format m_format = QRhiTexture::RGBA8;
+
+ QSGRenderer *m_renderer = nullptr;
+ QRhiTexture *m_texture = nullptr;
+ QRhiRenderBuffer *m_ds = nullptr;
+ QRhiRenderBuffer *m_msaaColorBuffer = nullptr;
+ QRhiTexture *m_secondaryTexture = nullptr;
+ QRhiTextureRenderTarget *m_rt = nullptr;
+ QRhiRenderPassDescriptor *m_rtRp = nullptr;
+
+ QSGDefaultRenderContext *m_context = nullptr;
+ QRhi *m_rhi = nullptr;
+ int m_samples = 0;
+
+ uint m_mipmap : 1;
+ uint m_live : 1;
+ uint m_recursive : 1;
+ uint m_dirtyTexture : 1;
+ uint m_multisampling : 1;
+ uint m_grab : 1;
+ uint m_mirrorHorizontal : 1;
+ uint m_mirrorVertical : 1;
+};
+
+class QSGRhiLayerPrivate : public QSGTexturePrivate
+{
+ Q_DECLARE_PUBLIC(QSGRhiLayer)
+public:
+ int comparisonKey() const override;
+ QRhiTexture *rhiTexture() const override;
+ void updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGRHILAYER_P_H
diff --git a/src/quick/scenegraph/qsgrhishadereffectnode.cpp b/src/quick/scenegraph/qsgrhishadereffectnode.cpp
new file mode 100644
index 0000000000..6f6544548c
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhishadereffectnode.cpp
@@ -0,0 +1,886 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "qsgrhishadereffectnode_p.h"
+#include "qsgdefaultrendercontext_p.h"
+#include "qsgrhisupport_p.h"
+#include <qsgmaterialrhishader.h>
+#include <qsgtextureprovider.h>
+#include <private/qsgplaintexture_p.h>
+#include <QtGui/private/qshaderdescription_p.h>
+#include <QQmlFile>
+#include <QFile>
+#include <QFileSelector>
+
+QT_BEGIN_NAMESPACE
+
+void QSGRhiShaderLinker::reset(const QShader &vs, const QShader &fs)
+{
+ Q_ASSERT(vs.isValid() && fs.isValid());
+ m_vs = vs;
+ m_fs = fs;
+
+ m_error = false;
+
+ m_constantBufferSize = 0;
+ m_constants.clear();
+ m_samplers.clear();
+ m_samplerNameMap.clear();
+}
+
+void QSGRhiShaderLinker::feedConstants(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices)
+{
+ Q_ASSERT(shader.shaderInfo.variables.count() == shader.varData.count());
+ if (!dirtyIndices) {
+ m_constantBufferSize = qMax(m_constantBufferSize, shader.shaderInfo.constantDataSize);
+ for (int i = 0; i < shader.shaderInfo.variables.count(); ++i) {
+ const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &var(shader.shaderInfo.variables.at(i));
+ if (var.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Constant) {
+ const QSGShaderEffectNode::VariableData &vd(shader.varData.at(i));
+ Constant c;
+ c.size = var.size;
+ c.specialType = vd.specialType;
+ if (c.specialType != QSGShaderEffectNode::VariableData::SubRect) {
+ c.value = vd.value;
+ if (QSGRhiSupport::instance()->isShaderEffectDebuggingRequested()) {
+ if (c.specialType == QSGShaderEffectNode::VariableData::None) {
+ qDebug() << "cbuf prepare" << shader.shaderInfo.name << var.name
+ << "offset" << var.offset << "value" << c.value;
+ } else {
+ qDebug() << "cbuf prepare" << shader.shaderInfo.name << var.name
+ << "offset" << var.offset << "special" << c.specialType;
+ }
+ }
+ } else {
+ Q_ASSERT(var.name.startsWith(QByteArrayLiteral("qt_SubRect_")));
+ c.value = var.name.mid(11);
+ }
+ m_constants[var.offset] = c;
+ }
+ }
+ } else {
+ for (int idx : *dirtyIndices) {
+ const int offset = shader.shaderInfo.variables.at(idx).offset;
+ const QVariant value = shader.varData.at(idx).value;
+ m_constants[offset].value = value;
+ if (QSGRhiSupport::instance()->isShaderEffectDebuggingRequested()) {
+ qDebug() << "cbuf update" << shader.shaderInfo.name
+ << "offset" << offset << "value" << value;
+ }
+ }
+ }
+}
+
+void QSGRhiShaderLinker::feedSamplers(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices)
+{
+ if (!dirtyIndices) {
+ for (int i = 0; i < shader.shaderInfo.variables.count(); ++i) {
+ const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &var(shader.shaderInfo.variables.at(i));
+ const QSGShaderEffectNode::VariableData &vd(shader.varData.at(i));
+ if (var.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler) {
+ Q_ASSERT(vd.specialType == QSGShaderEffectNode::VariableData::Source);
+ m_samplers.insert(var.bindPoint, vd.value);
+ m_samplerNameMap.insert(var.name, var.bindPoint);
+ }
+ }
+ } else {
+ for (int idx : *dirtyIndices) {
+ const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &var(shader.shaderInfo.variables.at(idx));
+ const QSGShaderEffectNode::VariableData &vd(shader.varData.at(idx));
+ m_samplers.insert(var.bindPoint, vd.value);
+ m_samplerNameMap.insert(var.name, var.bindPoint);
+ }
+ }
+}
+
+void QSGRhiShaderLinker::linkTextureSubRects()
+{
+ // feedConstants stores <name> in Constant::value for subrect entries. Now
+ // that both constants and textures are known, replace the name with the
+ // texture binding point.
+ for (Constant &c : m_constants) {
+ if (c.specialType == QSGShaderEffectNode::VariableData::SubRect) {
+ if (c.value.type() == QVariant::ByteArray) {
+ const QByteArray name = c.value.toByteArray();
+ if (!m_samplerNameMap.contains(name))
+ qWarning("ShaderEffect: qt_SubRect_%s refers to unknown source texture", name.constData());
+ c.value = m_samplerNameMap[name];
+ }
+ }
+ }
+}
+
+void QSGRhiShaderLinker::dump()
+{
+ if (m_error) {
+ qDebug() << "Failed to generate program data";
+ return;
+ }
+ qDebug() << "Combined shader data" << m_vs << m_fs << "cbuffer size" << m_constantBufferSize;
+ qDebug() << " - constants" << m_constants;
+ qDebug() << " - samplers" << m_samplers;
+}
+
+QDebug operator<<(QDebug debug, const QSGRhiShaderLinker::Constant &c)
+{
+ QDebugStateSaver saver(debug);
+ debug.space();
+ debug << "size" << c.size;
+ if (c.specialType != QSGShaderEffectNode::VariableData::None)
+ debug << "special" << c.specialType;
+ else
+ debug << "value" << c.value;
+ return debug;
+}
+
+struct QSGRhiShaderMaterialTypeCache
+{
+ QSGMaterialType *get(const QShader &vs, const QShader &fs);
+ void reset() { qDeleteAll(m_types); m_types.clear(); }
+
+ struct Key {
+ QShader blob[2];
+ Key() { }
+ Key(const QShader &vs, const QShader &fs) { blob[0] = vs; blob[1] = fs; }
+ bool operator==(const Key &other) const {
+ return blob[0] == other.blob[0] && blob[1] == other.blob[1];
+ }
+ };
+ QHash<Key, QSGMaterialType *> m_types;
+};
+
+uint qHash(const QSGRhiShaderMaterialTypeCache::Key &key, uint seed = 0)
+{
+ uint hash = seed;
+ for (int i = 0; i < 2; ++i)
+ hash = hash * 31337 + qHash(key.blob[i]);
+ return hash;
+}
+
+QSGMaterialType *QSGRhiShaderMaterialTypeCache::get(const QShader &vs, const QShader &fs)
+{
+ const Key k(vs, fs);
+ if (m_types.contains(k))
+ return m_types.value(k);
+
+ QSGMaterialType *t = new QSGMaterialType;
+ m_types.insert(k, t);
+ return t;
+}
+
+static QSGRhiShaderMaterialTypeCache shaderMaterialTypeCache;
+
+class QSGRhiShaderEffectMaterialShader : public QSGMaterialRhiShader
+{
+public:
+ QSGRhiShaderEffectMaterialShader(const QSGRhiShaderEffectMaterial *material);
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+ void updateSampledImage(RenderState &state, int binding, QSGTexture **texture, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+ bool updateGraphicsPipelineState(RenderState &state, GraphicsPipelineState *ps, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+QSGRhiShaderEffectMaterialShader::QSGRhiShaderEffectMaterialShader(const QSGRhiShaderEffectMaterial *material)
+{
+ setFlag(UpdatesGraphicsPipelineState, true);
+ setShader(VertexStage, material->m_vertexShader);
+ setShader(FragmentStage, material->m_fragmentShader);
+}
+
+static inline QColor qsg_premultiply_color(const QColor &c)
+{
+ return QColor::fromRgbF(c.redF() * c.alphaF(), c.greenF() * c.alphaF(), c.blueF() * c.alphaF(), c.alphaF());
+}
+
+bool QSGRhiShaderEffectMaterialShader::updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(oldMaterial);
+ QSGRhiShaderEffectMaterial *mat = static_cast<QSGRhiShaderEffectMaterial *>(newMaterial);
+
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+
+ for (auto it = mat->m_linker.m_constants.constBegin(), itEnd = mat->m_linker.m_constants.constEnd(); it != itEnd; ++it) {
+ const int offset = it.key();
+ char *dst = buf->data() + offset;
+ const QSGRhiShaderLinker::Constant &c(it.value());
+ if (c.specialType == QSGShaderEffectNode::VariableData::Opacity) {
+ if (state.isOpacityDirty()) {
+ const float f = state.opacity();
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, &f, sizeof(f));
+ changed = true;
+ }
+ } else if (c.specialType == QSGShaderEffectNode::VariableData::Matrix) {
+ if (state.isMatrixDirty()) {
+ const int sz = 16 * sizeof(float);
+ Q_ASSERT(sz == c.size);
+ memcpy(dst, state.combinedMatrix().constData(), sz);
+ changed = true;
+ }
+ } else if (c.specialType == QSGShaderEffectNode::VariableData::SubRect) {
+ // vec4
+ QRectF subRect(0, 0, 1, 1);
+ const int binding = c.value.toInt(); // filled in by linkTextureSubRects
+ if (binding < QSGRhiShaderEffectMaterial::MAX_BINDINGS) {
+ if (QSGTextureProvider *tp = mat->m_textureProviders.at(binding)) {
+ if (QSGTexture *t = tp->texture())
+ subRect = t->normalizedTextureSubRect();
+ }
+ }
+ const float f[4] = { float(subRect.x()), float(subRect.y()),
+ float(subRect.width()), float(subRect.height()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ } else if (c.specialType == QSGShaderEffectNode::VariableData::None) {
+ changed = true;
+ switch (int(c.value.type())) {
+ case QMetaType::QColor: {
+ const QColor v = qsg_premultiply_color(qvariant_cast<QColor>(c.value));
+ const float f[4] = { float(v.redF()), float(v.greenF()), float(v.blueF()), float(v.alphaF()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::Float: {
+ const float f = qvariant_cast<float>(c.value);
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, &f, sizeof(f));
+ break;
+ }
+ case QMetaType::Double: {
+ const float f = float(qvariant_cast<double>(c.value));
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, &f, sizeof(f));
+ break;
+ }
+ case QMetaType::Int: {
+ const int i = c.value.toInt();
+ Q_ASSERT(sizeof(i) == c.size);
+ memcpy(dst, &i, sizeof(i));
+ break;
+ }
+ case QMetaType::Bool: {
+ const bool b = c.value.toBool();
+ Q_ASSERT(sizeof(b) == c.size);
+ memcpy(dst, &b, sizeof(b));
+ break;
+ }
+ case QMetaType::QTransform: { // mat3
+ const QTransform v = qvariant_cast<QTransform>(c.value);
+ const float m[3][3] = {
+ { float(v.m11()), float(v.m12()), float(v.m13()) },
+ { float(v.m21()), float(v.m22()), float(v.m23()) },
+ { float(v.m31()), float(v.m32()), float(v.m33()) }
+ };
+ Q_ASSERT(sizeof(m) == c.size);
+ memcpy(dst, m[0], sizeof(m));
+ break;
+ }
+ case QMetaType::QSize:
+ case QMetaType::QSizeF: { // vec2
+ const QSizeF v = c.value.toSizeF();
+ const float f[2] = { float(v.width()), float(v.height()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QPoint:
+ case QMetaType::QPointF: { // vec2
+ const QPointF v = c.value.toPointF();
+ const float f[2] = { float(v.x()), float(v.y()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QRect:
+ case QMetaType::QRectF: { // vec4
+ const QRectF v = c.value.toRectF();
+ const float f[4] = { float(v.x()), float(v.y()), float(v.width()), float(v.height()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QVector2D: { // vec2
+ const QVector2D v = qvariant_cast<QVector2D>(c.value);
+ const float f[2] = { float(v.x()), float(v.y()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QVector3D: { // vec3
+ const QVector3D v = qvariant_cast<QVector3D>(c.value);
+ const float f[3] = { float(v.x()), float(v.y()), float(v.z()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QVector4D: { // vec4
+ const QVector4D v = qvariant_cast<QVector4D>(c.value);
+ const float f[4] = { float(v.x()), float(v.y()), float(v.z()), float(v.w()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QQuaternion: { // vec4
+ const QQuaternion v = qvariant_cast<QQuaternion>(c.value);
+ const float f[4] = { float(v.x()), float(v.y()), float(v.z()), float(v.scalar()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QMatrix4x4: { // mat4
+ const QMatrix4x4 v = qvariant_cast<QMatrix4x4>(c.value);
+ const int sz = 16 * sizeof(float);
+ Q_ASSERT(sz == c.size);
+ memcpy(dst, v.constData(), sz);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ return changed;
+}
+
+void QSGRhiShaderEffectMaterialShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(oldMaterial);
+ QSGRhiShaderEffectMaterial *mat = static_cast<QSGRhiShaderEffectMaterial *>(newMaterial);
+
+ if (binding >= QSGRhiShaderEffectMaterial::MAX_BINDINGS)
+ return;
+
+ QSGTextureProvider *tp = mat->m_textureProviders.at(binding);
+ if (tp) {
+ if (QSGTexture *t = tp->texture()) {
+ t->updateRhiTexture(state.rhi(), state.resourceUpdateBatch());
+ if (t->isAtlasTexture() && !mat->m_geometryUsesTextureSubRect) {
+ // Why the hassle with the batch: while removedFromAtlas() is
+ // able to operate with its own resource update batch (which is
+ // then committed immediately), that approach is wrong when the
+ // atlas enqueued (in the updateRhiTexture() above) not yet
+ // committed operations to state.resourceUpdateBatch()... The
+ // only safe way then is to use the same batch the atlas'
+ // updateRhiTexture() used.
+ t->setWorkResourceUpdateBatch(state.resourceUpdateBatch());
+ QSGTexture *newTexture = t->removedFromAtlas();
+ t->setWorkResourceUpdateBatch(nullptr);
+ if (newTexture)
+ t = newTexture;
+ }
+ *texture = t;
+ return;
+ }
+ }
+
+ if (!mat->m_dummyTexture) {
+ mat->m_dummyTexture = new QSGPlainTexture;
+ mat->m_dummyTexture->setFiltering(QSGTexture::Nearest);
+ mat->m_dummyTexture->setHorizontalWrapMode(QSGTexture::Repeat);
+ mat->m_dummyTexture->setVerticalWrapMode(QSGTexture::Repeat);
+ QImage img(128, 128, QImage::Format_ARGB32_Premultiplied);
+ img.fill(0);
+ mat->m_dummyTexture->setImage(img);
+ mat->m_dummyTexture->updateRhiTexture(state.rhi(), state.resourceUpdateBatch());
+ }
+ *texture = mat->m_dummyTexture;
+}
+
+bool QSGRhiShaderEffectMaterialShader::updateGraphicsPipelineState(RenderState &state, GraphicsPipelineState *ps,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_UNUSED(state);
+ Q_UNUSED(oldMaterial);
+ QSGRhiShaderEffectMaterial *mat = static_cast<QSGRhiShaderEffectMaterial *>(newMaterial);
+
+ switch (mat->m_cullMode) {
+ case QSGShaderEffectNode::FrontFaceCulling:
+ ps->cullMode = GraphicsPipelineState::CullFront;
+ return true;
+ case QSGShaderEffectNode::BackFaceCulling:
+ ps->cullMode = GraphicsPipelineState::CullBack;
+ return true;
+ default:
+ return false;
+ }
+}
+
+QSGRhiShaderEffectMaterial::QSGRhiShaderEffectMaterial(QSGRhiShaderEffectNode *node)
+ : m_node(node)
+{
+ setFlag(SupportsRhiShader | Blending | RequiresFullMatrix, true); // may be changed in syncMaterial()
+}
+
+QSGRhiShaderEffectMaterial::~QSGRhiShaderEffectMaterial()
+{
+ delete m_dummyTexture;
+}
+
+static bool hasAtlasTexture(const QVector<QSGTextureProvider *> &textureProviders)
+{
+ for (QSGTextureProvider *tp : textureProviders) {
+ if (tp && tp->texture() && tp->texture()->isAtlasTexture())
+ return true;
+ }
+ return false;
+}
+
+int QSGRhiShaderEffectMaterial::compare(const QSGMaterial *other) const
+{
+ Q_ASSERT(other && type() == other->type());
+ const QSGRhiShaderEffectMaterial *o = static_cast<const QSGRhiShaderEffectMaterial *>(other);
+
+ if (int diff = m_cullMode - o->m_cullMode)
+ return diff;
+
+ if (int diff = m_textureProviders.count() - o->m_textureProviders.count())
+ return diff;
+
+ if (m_linker.m_constants != o->m_linker.m_constants)
+ return 1;
+
+ if (hasAtlasTexture(m_textureProviders) && !m_geometryUsesTextureSubRect)
+ return -1;
+
+ if (hasAtlasTexture(o->m_textureProviders) && !o->m_geometryUsesTextureSubRect)
+ return 1;
+
+ for (int binding = 0, count = m_textureProviders.count(); binding != count; ++binding) {
+ QSGTextureProvider *tp1 = m_textureProviders.at(binding);
+ QSGTextureProvider *tp2 = o->m_textureProviders.at(binding);
+ if (tp1 && tp2) {
+ QSGTexture *t1 = tp1->texture();
+ QSGTexture *t2 = tp2->texture();
+ if (t1 && t2) {
+ if (int diff = t1->comparisonKey() - t2->comparisonKey())
+ return diff;
+ } else {
+ if (!t1 && t2)
+ return -1;
+ if (t1 && !t2)
+ return 1;
+ }
+ } else {
+ if (!tp1 && tp2)
+ return -1;
+ if (tp1 && !tp2)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+QSGMaterialType *QSGRhiShaderEffectMaterial::type() const
+{
+ return m_materialType;
+}
+
+QSGMaterialShader *QSGRhiShaderEffectMaterial::createShader() const
+{
+ Q_ASSERT(flags().testFlag(RhiShaderWanted));
+ return new QSGRhiShaderEffectMaterialShader(this);
+}
+
+void QSGRhiShaderEffectMaterial::updateTextureProviders(bool layoutChange)
+{
+ if (layoutChange) {
+ for (QSGTextureProvider *tp : m_textureProviders) {
+ if (tp) {
+ QObject::disconnect(tp, SIGNAL(textureChanged()), m_node,
+ SLOT(handleTextureChange()));
+ QObject::disconnect(tp, SIGNAL(destroyed(QObject*)), m_node,
+ SLOT(handleTextureProviderDestroyed(QObject*)));
+ }
+ }
+ m_textureProviders.fill(nullptr, MAX_BINDINGS);
+ }
+
+ for (auto it = m_linker.m_samplers.constBegin(), itEnd = m_linker.m_samplers.constEnd(); it != itEnd; ++it) {
+ const int binding = it.key();
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(it.value()));
+ QSGTextureProvider *newProvider = source && source->isTextureProvider() ? source->textureProvider() : nullptr;
+ if (binding >= MAX_BINDINGS) {
+ qWarning("Sampler at binding %d exceeds the available ShaderEffect binding slots; ignored",
+ binding);
+ continue;
+ }
+ QSGTextureProvider *&activeProvider(m_textureProviders[binding]);
+ if (newProvider != activeProvider) {
+ if (activeProvider) {
+ QObject::disconnect(activeProvider, SIGNAL(textureChanged()), m_node,
+ SLOT(handleTextureChange()));
+ QObject::disconnect(activeProvider, SIGNAL(destroyed(QObject*)), m_node,
+ SLOT(handleTextureProviderDestroyed(QObject*)));
+ }
+ if (newProvider) {
+ Q_ASSERT_X(newProvider->thread() == QThread::currentThread(),
+ "QSGRhiShaderEffectMaterial::updateTextureProviders",
+ "Texture provider must belong to the rendering thread");
+ QObject::connect(newProvider, SIGNAL(textureChanged()), m_node, SLOT(handleTextureChange()));
+ QObject::connect(newProvider, SIGNAL(destroyed(QObject*)), m_node,
+ SLOT(handleTextureProviderDestroyed(QObject*)));
+ } else {
+ const char *typeName = source ? source->metaObject()->className() : it.value().typeName();
+ qWarning("ShaderEffect: Texture t%d is not assigned a valid texture provider (%s).",
+ binding, typeName);
+ }
+ activeProvider = newProvider;
+ }
+ }
+}
+
+QSGRhiShaderEffectNode::QSGRhiShaderEffectNode(QSGDefaultRenderContext *rc, QSGRhiGuiThreadShaderEffectManager *mgr)
+ : QSGShaderEffectNode(mgr),
+ m_rc(rc),
+ m_mgr(mgr),
+ m_material(this)
+{
+ setFlag(UsePreprocess, true);
+ setMaterial(&m_material);
+}
+
+QRectF QSGRhiShaderEffectNode::updateNormalizedTextureSubRect(bool supportsAtlasTextures)
+{
+ QRectF srcRect(0, 0, 1, 1);
+ bool geometryUsesTextureSubRect = false;
+ if (supportsAtlasTextures) {
+ QSGTextureProvider *tp = nullptr;
+ for (int binding = 0, count = m_material.m_textureProviders.count(); binding != count; ++binding) {
+ if (QSGTextureProvider *candidate = m_material.m_textureProviders.at(binding)) {
+ if (!tp) {
+ tp = candidate;
+ } else { // there can only be one...
+ tp = nullptr;
+ break;
+ }
+ }
+ }
+ if (tp && tp->texture()) {
+ srcRect = tp->texture()->normalizedTextureSubRect();
+ geometryUsesTextureSubRect = true;
+ }
+ }
+
+ if (m_material.m_geometryUsesTextureSubRect != geometryUsesTextureSubRect) {
+ m_material.m_geometryUsesTextureSubRect = geometryUsesTextureSubRect;
+ markDirty(QSGNode::DirtyMaterial);
+ }
+
+ return srcRect;
+}
+
+static QShader loadShader(const QString &filename)
+{
+ QFile f(filename);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning() << "Failed to find shader" << filename;
+ return QShader();
+ }
+ return QShader::fromSerialized(f.readAll());
+}
+
+void QSGRhiShaderEffectNode::syncMaterial(SyncData *syncData)
+{
+ static QShader defaultVertexShader;
+ static QShader defaultFragmentShader;
+
+ if (bool(m_material.flags() & QSGMaterial::Blending) != syncData->blending) {
+ m_material.setFlag(QSGMaterial::Blending, syncData->blending);
+ markDirty(QSGNode::DirtyMaterial);
+ }
+
+ if (m_material.m_cullMode != syncData->cullMode) {
+ m_material.m_cullMode = syncData->cullMode;
+ markDirty(QSGNode::DirtyMaterial);
+ }
+
+ if (syncData->dirty & QSGShaderEffectNode::DirtyShaders) {
+ m_material.m_hasCustomVertexShader = syncData->vertex.shader->hasShaderCode;
+ if (m_material.m_hasCustomVertexShader) {
+ m_material.m_vertexShader = syncData->vertex.shader->shaderInfo.rhiShader;
+ } else {
+ if (!defaultVertexShader.isValid())
+ defaultVertexShader = loadShader(QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shadereffect.vert.qsb"));
+ m_material.m_vertexShader = defaultVertexShader;
+ }
+
+ m_material.m_hasCustomFragmentShader = syncData->fragment.shader->hasShaderCode;
+ if (m_material.m_hasCustomFragmentShader) {
+ m_material.m_fragmentShader = syncData->fragment.shader->shaderInfo.rhiShader;
+ } else {
+ if (!defaultFragmentShader.isValid())
+ defaultFragmentShader = loadShader(QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shadereffect.frag.qsb"));
+ m_material.m_fragmentShader = defaultFragmentShader;
+ }
+
+ m_material.m_materialType = shaderMaterialTypeCache.get(m_material.m_vertexShader, m_material.m_fragmentShader);
+ m_material.m_linker.reset(m_material.m_vertexShader, m_material.m_fragmentShader);
+
+ if (m_material.m_hasCustomVertexShader) {
+ m_material.m_linker.feedConstants(*syncData->vertex.shader);
+ m_material.m_linker.feedSamplers(*syncData->vertex.shader);
+ } else {
+ QSGShaderEffectNode::ShaderData defaultSD;
+ defaultSD.shaderInfo.name = QLatin1String("Default ShaderEffect vertex shader");
+ defaultSD.shaderInfo.rhiShader = m_material.m_vertexShader;
+ defaultSD.shaderInfo.type = QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex;
+
+ // { mat4 qt_Matrix; float qt_Opacity; } where only the matrix is used
+ QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
+ v.name = QByteArrayLiteral("qt_Matrix");
+ v.offset = 0;
+ v.size = 16 * sizeof(float);
+ defaultSD.shaderInfo.variables.append(v);
+ QSGShaderEffectNode::VariableData vd;
+ vd.specialType = QSGShaderEffectNode::VariableData::Matrix;
+ defaultSD.varData.append(vd);
+ defaultSD.shaderInfo.constantDataSize = (16 + 1) * sizeof(float);
+ m_material.m_linker.feedConstants(defaultSD);
+ }
+
+ if (m_material.m_hasCustomFragmentShader) {
+ m_material.m_linker.feedConstants(*syncData->fragment.shader);
+ m_material.m_linker.feedSamplers(*syncData->fragment.shader);
+ } else {
+ QSGShaderEffectNode::ShaderData defaultSD;
+ defaultSD.shaderInfo.name = QLatin1String("Default ShaderEffect fragment shader");
+ defaultSD.shaderInfo.rhiShader = m_material.m_fragmentShader;
+ defaultSD.shaderInfo.type = QSGGuiThreadShaderEffectManager::ShaderInfo::TypeFragment;
+
+ // { mat4 qt_Matrix; float qt_Opacity; } where only the opacity is used
+ QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
+ v.name = QByteArrayLiteral("qt_Opacity");
+ v.offset = 16 * sizeof(float);
+ v.size = sizeof(float);
+ defaultSD.shaderInfo.variables.append(v);
+ QSGShaderEffectNode::VariableData vd;
+ vd.specialType = QSGShaderEffectNode::VariableData::Opacity;
+ defaultSD.varData.append(vd);
+
+ v.name = QByteArrayLiteral("source");
+ v.bindPoint = 1;
+ v.type = QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler;
+ defaultSD.shaderInfo.variables.append(v);
+ for (const QSGShaderEffectNode::VariableData &extVarData : qAsConst(syncData->fragment.shader->varData)) {
+ if (extVarData.specialType == QSGShaderEffectNode::VariableData::Source) {
+ vd.value = extVarData.value;
+ break;
+ }
+ }
+ vd.specialType = QSGShaderEffectNode::VariableData::Source;
+ defaultSD.varData.append(vd);
+
+ defaultSD.shaderInfo.constantDataSize = (16 + 1) * sizeof(float);
+
+ m_material.m_linker.feedConstants(defaultSD);
+ m_material.m_linker.feedSamplers(defaultSD);
+ }
+
+ m_material.m_linker.linkTextureSubRects();
+ m_material.updateTextureProviders(true);
+ markDirty(QSGNode::DirtyMaterial);
+
+ } else {
+
+ if (syncData->dirty & QSGShaderEffectNode::DirtyShaderConstant) {
+ if (!syncData->vertex.dirtyConstants->isEmpty())
+ m_material.m_linker.feedConstants(*syncData->vertex.shader, syncData->vertex.dirtyConstants);
+ if (!syncData->fragment.dirtyConstants->isEmpty())
+ m_material.m_linker.feedConstants(*syncData->fragment.shader, syncData->fragment.dirtyConstants);
+ markDirty(QSGNode::DirtyMaterial);
+ }
+
+ if (syncData->dirty & QSGShaderEffectNode::DirtyShaderTexture) {
+ if (!syncData->vertex.dirtyTextures->isEmpty())
+ m_material.m_linker.feedSamplers(*syncData->vertex.shader, syncData->vertex.dirtyTextures);
+ if (!syncData->fragment.dirtyTextures->isEmpty())
+ m_material.m_linker.feedSamplers(*syncData->fragment.shader, syncData->fragment.dirtyTextures);
+ m_material.m_linker.linkTextureSubRects();
+ m_material.updateTextureProviders(false);
+ markDirty(QSGNode::DirtyMaterial);
+ }
+ }
+
+ if (bool(m_material.flags() & QSGMaterial::RequiresFullMatrix) != m_material.m_hasCustomVertexShader) {
+ m_material.setFlag(QSGMaterial::RequiresFullMatrix, m_material.m_hasCustomVertexShader);
+ markDirty(QSGNode::DirtyMaterial);
+ }
+}
+
+void QSGRhiShaderEffectNode::handleTextureChange()
+{
+ markDirty(QSGNode::DirtyMaterial);
+ emit m_mgr->textureChanged();
+}
+
+void QSGRhiShaderEffectNode::handleTextureProviderDestroyed(QObject *object)
+{
+ for (QSGTextureProvider *&tp : m_material.m_textureProviders) {
+ if (tp == object)
+ tp = nullptr;
+ }
+}
+
+void QSGRhiShaderEffectNode::preprocess()
+{
+ for (QSGTextureProvider *tp : m_material.m_textureProviders) {
+ if (tp) {
+ if (QSGDynamicTexture *texture = qobject_cast<QSGDynamicTexture *>(tp->texture()))
+ texture->updateTexture();
+ }
+ }
+}
+
+void QSGRhiShaderEffectNode::cleanupMaterialTypeCache()
+{
+ shaderMaterialTypeCache.reset();
+}
+
+bool QSGRhiGuiThreadShaderEffectManager::hasSeparateSamplerAndTextureObjects() const
+{
+ return false; // because SPIR-V and QRhi make it look so, regardless of the underlying API
+}
+
+QString QSGRhiGuiThreadShaderEffectManager::log() const
+{
+ return QString();
+}
+
+QSGGuiThreadShaderEffectManager::Status QSGRhiGuiThreadShaderEffectManager::status() const
+{
+ return m_status;
+}
+
+void QSGRhiGuiThreadShaderEffectManager::prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result)
+{
+ QUrl srcUrl(QString::fromUtf8(src));
+ if (!srcUrl.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) || srcUrl.isLocalFile()) {
+ if (!m_fileSelector) {
+ m_fileSelector = new QFileSelector(this);
+ m_fileSelector->setExtraSelectors(QStringList() << QStringLiteral("qsb"));
+ }
+ const QString fn = m_fileSelector->select(QQmlFile::urlToLocalFileOrQrc(srcUrl));
+ QFile f(fn);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning("ShaderEffect: Failed to read %s", qPrintable(fn));
+ m_status = Error;
+ emit shaderCodePrepared(false, typeHint, src, result);
+ emit logAndStatusChanged();
+ return;
+ }
+ const QShader s = QShader::fromSerialized(f.readAll());
+ f.close();
+ if (!s.isValid()) {
+ qWarning("ShaderEffect: Failed to deserialize QShader from %s", qPrintable(fn));
+ m_status = Error;
+ emit shaderCodePrepared(false, typeHint, src, result);
+ emit logAndStatusChanged();
+ return;
+ }
+ result->name = fn;
+ result->rhiShader = s;
+ const bool ok = reflect(result);
+ m_status = ok ? Compiled : Error;
+ emit shaderCodePrepared(ok, typeHint, src, result);
+ emit logAndStatusChanged();
+ } else {
+ qWarning("rhi shader effect only supports files (qrc or local) at the moment");
+ emit shaderCodePrepared(false, typeHint, src, result);
+ }
+}
+
+bool QSGRhiGuiThreadShaderEffectManager::reflect(ShaderInfo *result)
+{
+ switch (result->rhiShader.stage()) {
+ case QShader::VertexStage:
+ result->type = ShaderInfo::TypeVertex;
+ break;
+ case QShader::FragmentStage:
+ result->type = ShaderInfo::TypeFragment;
+ break;
+ default:
+ result->type = ShaderInfo::TypeOther;
+ qWarning("Unsupported shader stage (%d)", result->rhiShader.stage());
+ return false;
+ }
+
+ const QShaderDescription desc = result->rhiShader.description();
+ result->constantDataSize = 0;
+
+ int ubufBinding = -1;
+ const QVector<QShaderDescription::UniformBlock> ubufs = desc.uniformBlocks();
+ const int ubufCount = ubufs.count();
+ for (int i = 0; i < ubufCount; ++i) {
+ const QShaderDescription::UniformBlock &ubuf(ubufs[i]);
+ if (ubufBinding == -1 && ubuf.binding >= 0) {
+ ubufBinding = ubuf.binding;
+ result->constantDataSize = ubuf.size;
+ for (const QShaderDescription::BlockVariable &member : ubuf.members) {
+ ShaderInfo::Variable v;
+ v.type = ShaderInfo::Constant;
+ v.name = member.name.toUtf8();
+ v.offset = member.offset;
+ v.size = member.size;
+ result->variables.append(v);
+ }
+ } else {
+ qWarning("Uniform block %s (binding %d) ignored", qPrintable(ubuf.blockName), ubuf.binding);
+ }
+ }
+
+ const QVector<QShaderDescription::InOutVariable> combinedImageSamplers = desc.combinedImageSamplers();
+ const int samplerCount = combinedImageSamplers.count();
+ for (int i = 0; i < samplerCount; ++i) {
+ const QShaderDescription::InOutVariable &combinedImageSampler(combinedImageSamplers[i]);
+ ShaderInfo::Variable v;
+ v.type = ShaderInfo::Sampler;
+ v.name = combinedImageSampler.name.toUtf8();
+ v.bindPoint = combinedImageSampler.binding;
+ result->variables.append(v);
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgrhishadereffectnode_p.h b/src/quick/scenegraph/qsgrhishadereffectnode_p.h
new file mode 100644
index 0000000000..26460d24b2
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhishadereffectnode_p.h
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 QSGRHISHADEREFFECTNODE_P_H
+#define QSGRHISHADEREFFECTNODE_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/qsgadaptationlayer_p.h>
+#include <qsgmaterial.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGDefaultRenderContext;
+class QSGPlainTexture;
+class QSGRhiShaderEffectNode;
+class QSGRhiGuiThreadShaderEffectManager;
+class QFileSelector;
+
+class QSGRhiShaderLinker
+{
+public:
+ void reset(const QShader &vs, const QShader &fs);
+
+ void feedConstants(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices = nullptr);
+ void feedSamplers(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices = nullptr);
+ void linkTextureSubRects();
+
+ void dump();
+
+ struct Constant {
+ uint size;
+ QSGShaderEffectNode::VariableData::SpecialType specialType;
+ QVariant value;
+ bool operator==(const Constant &other) const {
+ return size == other.size && specialType == other.specialType
+ && (specialType == QSGShaderEffectNode::VariableData::None ? value == other.value : true);
+ }
+ };
+
+ bool m_error;
+ QShader m_vs;
+ QShader m_fs;
+ uint m_constantBufferSize;
+ QHash<uint, Constant> m_constants; // offset -> Constant
+ QHash<int, QVariant> m_samplers; // binding -> value (source ref)
+ QHash<QByteArray, int> m_samplerNameMap; // name -> binding
+};
+
+QDebug operator<<(QDebug debug, const QSGRhiShaderLinker::Constant &c);
+
+class QSGRhiShaderEffectMaterial : public QSGMaterial
+{
+public:
+ QSGRhiShaderEffectMaterial(QSGRhiShaderEffectNode *node);
+ ~QSGRhiShaderEffectMaterial();
+
+ int compare(const QSGMaterial *other) const override;
+ QSGMaterialType *type() const override;
+ QSGMaterialShader *createShader() const override;
+
+ void updateTextureProviders(bool layoutChange);
+
+ static const int MAX_BINDINGS = 32;
+
+ QSGRhiShaderEffectNode *m_node;
+ QSGMaterialType *m_materialType = nullptr;
+ QSGRhiShaderLinker m_linker;
+ QVector<QSGTextureProvider *> m_textureProviders; // [binding] = QSGTextureProvider
+ bool m_geometryUsesTextureSubRect = false;
+ QSGShaderEffectNode::CullMode m_cullMode = QSGShaderEffectNode::NoCulling;
+ bool m_hasCustomVertexShader = false;
+ bool m_hasCustomFragmentShader = false;
+ QShader m_vertexShader;
+ QShader m_fragmentShader;
+ QSGPlainTexture *m_dummyTexture = nullptr;
+};
+
+class QSGRhiShaderEffectNode : public QObject, public QSGShaderEffectNode
+{
+ Q_OBJECT
+
+public:
+ QSGRhiShaderEffectNode(QSGDefaultRenderContext *rc, QSGRhiGuiThreadShaderEffectManager *mgr);
+
+ QRectF updateNormalizedTextureSubRect(bool supportsAtlasTextures) override;
+ void syncMaterial(SyncData *syncData) override;
+ void preprocess() override;
+
+ static void cleanupMaterialTypeCache();
+
+private Q_SLOTS:
+ void handleTextureChange();
+ void handleTextureProviderDestroyed(QObject *object);
+
+private:
+ QSGDefaultRenderContext *m_rc;
+ QSGRhiGuiThreadShaderEffectManager *m_mgr;
+ QSGRhiShaderEffectMaterial m_material;
+};
+
+class QSGRhiGuiThreadShaderEffectManager : public QSGGuiThreadShaderEffectManager
+{
+public:
+ bool hasSeparateSamplerAndTextureObjects() const override;
+ QString log() const override;
+ Status status() const override;
+ void prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result) override;
+
+private:
+ bool reflect(ShaderInfo *result);
+ Status m_status = Uncompiled;
+ QFileSelector *m_fileSelector = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGRHISHADEREFFECTNODE_P_H
diff --git a/src/quick/scenegraph/qsgrhisupport.cpp b/src/quick/scenegraph/qsgrhisupport.cpp
new file mode 100644
index 0000000000..12c6742342
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhisupport.cpp
@@ -0,0 +1,608 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "qsgrhisupport_p.h"
+#include "qsgdefaultrendercontext_p.h"
+#include <QtGui/qwindow.h>
+
+#if QT_CONFIG(vulkan)
+#include <QtGui/qvulkaninstance.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if QT_CONFIG(vulkan)
+QVulkanInstance *s_vulkanInstance = nullptr;
+#endif
+
+QVulkanInstance *QSGRhiSupport::vulkanInstance()
+{
+#if QT_CONFIG(vulkan)
+ QSGRhiSupport *inst = QSGRhiSupport::instance();
+ if (!inst->isRhiEnabled() || inst->rhiBackend() != QRhi::Vulkan)
+ return nullptr;
+
+ if (!s_vulkanInstance) {
+ s_vulkanInstance = new QVulkanInstance;
+ if (inst->isDebugLayerRequested()) {
+#ifndef Q_OS_ANDROID
+ s_vulkanInstance->setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
+#else
+ s_vulkanInstance->setLayers(QByteArrayList()
+ << "VK_LAYER_GOOGLE_threading"
+ << "VK_LAYER_LUNARG_parameter_validation"
+ << "VK_LAYER_LUNARG_object_tracker"
+ << "VK_LAYER_LUNARG_core_validation"
+ << "VK_LAYER_LUNARG_image"
+ << "VK_LAYER_LUNARG_swapchain"
+ << "VK_LAYER_GOOGLE_unique_objects");
+#endif
+ }
+ s_vulkanInstance->setExtensions(QByteArrayList()
+ << "VK_KHR_get_physical_device_properties2");
+ 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::cleanup()
+{
+#if QT_CONFIG(vulkan)
+ delete s_vulkanInstance;
+ s_vulkanInstance = nullptr;
+#endif
+}
+
+QSGRhiSupport::QSGRhiSupport()
+ : m_set(false),
+ m_enableRhi(false),
+ m_debugLayer(false),
+ m_profile(false),
+ m_shaderEffectDebug(false),
+ m_preferSoftwareRenderer(false)
+{
+}
+
+void QSGRhiSupport::applySettings()
+{
+ m_set = true;
+
+ // This is also done when creating the renderloop but we may be before that
+ // in case we get here due to a setScenegraphBackend() -> configure() early
+ // on in main(). Avoid losing info logs since troubleshooting gets
+ // confusing otherwise.
+ QSGRhiSupport::checkEnvQSgInfo();
+
+ if (m_requested.valid) {
+ // explicit rhi backend request from C++ (e.g. via QQuickWindow)
+ m_enableRhi = m_requested.rhi;
+ switch (m_requested.api) {
+ case QSGRendererInterface::OpenGLRhi:
+ m_rhiBackend = QRhi::OpenGLES2;
+ break;
+ case QSGRendererInterface::Direct3D11Rhi:
+ m_rhiBackend = QRhi::D3D11;
+ break;
+ case QSGRendererInterface::VulkanRhi:
+ m_rhiBackend = QRhi::Vulkan;
+ break;
+ case QSGRendererInterface::MetalRhi:
+ m_rhiBackend = QRhi::Metal;
+ break;
+ case QSGRendererInterface::NullRhi:
+ m_rhiBackend = QRhi::Null;
+ break;
+ default:
+ Q_ASSERT_X(false, "QSGRhiSupport", "Internal error: unhandled GraphicsApi type");
+ break;
+ }
+ } else {
+ // check env.vars., fall back to platform-specific defaults when backend is not set
+ m_enableRhi = uint(qEnvironmentVariableIntValue("QSG_RHI"));
+ const QByteArray rhiBackend = qgetenv("QSG_RHI_BACKEND");
+ if (rhiBackend == QByteArrayLiteral("gl")
+ || rhiBackend == QByteArrayLiteral("gles2")
+ || rhiBackend == QByteArrayLiteral("opengl"))
+ {
+ m_rhiBackend = QRhi::OpenGLES2;
+ } else if (rhiBackend == QByteArrayLiteral("d3d11") || rhiBackend == QByteArrayLiteral("d3d")) {
+ m_rhiBackend = QRhi::D3D11;
+ } else if (rhiBackend == QByteArrayLiteral("vulkan")) {
+ m_rhiBackend = QRhi::Vulkan;
+ } else if (rhiBackend == QByteArrayLiteral("metal")) {
+ m_rhiBackend = QRhi::Metal;
+ } else if (rhiBackend == QByteArrayLiteral("null")) {
+ m_rhiBackend = QRhi::Null;
+ } else {
+#if defined(Q_OS_WIN)
+ m_rhiBackend = QRhi::D3D11;
+#elif defined(Q_OS_DARWIN)
+ m_rhiBackend = QRhi::Metal;
+#else
+ m_rhiBackend = QRhi::OpenGLES2;
+#endif
+ // Vulkan has to be requested explicitly
+ }
+ }
+
+ if (!m_enableRhi)
+ return;
+
+ // validation layers (Vulkan) or debug layer (D3D)
+ m_debugLayer = uint(qEnvironmentVariableIntValue("QSG_RHI_DEBUG_LAYER"));
+
+ // EnableProfiling + DebugMarkers
+ m_profile = uint(qEnvironmentVariableIntValue("QSG_RHI_PROFILE"));
+
+ m_shaderEffectDebug = uint(qEnvironmentVariableIntValue("QSG_RHI_SHADEREFFECT_DEBUG"));
+
+ m_preferSoftwareRenderer = uint(qEnvironmentVariableIntValue("QSG_RHI_PREFER_SOFTWARE_RENDERER"));
+
+ m_killDeviceFrameCount = qEnvironmentVariableIntValue("QSG_RHI_SIMULATE_DEVICE_LOSS");
+ if (m_killDeviceFrameCount > 0 && m_rhiBackend == QRhi::D3D11)
+ qDebug("Graphics device will be reset every %d frames", m_killDeviceFrameCount);
+
+ const char *backendName = "unknown";
+ switch (m_rhiBackend) {
+ case QRhi::Null:
+ backendName = "Null";
+ break;
+ case QRhi::Vulkan:
+ backendName = "Vulkan";
+ break;
+ case QRhi::OpenGLES2:
+ backendName = "OpenGL";
+ break;
+ case QRhi::D3D11:
+ backendName = "D3D11";
+ break;
+ case QRhi::Metal:
+ backendName = "Metal";
+ break;
+ default:
+ break;
+ }
+ qCDebug(QSG_LOG_INFO,
+ "Using QRhi with backend %s\n graphics API debug/validation layers: %d\n QRhi profiling and debug markers: %d",
+ backendName, m_debugLayer, m_profile);
+ if (m_preferSoftwareRenderer)
+ qCDebug(QSG_LOG_INFO, "Prioritizing software renderers");
+}
+
+QSGRhiSupport *QSGRhiSupport::staticInst()
+{
+ static QSGRhiSupport inst;
+ return &inst;
+}
+
+void QSGRhiSupport::checkEnvQSgInfo()
+{
+ // For compatibility with 5.3 and earlier's QSG_INFO environment variables
+ if (qEnvironmentVariableIsSet("QSG_INFO"))
+ const_cast<QLoggingCategory &>(QSG_LOG_INFO()).setEnabled(QtDebugMsg, true);
+}
+
+void QSGRhiSupport::configure(QSGRendererInterface::GraphicsApi api)
+{
+ Q_ASSERT(QSGRendererInterface::isApiRhiBased(api));
+ QSGRhiSupport *inst = staticInst();
+ if (inst->m_set) {
+ qWarning("QRhi is already configured, request ignored");
+ return;
+ }
+ inst->m_requested.valid = true;
+ inst->m_requested.api = api;
+ inst->m_requested.rhi = true;
+ inst->applySettings();
+}
+
+QSGRhiSupport *QSGRhiSupport::instance()
+{
+ QSGRhiSupport *inst = staticInst();
+ if (!inst->m_set)
+ inst->applySettings();
+ return inst;
+}
+
+QSGRendererInterface::GraphicsApi QSGRhiSupport::graphicsApi() const
+{
+ if (!m_enableRhi)
+ return QSGRendererInterface::OpenGL;
+
+ switch (m_rhiBackend) {
+ case QRhi::Null:
+ return QSGRendererInterface::NullRhi;
+ case QRhi::Vulkan:
+ return QSGRendererInterface::VulkanRhi;
+ case QRhi::OpenGLES2:
+ return QSGRendererInterface::OpenGLRhi;
+ case QRhi::D3D11:
+ return QSGRendererInterface::Direct3D11Rhi;
+ case QRhi::Metal:
+ return QSGRendererInterface::MetalRhi;
+ default:
+ return QSGRendererInterface::Unknown;
+ }
+}
+
+QSurface::SurfaceType QSGRhiSupport::windowSurfaceType() const
+{
+ if (!m_enableRhi)
+ return QSurface::OpenGLSurface;
+
+ switch (m_rhiBackend) {
+ case QRhi::Vulkan:
+ return QSurface::VulkanSurface;
+ case QRhi::OpenGLES2:
+ return QSurface::OpenGLSurface;
+ case QRhi::D3D11:
+ return QSurface::OpenGLSurface; // yup, OpenGLSurface
+ case QRhi::Metal:
+ return QSurface::MetalSurface;
+ default:
+ return QSurface::OpenGLSurface;
+ }
+}
+
+#if QT_CONFIG(vulkan)
+static const void *qsgrhi_vk_rifResource(QSGRendererInterface::Resource res,
+ const QRhiNativeHandles *nat,
+ const QRhiNativeHandles *cbNat,
+ const QRhiNativeHandles *rpNat)
+{
+ const QRhiVulkanNativeHandles *vknat = static_cast<const QRhiVulkanNativeHandles *>(nat);
+ const QRhiVulkanCommandBufferNativeHandles *maybeVkCbNat =
+ static_cast<const QRhiVulkanCommandBufferNativeHandles *>(cbNat);
+ const QRhiVulkanRenderPassNativeHandles *maybeVkRpNat =
+ static_cast<const QRhiVulkanRenderPassNativeHandles *>(rpNat);
+
+ switch (res) {
+ case QSGRendererInterface::DeviceResource:
+ return &vknat->dev;
+ case QSGRendererInterface::CommandQueueResource:
+ return &vknat->gfxQueue;
+ case QSGRendererInterface::CommandListResource:
+ if (maybeVkCbNat)
+ return &maybeVkCbNat->commandBuffer;
+ else
+ return nullptr;
+ case QSGRendererInterface::PhysicalDeviceResource:
+ return &vknat->physDev;
+ case QSGRendererInterface::RenderPassResource:
+ if (maybeVkRpNat)
+ return &maybeVkRpNat->renderPass;
+ else
+ return nullptr;
+ default:
+ return nullptr;
+ }
+}
+#endif
+
+#if QT_CONFIG(opengl)
+static const void *qsgrhi_gl_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat)
+{
+ const QRhiGles2NativeHandles *glnat = static_cast<const QRhiGles2NativeHandles *>(nat);
+ switch (res) {
+ case QSGRendererInterface::OpenGLContextResource:
+ return glnat->context;
+ default:
+ return nullptr;
+ }
+}
+#endif
+
+#ifdef Q_OS_WIN
+static const void *qsgrhi_d3d11_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat)
+{
+ const QRhiD3D11NativeHandles *d3dnat = static_cast<const QRhiD3D11NativeHandles *>(nat);
+ switch (res) {
+ case QSGRendererInterface::DeviceResource:
+ return d3dnat->dev;
+ case QSGRendererInterface::DeviceContextResource:
+ return d3dnat->context;
+ default:
+ return nullptr;
+ }
+}
+#endif
+
+#ifdef Q_OS_DARWIN
+static const void *qsgrhi_mtl_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat,
+ const QRhiNativeHandles *cbNat)
+{
+ const QRhiMetalNativeHandles *mtlnat = static_cast<const QRhiMetalNativeHandles *>(nat);
+ const QRhiMetalCommandBufferNativeHandles *maybeMtlCbNat =
+ static_cast<const QRhiMetalCommandBufferNativeHandles *>(cbNat);
+
+ switch (res) {
+ case QSGRendererInterface::DeviceResource:
+ return mtlnat->dev;
+ case QSGRendererInterface::CommandQueueResource:
+ return mtlnat->cmdQueue;
+ case QSGRendererInterface::CommandListResource:
+ if (maybeMtlCbNat)
+ return maybeMtlCbNat->commandBuffer;
+ else
+ return nullptr;
+ case QSGRendererInterface::CommandEncoderResource:
+ if (maybeMtlCbNat)
+ return maybeMtlCbNat->encoder;
+ else
+ return nullptr;
+ default:
+ return nullptr;
+ }
+}
+#endif
+
+const void *QSGRhiSupport::rifResource(QSGRendererInterface::Resource res, const QSGDefaultRenderContext *rc)
+{
+ QRhi *rhi = rc->rhi();
+ if (res == QSGRendererInterface::RhiResource || !rhi)
+ return rhi;
+
+ const QRhiNativeHandles *nat = rhi->nativeHandles();
+ if (!nat)
+ return nullptr;
+
+ switch (m_rhiBackend) {
+#if QT_CONFIG(vulkan)
+ case QRhi::Vulkan:
+ {
+ QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer();
+ QRhiRenderPassDescriptor *rp = rc->currentFrameRenderPass();
+ return qsgrhi_vk_rifResource(res, nat,
+ cb ? cb->nativeHandles() : nullptr,
+ rp ? rp->nativeHandles() : nullptr);
+ }
+#endif
+#if QT_CONFIG(opengl)
+ case QRhi::OpenGLES2:
+ return qsgrhi_gl_rifResource(res, nat);
+#endif
+#ifdef Q_OS_WIN
+ case QRhi::D3D11:
+ return qsgrhi_d3d11_rifResource(res, nat);
+#endif
+#ifdef Q_OS_DARWIN
+ case QRhi::Metal:
+ {
+ QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer();
+ return qsgrhi_mtl_rifResource(res, nat, cb ? cb->nativeHandles() : nullptr);
+ }
+#endif
+ default:
+ return nullptr;
+ }
+}
+
+int QSGRhiSupport::chooseSampleCountForWindowWithRhi(QWindow *window, QRhi *rhi)
+{
+ int msaaSampleCount = qMax(QSurfaceFormat::defaultFormat().samples(), window->requestedFormat().samples());
+ if (qEnvironmentVariableIsSet("QSG_SAMPLES"))
+ msaaSampleCount = qEnvironmentVariableIntValue("QSG_SAMPLES");
+ msaaSampleCount = qMax(1, msaaSampleCount);
+ if (msaaSampleCount > 1) {
+ const QVector<int> supportedSampleCounts = rhi->supportedSampleCounts();
+ if (!supportedSampleCounts.contains(msaaSampleCount)) {
+ int reducedSampleCount = 1;
+ for (int i = supportedSampleCounts.count() - 1; i >= 0; --i) {
+ if (supportedSampleCounts[i] <= msaaSampleCount) {
+ reducedSampleCount = supportedSampleCounts[i];
+ break;
+ }
+ }
+ qWarning() << "Requested MSAA sample count" << msaaSampleCount
+ << "but supported sample counts are" << supportedSampleCounts
+ << ", using sample count" << reducedSampleCount << "instead";
+ msaaSampleCount = reducedSampleCount;
+ }
+ }
+ return msaaSampleCount;
+}
+
+// must be called on the main thread
+QOffscreenSurface *QSGRhiSupport::maybeCreateOffscreenSurface(QWindow *window)
+{
+ QOffscreenSurface *offscreenSurface = nullptr;
+#if QT_CONFIG(opengl)
+ if (rhiBackend() == QRhi::OpenGLES2) {
+ const QSurfaceFormat format = window->requestedFormat();
+ offscreenSurface = QRhiGles2InitParams::newFallbackSurface(format);
+ }
+#else
+ Q_UNUSED(window);
+#endif
+ return offscreenSurface;
+}
+
+// must be called on the render thread
+QRhi *QSGRhiSupport::createRhi(QWindow *window, QOffscreenSurface *offscreenSurface)
+{
+ QRhi *rhi = nullptr;
+
+ QRhi::Flags flags = 0;
+ if (isProfilingRequested())
+ flags |= QRhi::EnableProfiling | QRhi::EnableDebugMarkers;
+ if (isSoftwareRendererRequested())
+ flags |= QRhi::PreferSoftwareRenderer;
+
+ QRhi::Implementation backend = rhiBackend();
+ if (backend == QRhi::Null) {
+ QRhiNullInitParams rhiParams;
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ }
+#if QT_CONFIG(opengl)
+ if (backend == QRhi::OpenGLES2) {
+ const QSurfaceFormat format = window->requestedFormat();
+ QRhiGles2InitParams rhiParams;
+ rhiParams.format = format;
+ rhiParams.fallbackSurface = offscreenSurface;
+ rhiParams.window = window;
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ }
+#endif
+#if QT_CONFIG(vulkan)
+ if (backend == QRhi::Vulkan) {
+ QRhiVulkanInitParams rhiParams;
+ rhiParams.inst = window->vulkanInstance();
+ if (!rhiParams.inst)
+ qWarning("No QVulkanInstance set for QQuickWindow, this is wrong.");
+ rhiParams.window = window;
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ }
+#endif
+#ifdef Q_OS_WIN
+ if (backend == QRhi::D3D11) {
+ QRhiD3D11InitParams rhiParams;
+ rhiParams.enableDebugLayer = isDebugLayerRequested();
+ if (m_killDeviceFrameCount > 0) {
+ rhiParams.framesUntilKillingDeviceViaTdr = m_killDeviceFrameCount;
+ rhiParams.repeatDeviceKill = true;
+ }
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ }
+#endif
+#ifdef Q_OS_DARWIN
+ if (backend == QRhi::Metal) {
+ QRhiMetalInitParams rhiParams;
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ }
+#endif
+
+ if (!rhi)
+ qWarning("Failed to create RHI (backend %d)", backend);
+
+ return rhi;
+}
+
+QImage QSGRhiSupport::grabAndBlockInCurrentFrame(QRhi *rhi, QRhiSwapChain *swapchain)
+{
+ Q_ASSERT(rhi->isRecordingFrame());
+
+ QRhiReadbackResult result;
+ QRhiReadbackDescription readbackDesc; // read from swapchain backbuffer
+ QRhiResourceUpdateBatch *resourceUpdates = rhi->nextResourceUpdateBatch();
+ resourceUpdates->readBackTexture(readbackDesc, &result);
+
+ swapchain->currentFrameCommandBuffer()->resourceUpdate(resourceUpdates);
+ rhi->finish(); // make sure the readback has finished, stall the pipeline if needed
+
+ // May be RGBA or BGRA. Plus premultiplied alpha.
+ QImage::Format imageFormat;
+ if (result.format == QRhiTexture::BGRA8) {
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ imageFormat = QImage::Format_ARGB32_Premultiplied;
+#else
+ imageFormat = QImage::Format_RGBA8888_Premultiplied;
+ // ### and should swap too
+#endif
+ } else {
+ imageFormat = QImage::Format_RGBA8888_Premultiplied;
+ }
+
+ const uchar *p = reinterpret_cast<const uchar *>(result.data.constData());
+ const QImage img(p, result.pixelSize.width(), result.pixelSize.height(), imageFormat);
+
+ if (rhi->isYUpInFramebuffer())
+ return img.mirrored();
+
+ return img.copy();
+}
+
+QSGRhiProfileConnection *QSGRhiProfileConnection::instance()
+{
+ static QSGRhiProfileConnection inst;
+ return &inst;
+}
+
+void QSGRhiProfileConnection::initialize(QRhi *rhi)
+{
+#ifdef RHI_REMOTE_PROFILER
+ const QString profHost = qEnvironmentVariable("QSG_RHI_PROFILE_HOST");
+ if (!profHost.isEmpty()) {
+ int profPort = qEnvironmentVariableIntValue("QSG_RHI_PROFILE_PORT");
+ if (!profPort)
+ profPort = 30667;
+ qCDebug(QSG_LOG_INFO, "Sending RHI profiling output to %s:%d", qPrintable(profHost), profPort);
+ m_profConn.reset(new QTcpSocket);
+ QObject::connect(m_profConn.data(), QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), m_profConn.data(),
+ [this](QAbstractSocket::SocketError socketError) { qWarning(" RHI profiler error: %d (%s)",
+ socketError, qPrintable(m_profConn->errorString())); });
+ m_profConn->connectToHost(profHost, profPort);
+ m_profConn->waitForConnected(); // blocking wait because we want to send stuff already from the init below
+ rhi->profiler()->setDevice(m_profConn.data());
+ m_lastMemStatWrite.start();
+ }
+#else
+ Q_UNUSED(rhi);
+#endif
+}
+
+void QSGRhiProfileConnection::cleanup()
+{
+#ifdef RHI_REMOTE_PROFILER
+ m_profConn.reset();
+#endif
+}
+
+void QSGRhiProfileConnection::send(QRhi *rhi)
+{
+#ifdef RHI_REMOTE_PROFILER
+ if (m_profConn) {
+ // do this every 5 sec at most
+ if (m_lastMemStatWrite.elapsed() >= 5000) {
+ rhi->profiler()->addVMemAllocatorStats();
+ m_lastMemStatWrite.restart();
+ }
+ }
+#else
+ Q_UNUSED(rhi);
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgrhisupport_p.h b/src/quick/scenegraph/qsgrhisupport_p.h
new file mode 100644
index 0000000000..d008ecd0af
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhisupport_p.h
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 QSGRHISUPPORT_P_H
+#define QSGRHISUPPORT_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 "qsgrenderloop_p.h"
+#include "qsgrendererinterface.h"
+
+#include <QtGui/private/qrhi_p.h>
+
+#include <QtGui/private/qrhinull_p.h>
+
+#if QT_CONFIG(opengl)
+#include <QtGui/private/qrhigles2_p.h>
+#endif
+
+#if QT_CONFIG(vulkan)
+#include <QtGui/private/qrhivulkan_p.h>
+#endif
+
+#ifdef Q_OS_WIN
+#include <QtGui/private/qrhid3d11_p.h>
+#endif
+
+#ifdef Q_OS_DARWIN
+#include <QtGui/private/qrhimetal_p.h>
+#endif
+
+#if QT_CONFIG(qml_network)
+#define RHI_REMOTE_PROFILER
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtNetwork/qtcpsocket.h>
+#include <QtGui/private/qrhiprofiler_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QSGDefaultRenderContext;
+class QVulkanInstance;
+class QOffscreenSurface;
+
+// Opting in/out of QRhi and choosing the default/requested backend is managed
+// by this singleton. This is because this information may be needed before
+// creating a render loop. A well-written render loop sets up its QRhi and
+// related machinery based on the settings queriable from 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.
+//
+class QSGRhiSupport
+{
+public:
+ static void configure(QSGRendererInterface::GraphicsApi api);
+ static QSGRhiSupport *instance();
+ static QVulkanInstance *vulkanInstance();
+ void cleanup();
+
+ bool isRhiEnabled() const { return m_enableRhi; }
+ QRhi::Implementation rhiBackend() const { return m_rhiBackend; }
+ QSGRendererInterface::GraphicsApi graphicsApi() const;
+
+ bool isDebugLayerRequested() const { return m_debugLayer; }
+ bool isProfilingRequested() const { return m_profile; }
+ bool isShaderEffectDebuggingRequested() const { return m_shaderEffectDebug; }
+ bool isSoftwareRendererRequested() const { return m_preferSoftwareRenderer; }
+
+ QSurface::SurfaceType windowSurfaceType() const;
+
+ const void *rifResource(QSGRendererInterface::Resource res, const QSGDefaultRenderContext *rc);
+
+ int chooseSampleCountForWindowWithRhi(QWindow *window, QRhi *rhi);
+
+ QOffscreenSurface *maybeCreateOffscreenSurface(QWindow *window);
+ QRhi *createRhi(QWindow *window, QOffscreenSurface *offscreenSurface);
+
+ QImage grabAndBlockInCurrentFrame(QRhi *rhi, QRhiSwapChain *swapchain);
+
+ static void checkEnvQSgInfo();
+
+private:
+ QSGRhiSupport();
+ void applySettings();
+ static QSGRhiSupport *staticInst();
+ struct {
+ bool valid = false;
+ QSGRendererInterface::GraphicsApi api;
+ uint rhi : 1;
+ } m_requested;
+ QRhi::Implementation m_rhiBackend = QRhi::Null;
+ int m_killDeviceFrameCount;
+ uint m_set : 1;
+ uint m_enableRhi : 1;
+ uint m_debugLayer : 1;
+ uint m_profile : 1;
+ uint m_shaderEffectDebug : 1;
+ uint m_preferSoftwareRenderer : 1;
+};
+
+// Sends QRhi resource statistics over a QTcpSocket. To be initialized by the
+// renderloop when QSGRhiSupport::isProfilingRequested() is true. Will not do
+// anything unless extra env vars (QSG_RHI_PROFILE_HOST) are set.
+class QSGRhiProfileConnection
+{
+public:
+ static QSGRhiProfileConnection *instance();
+ void initialize(QRhi *rhi);
+ void cleanup();
+ void send(QRhi *rhi);
+
+private:
+#ifdef RHI_REMOTE_PROFILER
+ QScopedPointer<QTcpSocket> m_profConn;
+ QElapsedTimer m_lastMemStatWrite;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGRHISUPPORT_P_H
diff --git a/src/quick/scenegraph/qsgrhitextureglyphcache.cpp b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
new file mode 100644
index 0000000000..d0108bc56e
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
@@ -0,0 +1,272 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "qsgrhitextureglyphcache_p.h"
+#include <qrgb.h>
+#include <private/qdrawhelper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGRhiTextureGlyphCache::QSGRhiTextureGlyphCache(QRhi *rhi, QFontEngine::GlyphFormat format, const QTransform &matrix,
+ const QColor &color)
+ : QImageTextureGlyphCache(format, matrix, color),
+ m_rhi(rhi)
+{
+ // Some OpenGL implementations, for instance macOS, have issues with
+ // GL_ALPHA render targets. Similarly, BGRA may be problematic on GLES 2.0.
+ // So stick with plain image uploads on GL.
+ m_resizeWithTextureCopy = m_rhi->backend() != QRhi::OpenGLES2;
+}
+
+QSGRhiTextureGlyphCache::~QSGRhiTextureGlyphCache()
+{
+ if (m_resourceUpdates)
+ m_resourceUpdates->release();
+
+ delete m_texture;
+
+ // should be empty, but just in case
+ qDeleteAll(m_pendingDispose);
+}
+
+QRhiTexture *QSGRhiTextureGlyphCache::createEmptyTexture(QRhiTexture::Format format)
+{
+ QRhiTexture *t = m_rhi->newTexture(format, m_size, 1, QRhiTexture::UsedAsTransferSource);
+ if (!t->build()) {
+ qWarning("Failed to build new glyph cache texture of size %dx%d", m_size.width(), m_size.height());
+ return nullptr;
+ }
+
+ if (!m_resourceUpdates)
+ m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
+
+ // The new texture must be cleared to 0 always, this cannot be avoided
+ // otherwise artifacts will occur around the glyphs.
+ QByteArray data;
+ if (format == QRhiTexture::RED_OR_ALPHA8)
+ data.fill(0, m_size.width() * m_size.height());
+ else
+ data.fill(0, m_size.width() * m_size.height() * 4);
+ QRhiTextureSubresourceUploadDescription subresDesc(data.constData(), data.size());
+ subresDesc.setSourceSize(m_size);
+ m_resourceUpdates->uploadTexture(t, QRhiTextureUploadEntry(0, 0, subresDesc));
+
+ return t;
+}
+
+void QSGRhiTextureGlyphCache::createTextureData(int width, int height)
+{
+ width = qMax(128, width);
+ height = qMax(32, height);
+
+ if (!m_resizeWithTextureCopy)
+ QImageTextureGlyphCache::createTextureData(width, height);
+
+ m_size = QSize(width, height);
+}
+
+void QSGRhiTextureGlyphCache::resizeTextureData(int width, int height)
+{
+ width = qMax(128, width);
+ height = qMax(32, height);
+
+ if (m_size.width() >= width && m_size.height() >= height)
+ return;
+
+ m_size = QSize(width, height);
+
+ if (m_texture) {
+ QRhiTexture *t = createEmptyTexture(m_texture->format());
+ if (!t)
+ return;
+
+ if (!m_resourceUpdates)
+ m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
+
+ if (m_resizeWithTextureCopy) {
+ m_resourceUpdates->copyTexture(t, m_texture);
+ } else {
+ QImageTextureGlyphCache::resizeTextureData(width, height);
+ QImage img = image();
+ prepareGlyphImage(&img);
+ QRhiTextureSubresourceUploadDescription subresDesc(img);
+ const QSize oldSize = m_texture->pixelSize();
+ subresDesc.setSourceSize(QSize(qMin(oldSize.width(), width), qMin(oldSize.height(), height)));
+ m_resourceUpdates->uploadTexture(t, QRhiTextureUploadEntry(0, 0, subresDesc));
+ }
+
+ m_pendingDispose.insert(m_texture);
+ m_texture = t;
+ }
+}
+
+void QSGRhiTextureGlyphCache::beginFillTexture()
+{
+ Q_ASSERT(m_uploads.isEmpty());
+}
+
+void QSGRhiTextureGlyphCache::prepareGlyphImage(QImage *img)
+{
+ const int maskWidth = img->width();
+ const int maskHeight = img->height();
+ const bool supportsBgra = m_rhi->isTextureFormatSupported(QRhiTexture::BGRA8);
+ m_bgra = false;
+
+ if (img->format() == QImage::Format_Mono) {
+ *img = img->convertToFormat(QImage::Format_Grayscale8);
+ } else if (img->depth() == 32) {
+ if (img->format() == QImage::Format_RGB32 || img->format() == QImage::Format_ARGB32_Premultiplied) {
+ // We need to make the alpha component equal to the average of the RGB values.
+ // This is needed when drawing sub-pixel antialiased text on translucent targets.
+ for (int y = 0; y < maskHeight; ++y) {
+ QRgb *src = (QRgb *) img->scanLine(y);
+ for (int x = 0; x < maskWidth; ++x) {
+ int r = qRed(src[x]);
+ int g = qGreen(src[x]);
+ int b = qBlue(src[x]);
+ int avg;
+ if (img->format() == QImage::Format_RGB32)
+ avg = (r + g + b + 1) / 3; // "+1" for rounding.
+ else // Format_ARGB_Premultiplied
+ avg = qAlpha(src[x]);
+
+ src[x] = qRgba(r, g, b, avg);
+#if Q_BYTE_ORDER != Q_BIG_ENDIAN
+ if (supportsBgra) {
+ m_bgra = true;
+ } else {
+ // swizzle the bits to accommodate for the RGBA upload.
+ src[x] = ARGB2RGBA(src[x]);
+ m_bgra = false;
+ }
+#endif
+ }
+ }
+ }
+ }
+}
+
+void QSGRhiTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition)
+{
+ QRhiTextureSubresourceUploadDescription subresDesc;
+ QImage mask;
+
+ if (!m_resizeWithTextureCopy) {
+ QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition);
+ mask = image();
+ subresDesc.setSourceTopLeft(QPoint(c.x, c.y));
+ subresDesc.setSourceSize(QSize(c.w, c.h));
+ } else {
+ mask = textureMapForGlyph(glyph, subPixelPosition);
+ }
+
+ prepareGlyphImage(&mask);
+
+ subresDesc.setImage(mask);
+ subresDesc.setDestinationTopLeft(QPoint(c.x, c.y));
+ m_uploads.append(QRhiTextureUploadEntry(0, 0, subresDesc));
+}
+
+void QSGRhiTextureGlyphCache::endFillTexture()
+{
+ if (m_uploads.isEmpty())
+ return;
+
+ if (!m_texture) {
+ QRhiTexture::Format texFormat;
+ if (m_format == QFontEngine::Format_A32 || m_format == QFontEngine::Format_ARGB)
+ texFormat = m_bgra ? QRhiTexture::BGRA8 : QRhiTexture::RGBA8;
+ else // should be R8, but there is the OpenGL ES 2.0 nonsense
+ texFormat = QRhiTexture::RED_OR_ALPHA8;
+
+ m_texture = createEmptyTexture(texFormat);
+ if (!m_texture)
+ return;
+ }
+
+ if (!m_resourceUpdates)
+ m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
+
+ QRhiTextureUploadDescription desc;
+ desc.setEntries(m_uploads.cbegin(), m_uploads.cend());
+ m_resourceUpdates->uploadTexture(m_texture, desc);
+ m_uploads.clear();
+}
+
+int QSGRhiTextureGlyphCache::glyphPadding() const
+{
+ return 1;
+}
+
+int QSGRhiTextureGlyphCache::maxTextureWidth() const
+{
+ return m_rhi->resourceLimit(QRhi::TextureSizeMax);
+}
+
+int QSGRhiTextureGlyphCache::maxTextureHeight() const
+{
+ if (!m_resizeWithTextureCopy)
+ return qMin(1024, m_rhi->resourceLimit(QRhi::TextureSizeMax));
+
+ return m_rhi->resourceLimit(QRhi::TextureSizeMax);
+}
+
+void QSGRhiTextureGlyphCache::commitResourceUpdates(QRhiResourceUpdateBatch *mergeInto)
+{
+ if (m_resourceUpdates) {
+ mergeInto->merge(m_resourceUpdates);
+ m_resourceUpdates->release();
+ m_resourceUpdates = nullptr;
+ }
+
+ // now let's assume the resource updates will be committed in this frame
+ for (QRhiTexture *t : m_pendingDispose)
+ t->releaseAndDestroyLater(); // will be releaseAndDestroyed after the frame is submitted -> safe
+
+ m_pendingDispose.clear();
+}
+
+bool QSGRhiTextureGlyphCache::eightBitFormatIsAlphaSwizzled() const
+{
+ // return true when the shaders for 8-bit formats need .a instead of .r
+ // when sampling the texture
+ return !m_rhi->isFeatureSupported(QRhi::RedOrAlpha8IsRed);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgrhitextureglyphcache_p.h b/src/quick/scenegraph/qsgrhitextureglyphcache_p.h
new file mode 100644
index 0000000000..49854a596e
--- /dev/null
+++ b/src/quick/scenegraph/qsgrhitextureglyphcache_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 QSGRHITEXTUREGLYPHCACHE_P_H
+#define QSGRHITEXTUREGLYPHCACHE_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 <QtGui/private/qtextureglyphcache_p.h>
+#include <QtGui/private/qrhi_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGRhiTextureGlyphCache : public QImageTextureGlyphCache
+{
+public:
+ QSGRhiTextureGlyphCache(QRhi *rhi, QFontEngine::GlyphFormat format, const QTransform &matrix,
+ const QColor &color = QColor());
+ ~QSGRhiTextureGlyphCache();
+
+ void createTextureData(int width, int height) override;
+ void resizeTextureData(int width, int height) override;
+ void beginFillTexture() override;
+ void fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition) override;
+ void endFillTexture() override;
+ int glyphPadding() const override;
+ int maxTextureWidth() const override;
+ int maxTextureHeight() const override;
+
+ QRhiTexture *texture() const { return m_texture; }
+ void commitResourceUpdates(QRhiResourceUpdateBatch *mergeInto);
+
+ // Clamp the default -1 width and height to 0 for compatibility with
+ // QOpenGLTextureGlyphCache.
+ int width() const { return qMax(0, m_size.width()); }
+ int height() const { return qMax(0, m_size.height()); }
+
+ bool eightBitFormatIsAlphaSwizzled() const;
+
+private:
+ void prepareGlyphImage(QImage *img);
+ QRhiTexture *createEmptyTexture(QRhiTexture::Format format);
+
+ QRhi *m_rhi;
+ bool m_resizeWithTextureCopy;
+ QRhiResourceUpdateBatch *m_resourceUpdates = nullptr;
+ QRhiTexture *m_texture = nullptr;
+ QSize m_size;
+ bool m_bgra = false;
+ QVarLengthArray<QRhiTextureUploadEntry, 16> m_uploads;
+ QSet<QRhiTexture *> m_pendingDispose;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGRHITEXTUREGLYPHCACHE_P_H
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index 232ef77324..dc1f97de54 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -57,6 +57,7 @@
#include <QtQuick/private/qsgrenderer_p.h>
#include "qsgthreadedrenderloop_p.h"
+#include "qsgrhisupport_p.h"
#include <private/qquickanimatorcontroller_p.h>
#include <private/qquickprofiler_p.h>
@@ -66,6 +67,7 @@
#if QT_CONFIG(quick_shadereffect)
#include <private/qquickopenglshadereffectnode_p.h>
#endif
+#include <private/qsgrhishadereffectnode_p.h>
#include <private/qsgdefaultrendercontext_p.h>
/*
@@ -145,10 +147,6 @@ const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 1);
// (updatePaintNode())
const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2);
-// Passed by the RT to itself to trigger another render pass. This is
-// typically a result of QQuickWindow::update().
-const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 3);
-
// Passed by the RL to the RT to free up maybe release SG and GL contexts
// if no windows are rendering.
const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4);
@@ -160,6 +158,10 @@ const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5);
// Passed by the window when there is a render job to run
const QEvent::Type WM_PostJob = QEvent::Type(QEvent::User + 6);
+// When using the QRhi this is sent upon PlatformSurfaceAboutToBeDestroyed from
+// the event filter installed on the QQuickWindow.
+const QEvent::Type WM_ReleaseSwapchain = QEvent::Type(QEvent::User + 7);
+
template <typename T> T *windowFor(const QList<T> &list, QQuickWindow *window)
{
for (int i=0; i<list.size(); ++i) {
@@ -181,14 +183,14 @@ public:
class WMTryReleaseEvent : public WMWindowEvent
{
public:
- WMTryReleaseEvent(QQuickWindow *win, bool destroy, QOffscreenSurface *fallback)
+ WMTryReleaseEvent(QQuickWindow *win, bool destroy, bool needsFallbackSurface)
: WMWindowEvent(win, WM_TryRelease)
, inDestructor(destroy)
- , fallbackSurface(fallback)
+ , needsFallback(needsFallbackSurface)
{}
bool inDestructor;
- QOffscreenSurface *fallbackSurface;
+ bool needsFallback;
};
class WMSyncEvent : public WMWindowEvent
@@ -197,10 +199,12 @@ public:
WMSyncEvent(QQuickWindow *c, bool inExpose, bool force)
: WMWindowEvent(c, WM_RequestSync)
, size(c->size())
+ , dpr(float(c->effectiveDevicePixelRatio()))
, syncInExpose(inExpose)
, forceRenderPass(force)
{}
QSize size;
+ float dpr;
bool syncInExpose;
bool forceRenderPass;
};
@@ -222,6 +226,12 @@ public:
QRunnable *job;
};
+class WMReleaseSwapchainEvent : public WMWindowEvent
+{
+public:
+ WMReleaseSwapchainEvent(QQuickWindow *c) : WMWindowEvent(c, WM_ReleaseSwapchain) { }
+};
+
class QSGRenderThreadEventQueue : public QQueue<QEvent *>
{
public:
@@ -271,6 +281,9 @@ public:
QSGRenderThread(QSGThreadedRenderLoop *w, QSGRenderContext *renderContext)
: wm(w)
, gl(nullptr)
+ , enableRhi(false)
+ , rhi(nullptr)
+ , offscreenSurface(nullptr)
, animatorDriver(nullptr)
, pendingUpdate(0)
, sleeping(false)
@@ -290,16 +303,16 @@ public:
~QSGRenderThread()
{
delete sgrc;
+ delete offscreenSurface;
}
- void invalidateOpenGL(QQuickWindow *window, bool inDestructor, QOffscreenSurface *backupSurface);
- void initializeOpenGL();
+ void invalidateGraphics(QQuickWindow *window, bool inDestructor, QOffscreenSurface *backupSurface);
bool event(QEvent *) override;
void run() override;
- void syncAndRender();
- void sync(bool inExpose);
+ void syncAndRender(QImage *grabImage = nullptr);
+ void sync(bool inExpose, bool inGrab);
void requestRepaint()
{
@@ -326,9 +339,15 @@ public:
ExposeRequest = 0x04 | RepaintRequest | SyncRequest
};
+ void ensureRhi();
+ void handleDeviceLoss();
+
QSGThreadedRenderLoop *wm;
QOpenGLContext *gl;
+ bool enableRhi;
+ QRhi *rhi;
QSGDefaultRenderContext *sgrc;
+ QOffscreenSurface *offscreenSurface;
QAnimationDriver *animatorDriver;
@@ -347,6 +366,8 @@ public:
QQuickWindow *window; // Will be 0 when window is not exposed
QSize windowSize;
+ float dpr = 1;
+ int rhiSampleCount = 1;
// Local event queue stuff...
bool stopEventProcessing;
@@ -380,6 +401,7 @@ bool QSGRenderThread::event(QEvent *e)
stopEventProcessing = true;
window = se->window;
windowSize = se->size;
+ dpr = se->dpr;
pendingUpdate |= SyncRequest;
if (se->syncInExpose) {
@@ -399,9 +421,9 @@ bool QSGRenderThread::event(QEvent *e)
WMTryReleaseEvent *wme = static_cast<WMTryReleaseEvent *>(e);
if (!window || wme->inDestructor) {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- setting exit flag and invalidating OpenGL");
- invalidateOpenGL(wme->window, wme->inDestructor, wme->fallbackSurface);
- active = gl;
- Q_ASSERT_X(!wme->inDestructor || !active, "QSGRenderThread::invalidateOpenGL()", "Thread's active state is not set to false when shutting down");
+ invalidateGraphics(wme->window, wme->inDestructor, wme->needsFallback ? offscreenSurface : nullptr);
+ active = gl || rhi;
+ Q_ASSERT_X(!wme->inDestructor || !active, "QSGRenderThread::invalidateGraphics()", "Thread's active state is not set to false when shutting down");
if (sleeping)
stopEventProcessing = true;
} else {
@@ -427,19 +449,25 @@ bool QSGRenderThread::event(QEvent *e)
Q_ASSERT(ce->window == window || !window);
mutex.lock();
if (ce->window) {
- gl->makeCurrent(ce->window);
-
- qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- sync scene graph");
- QQuickWindowPrivate *d = QQuickWindowPrivate::get(ce->window);
- d->syncSceneGraph();
- sgrc->endSync();
-
- qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- rendering scene graph");
- QQuickWindowPrivate::get(ce->window)->renderSceneGraph(ce->window->size());
-
- qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- grabbing result");
- bool alpha = ce->window->format().alphaBufferSize() > 0 && ce->window->color().alpha() != 255;
- *ce->image = qt_gl_read_framebuffer(windowSize * ce->window->effectiveDevicePixelRatio(), alpha, alpha);
+ const bool alpha = ce->window->format().alphaBufferSize() > 0 && ce->window->color().alpha() != 255;
+ const QSize readbackSize = windowSize * ce->window->effectiveDevicePixelRatio();
+ if (rhi) {
+ rhi->makeThreadLocalNativeContextCurrent();
+ syncAndRender(ce->image);
+ } else {
+ gl->makeCurrent(ce->window);
+
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- sync scene graph");
+ QQuickWindowPrivate *d = QQuickWindowPrivate::get(ce->window);
+ d->syncSceneGraph();
+ sgrc->endSync();
+
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- rendering scene graph");
+ QQuickWindowPrivate::get(ce->window)->renderSceneGraph(ce->window->size());
+
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- grabbing result");
+ *ce->image = qt_gl_read_framebuffer(readbackSize, alpha, alpha);
+ }
ce->image->setDevicePixelRatio(ce->window->effectiveDevicePixelRatio());
}
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- waking gui to handle result");
@@ -453,7 +481,10 @@ bool QSGRenderThread::event(QEvent *e)
WMJobEvent *ce = static_cast<WMJobEvent *>(e);
Q_ASSERT(ce->window == window);
if (window) {
- gl->makeCurrent(window);
+ if (rhi)
+ rhi->makeThreadLocalNativeContextCurrent();
+ else
+ gl->makeCurrent(window);
ce->job->run();
delete ce->job;
ce->job = nullptr;
@@ -462,12 +493,20 @@ bool QSGRenderThread::event(QEvent *e)
return true;
}
- case WM_RequestRepaint:
- qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "WM_RequestPaint");
- // When GUI posts this event, it is followed by a polishAndSync, so we mustn't
- // exit the event loop yet.
- pendingUpdate |= RepaintRequest;
- break;
+ case WM_ReleaseSwapchain: {
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "WM_ReleaseSwapchain");
+ WMReleaseSwapchainEvent *ce = static_cast<WMReleaseSwapchainEvent *>(e);
+ // forget about 'window' here that may be null when already unexposed
+ Q_ASSERT(ce->window);
+ mutex.lock();
+ if (ce->window) {
+ wm->releaseSwapchain(ce->window);
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- swapchain released");
+ }
+ waitCondition.wakeOne();
+ mutex.unlock();
+ return true;
+ }
default:
break;
@@ -475,11 +514,11 @@ bool QSGRenderThread::event(QEvent *e)
return QThread::event(e);
}
-void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor, QOffscreenSurface *fallback)
+void QSGRenderThread::invalidateGraphics(QQuickWindow *window, bool inDestructor, QOffscreenSurface *fallback)
{
- qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "invalidateOpenGL()");
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "invalidateGraphics()");
- if (!gl)
+ if (!gl && !rhi)
return;
if (!window) {
@@ -491,7 +530,12 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor,
bool wipeSG = inDestructor || !window->isPersistentSceneGraph();
bool wipeGL = inDestructor || (wipeSG && !window->isPersistentOpenGLContext());
- bool current = gl->makeCurrent(fallback ? static_cast<QSurface *>(fallback) : static_cast<QSurface *>(window));
+ bool current = true;
+ if (gl)
+ current = gl->makeCurrent(fallback ? static_cast<QSurface *>(fallback) : static_cast<QSurface *>(window));
+ else if (rhi)
+ rhi->makeThreadLocalNativeContextCurrent();
+
if (Q_UNLIKELY(!current)) {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- cleanup without an OpenGL context");
}
@@ -499,16 +543,19 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor,
QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window);
#if QT_CONFIG(quick_shadereffect)
+ QSGRhiShaderEffectNode::cleanupMaterialTypeCache();
+#if QT_CONFIG(opengl)
if (current)
QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
#endif
+#endif
// The canvas nodes must be cleaned up regardless if we are in the destructor..
if (wipeSG) {
dd->cleanupNodesOnShutdown();
} else {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- persistent SG, avoiding cleanup");
- if (current)
+ if (current && gl)
gl->doneCurrent();
return;
}
@@ -517,14 +564,26 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor,
QCoreApplication::processEvents();
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
if (inDestructor)
- delete dd->animationController;
- if (current)
+ dd->animationController.reset();
+ if (current && gl)
gl->doneCurrent();
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- invalidating scene graph");
if (wipeGL) {
+ if (dd->swapchain) {
+ if (window->handle()) {
+ // We get here when exiting via QCoreApplication::quit() instead of
+ // through QWindow::close().
+ wm->releaseSwapchain(window);
+ } else {
+ qWarning("QSGThreadedRenderLoop cleanup with QQuickWindow %p swapchain %p still alive, this should not happen.",
+ window, dd->swapchain);
+ }
+ }
delete gl;
gl = nullptr;
+ delete rhi;
+ rhi = nullptr;
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- invalidated OpenGL");
} else {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- persistent GL, avoiding cleanup");
@@ -535,23 +594,41 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor,
Enters the mutex lock to make sure GUI is blocking and performs
sync, then wakes GUI.
*/
-void QSGRenderThread::sync(bool inExpose)
+void QSGRenderThread::sync(bool inExpose, bool inGrab)
{
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "sync()");
- mutex.lock();
+ if (!inGrab)
+ mutex.lock();
Q_ASSERT_X(wm->m_lockedForSync, "QSGRenderThread::sync()", "sync triggered on bad terms as gui is not already locked...");
- bool current = false;
- if (windowSize.width() > 0 && windowSize.height() > 0)
- current = gl->makeCurrent(window);
- // Check for context loss.
- if (!current && !gl->isValid()) {
- QQuickWindowPrivate::get(window)->cleanupNodesOnShutdown();
- sgrc->invalidate();
- current = gl->create() && gl->makeCurrent(window);
- if (current)
- sgrc->initialize(gl);
+ bool current = true;
+ if (gl) {
+ if (windowSize.width() > 0 && windowSize.height() > 0)
+ current = gl->makeCurrent(window);
+ else
+ current = false;
+ // Check for context loss.
+ if (!current && !gl->isValid()) {
+ QQuickWindowPrivate::get(window)->cleanupNodesOnShutdown();
+ sgrc->invalidate();
+ current = gl->create() && gl->makeCurrent(window);
+ if (current) {
+ QSGDefaultRenderContext::InitParams rcParams;
+ rcParams.sampleCount = qMax(1, gl->format().samples());
+ rcParams.openGLContext = gl;
+ rcParams.initialSurfacePixelSize = windowSize * qreal(dpr);
+ rcParams.maybeSurface = window;
+ sgrc->initialize(&rcParams);
+ }
+ }
+ } else {
+ // With the rhi making the (OpenGL) context current serves only one
+ // purpose: to enable external OpenGL rendering connected to one of
+ // the QQuickWindow signals (beforeSynchronizing, beforeRendering,
+ // etc.) to function like it did on the direct OpenGL path. For our
+ // own rendering this call would not be necessary.
+ rhi->makeThreadLocalNativeContextCurrent();
}
if (current) {
QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
@@ -576,14 +653,27 @@ void QSGRenderThread::sync(bool inExpose)
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- window has bad size, sync aborted");
}
- if (!inExpose) {
+ if (!inExpose && !inGrab) {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- sync complete, waking Gui");
waitCondition.wakeOne();
mutex.unlock();
}
}
-void QSGRenderThread::syncAndRender()
+void QSGRenderThread::handleDeviceLoss()
+{
+ if (!rhi || !rhi->isDeviceLost())
+ return;
+
+ qWarning("Graphics device lost, cleaning up scenegraph and releasing RHI");
+ QQuickWindowPrivate::get(window)->cleanupNodesOnShutdown();
+ sgrc->invalidate();
+ wm->releaseSwapchain(window);
+ delete rhi;
+ rhi = nullptr;
+}
+
+void QSGRenderThread::syncAndRender(QImage *grabImage)
{
bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
if (profileFrames) {
@@ -600,14 +690,78 @@ void QSGRenderThread::syncAndRender()
syncResultedInChanges = false;
QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
- bool repaintRequested = (pendingUpdate & RepaintRequest) || d->customRenderStage;
- bool syncRequested = pendingUpdate & SyncRequest;
- bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
+ const bool repaintRequested = (pendingUpdate & RepaintRequest) || d->customRenderStage || grabImage;
+ const bool syncRequested = (pendingUpdate & SyncRequest) || grabImage;
+ const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
pendingUpdate = 0;
+ const bool grabRequested = grabImage != nullptr;
+
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
+ // Begin the frame before syncing -> sync is where we may invoke
+ // updatePaintNode() on the items and they may want to do resource updates.
+ // Also relevant for applications that connect to the before/afterSynchronizing
+ // signals and want to do graphics stuff already there.
+ if (cd->swapchain && windowSize.width() > 0 && windowSize.height() > 0) {
+ // always prefer what the surface tells us, not the QWindow
+ const QSize effectiveOutputSize = cd->swapchain->surfacePixelSize();
+ // An update request could still be delivered right before we get an
+ // unexpose. With Vulkan on Windows for example attempting to render
+ // leads to failures at this stage since the surface size is already 0.
+ if (effectiveOutputSize.isEmpty())
+ return;
+
+ const QSize previousOutputSize = cd->swapchain->currentPixelSize();
+ if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable) {
+ if (cd->swapchainJustBecameRenderable)
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "just became exposed");
+
+ cd->hasActiveSwapchain = cd->swapchain->buildOrResize();
+ if (!cd->hasActiveSwapchain && rhi->isDeviceLost()) {
+ handleDeviceLoss();
+ QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
+ return;
+ }
+
+ cd->swapchainJustBecameRenderable = false;
+ cd->hasRenderableSwapchain = cd->hasActiveSwapchain;
+
+ if (!cd->hasActiveSwapchain)
+ qWarning("Failed to build or resize swapchain");
+ else
+ qCDebug(QSG_LOG_RENDERLOOP) << "rhi swapchain size" << cd->swapchain->currentPixelSize();
+ }
+
+ Q_ASSERT(rhi == cd->rhi);
+ // ### the flag should only be set when the app requests it, but there's no way to do that right now
+ QRhi::BeginFrameFlags frameFlags = QRhi::ExternalContentsInPass;
+ QRhi::FrameOpResult frameResult = rhi->beginFrame(cd->swapchain, frameFlags);
+ if (frameResult != QRhi::FrameOpSuccess) {
+ if (frameResult == QRhi::FrameOpDeviceLost)
+ handleDeviceLoss();
+ else if (frameResult == QRhi::FrameOpError)
+ qWarning("Failed to start frame");
+ // try again later
+ if (frameResult == QRhi::FrameOpDeviceLost || frameResult == QRhi::FrameOpSwapChainOutOfDate)
+ QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
+ // Before returning we need to ensure the same wake up logic that
+ // would have happened if beginFrame() had suceeded.
+ if (exposeRequested) {
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- bailing out due to failed beginFrame, wake Gui");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ } else if (syncRequested && !grabRequested) {
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- bailing out due to failed beginFrame, wake Gui like sync would do");
+ mutex.lock();
+ waitCondition.wakeOne();
+ mutex.unlock();
+ }
+ return;
+ }
+ }
if (syncRequested) {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- updatePending, doing sync");
- sync(exposeRequested);
+ sync(exposeRequested, grabRequested);
}
#ifndef QSG_NO_RENDER_TIMING
if (profileFrames)
@@ -616,41 +770,80 @@ void QSGRenderThread::syncAndRender()
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopSync);
- if (!syncResultedInChanges && !repaintRequested && sgrc->isValid()) {
- qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- no changes, render aborted");
- int waitTime = vsyncDelta - (int) waitTimer.elapsed();
- if (waitTime > 0)
- msleep(waitTime);
- return;
+ if (!syncResultedInChanges && !repaintRequested && sgrc->isValid() && !grabImage) {
+ if (gl || !rhi->isRecordingFrame()) {
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- no changes, render aborted");
+ int waitTime = vsyncDelta - (int) waitTimer.elapsed();
+ if (waitTime > 0)
+ msleep(waitTime);
+ return;
+ }
}
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- rendering started");
- if (animatorDriver->isRunning()) {
+ if (animatorDriver->isRunning() && !grabImage) {
d->animationController->lock();
animatorDriver->advance();
d->animationController->unlock();
}
- bool current = false;
- if (d->renderer && windowSize.width() > 0 && windowSize.height() > 0)
- current = gl->makeCurrent(window);
- // Check for context loss.
- if (!current && !gl->isValid()) {
- // Cannot do anything here because gui is not locked. Request a new
- // sync+render round on the gui thread and let the sync handle it.
- QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
+ bool current = true;
+ if (d->renderer && windowSize.width() > 0 && windowSize.height() > 0) {
+ if (gl)
+ current = gl->makeCurrent(window);
+ else if (rhi)
+ rhi->makeThreadLocalNativeContextCurrent();
+ } else {
+ current = false;
+ }
+ // Check for context loss (GL, RHI case handled after the beginFrame() above)
+ if (gl) {
+ if (!current && !gl->isValid()) {
+ // Cannot do anything here because gui is not locked. Request a new
+ // sync+render round on the gui thread and let the sync handle it.
+ QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
+ }
}
if (current) {
- d->renderSceneGraph(windowSize);
+ d->renderSceneGraph(windowSize, rhi ? cd->swapchain->currentPixelSize() : QSize());
+
if (profileFrames)
renderTime = threadTimer.nsecsElapsed();
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopRender);
- if (!d->customRenderStage || !d->customRenderStage->swap())
- gl->swapBuffers(window);
- d->fireFrameSwapped();
+
+ // With the rhi grabs can only be done by adding a readback and then
+ // blocking in a real frame. The legacy GL path never gets here with
+ // grabs as it rather invokes sync/render directly without going
+ // through syncAndRender().
+ if (grabImage) {
+ Q_ASSERT(rhi && !gl && cd->swapchain);
+ *grabImage = QSGRhiSupport::instance()->grabAndBlockInCurrentFrame(rhi, cd->swapchain);
+ }
+
+ if (cd->swapchain) {
+ QRhi::EndFrameFlags flags = 0;
+ if (grabImage)
+ flags |= QRhi::SkipPresent;
+ QRhi::FrameOpResult frameResult = rhi->endFrame(cd->swapchain, flags);
+ if (frameResult != QRhi::FrameOpSuccess) {
+ if (frameResult == QRhi::FrameOpDeviceLost)
+ handleDeviceLoss();
+ else if (frameResult == QRhi::FrameOpError)
+ qWarning("Failed to end frame");
+ if (frameResult == QRhi::FrameOpDeviceLost || frameResult == QRhi::FrameOpSwapChainOutOfDate)
+ QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
+ }
+ } else {
+ if (!cd->customRenderStage || !cd->customRenderStage->swap())
+ gl->swapBuffers(window);
+ }
+
+ if (!grabImage)
+ d->fireFrameSwapped();
+
} else {
Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopSync, 1);
@@ -680,6 +873,8 @@ void QSGRenderThread::syncAndRender()
Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopSwap);
+
+ QSGRhiProfileConnection::instance()->send(rhi);
}
@@ -714,6 +909,57 @@ void QSGRenderThread::processEventsAndWaitForMore()
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "--- done processEventsAndWaitForMore()");
}
+void QSGRenderThread::ensureRhi()
+{
+ if (!rhi) {
+ QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
+ rhi = rhiSupport->createRhi(window, offscreenSurface);
+ if (rhi) {
+ rhiSampleCount = rhiSupport->chooseSampleCountForWindowWithRhi(window, rhi);
+ if (rhiSupport->isProfilingRequested())
+ QSGRhiProfileConnection::instance()->initialize(rhi); // ### this breaks down with multiple windows
+ } else {
+ qWarning("Failed to create QRhi on the render thread; scenegraph is not functional");
+ return;
+ }
+ }
+ if (!sgrc->rhi() && windowSize.width() > 0 && windowSize.height() > 0) {
+ rhi->makeThreadLocalNativeContextCurrent();
+ QSGDefaultRenderContext::InitParams rcParams;
+ rcParams.rhi = rhi;
+ rcParams.sampleCount = rhiSampleCount;
+ rcParams.openGLContext = nullptr;
+ rcParams.initialSurfacePixelSize = windowSize * qreal(dpr);
+ rcParams.maybeSurface = window;
+ sgrc->initialize(&rcParams);
+ }
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
+ if (rhi && !cd->swapchain) {
+ cd->rhi = rhi;
+ QRhiSwapChain::Flags flags = QRhiSwapChain::UsedAsTransferSource; // may be used in a grab
+ // QQ is always premul alpha. Decide based on alphaBufferSize in
+ // requestedFormat(). (the platform plugin can override format() but
+ // what matters here is what the application wanted, hence using the
+ // requested one)
+ const bool alpha = window->requestedFormat().alphaBufferSize() > 0;
+ if (alpha)
+ flags |= QRhiSwapChain::SurfaceHasPreMulAlpha;
+ cd->swapchain = rhi->newSwapChain();
+ cd->depthStencilForSwapchain = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
+ QSize(),
+ rhiSampleCount,
+ QRhiRenderBuffer::UsedWithSwapChainOnly);
+ cd->swapchain->setWindow(window);
+ cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain);
+ qCDebug(QSG_LOG_INFO, "MSAA sample count for the swapchain is %d. Alpha channel requested = %s.",
+ rhiSampleCount, alpha ? "yes" : "no");
+ cd->swapchain->setSampleCount(rhiSampleCount);
+ cd->swapchain->setFlags(flags);
+ cd->rpDescForSwapchain = cd->swapchain->newCompatibleRenderPassDescriptor();
+ cd->swapchain->setRenderPassDescriptor(cd->rpDescForSwapchain);
+ }
+}
+
void QSGRenderThread::run()
{
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "run()");
@@ -723,11 +969,26 @@ void QSGRenderThread::run()
QQuickProfiler::registerAnimationCallback();
while (active) {
+#ifdef Q_OS_DARWIN
+ QMacAutoReleasePool frameReleasePool;
+#endif
if (window) {
- if (!sgrc->openglContext() && windowSize.width() > 0 && windowSize.height() > 0 && gl->makeCurrent(window))
- sgrc->initialize(gl);
- syncAndRender();
+ if (enableRhi) {
+ ensureRhi();
+ if (rhi)
+ syncAndRender();
+ } else {
+ if (!sgrc->openglContext() && windowSize.width() > 0 && windowSize.height() > 0 && gl->makeCurrent(window)) {
+ QSGDefaultRenderContext::InitParams rcParams;
+ rcParams.sampleCount = qMax(1, gl->format().samples());
+ rcParams.openGLContext = gl;
+ rcParams.initialSurfacePixelSize = windowSize * qreal(dpr);
+ rcParams.maybeSurface = window;
+ sgrc->initialize(&rcParams);
+ }
+ syncAndRender();
+ }
}
processEvents();
@@ -741,7 +1002,7 @@ void QSGRenderThread::run()
}
}
- Q_ASSERT_X(!gl, "QSGRenderThread::run()", "The OpenGL context should be cleaned up before exiting the render thread...");
+ Q_ASSERT_X(!gl && !rhi, "QSGRenderThread::run()", "The graphics context should be cleaned up before exiting the render thread...");
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "run() completed");
@@ -911,12 +1172,48 @@ void QSGThreadedRenderLoop::windowDestroyed(QQuickWindow *window)
qCDebug(QSG_LOG_RENDERLOOP) << "done windowDestroyed()" << window;
}
+void QSGThreadedRenderLoop::releaseSwapchain(QQuickWindow *window)
+{
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ delete wd->rpDescForSwapchain;
+ wd->rpDescForSwapchain = nullptr;
+ delete wd->swapchain;
+ wd->swapchain = nullptr;
+ delete wd->depthStencilForSwapchain;
+ wd->depthStencilForSwapchain = nullptr;
+ wd->hasActiveSwapchain = wd->hasRenderableSwapchain = wd->swapchainJustBecameRenderable = false;
+}
void QSGThreadedRenderLoop::exposureChanged(QQuickWindow *window)
{
qCDebug(QSG_LOG_RENDERLOOP) << "exposureChanged()" << window;
+
+ // This is tricker than used to be. We want to detect having an empty
+ // surface size (which may be the case even when window->size() is
+ // non-empty, on some platforms with some graphics APIs!) as well as the
+ // case when the window just became "newly exposed" (e.g. after a
+ // minimize-restore on Windows, or when switching between fully obscured -
+ // not fully obscured on macOS)
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ if (!window->isExposed())
+ wd->hasRenderableSwapchain = false;
+
+ bool skipThisExpose = false;
+ if (window->isExposed() && wd->hasActiveSwapchain && wd->swapchain->surfacePixelSize().isEmpty()) {
+ wd->hasRenderableSwapchain = false;
+ skipThisExpose = true;
+ }
+
+ if (window->isExposed() && !wd->hasRenderableSwapchain && wd->hasActiveSwapchain
+ && !wd->swapchain->surfacePixelSize().isEmpty())
+ {
+ wd->hasRenderableSwapchain = true;
+ wd->swapchainJustBecameRenderable = true;
+ }
+
if (window->isExposed()) {
- handleExposure(window);
+ if (!skipThisExpose)
+ handleExposure(window);
} else {
Window *w = windowFor(m_windows, window);
if (w)
@@ -968,30 +1265,43 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window)
// Start render thread if it is not running
if (!w->thread->isRunning()) {
-
qCDebug(QSG_LOG_RENDERLOOP, "- starting render thread");
- if (!w->thread->gl) {
- w->thread->gl = new QOpenGLContext();
- if (qt_gl_global_share_context())
- w->thread->gl->setShareContext(qt_gl_global_share_context());
- w->thread->gl->setFormat(w->window->requestedFormat());
- w->thread->gl->setScreen(w->window->screen());
- if (!w->thread->gl->create()) {
- const bool isEs = w->thread->gl->isOpenGLES();
- delete w->thread->gl;
- w->thread->gl = nullptr;
- handleContextCreationFailure(w->window, isEs);
- return;
+ w->thread->enableRhi = QSGRhiSupport::instance()->isRhiEnabled();
+ if (w->thread->enableRhi) {
+ if (!w->thread->rhi) {
+ QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
+ w->thread->offscreenSurface = rhiSupport->maybeCreateOffscreenSurface(window);
+ window->installEventFilter(this);
}
+ } else {
+ if (!w->thread->gl) {
+ w->thread->gl = new QOpenGLContext();
+ if (qt_gl_global_share_context())
+ w->thread->gl->setShareContext(qt_gl_global_share_context());
+ w->thread->gl->setFormat(w->window->requestedFormat());
+ w->thread->gl->setScreen(w->window->screen());
+ if (!w->thread->gl->create()) {
+ const bool isEs = w->thread->gl->isOpenGLES();
+ delete w->thread->gl;
+ w->thread->gl = nullptr;
+ handleContextCreationFailure(w->window, isEs);
+ return;
+ }
+
+ QQuickWindowPrivate::get(w->window)->fireOpenGLContextCreated(w->thread->gl);
- QQuickWindowPrivate::get(w->window)->fireOpenGLContextCreated(w->thread->gl);
+ w->thread->gl->moveToThread(w->thread);
+ qCDebug(QSG_LOG_RENDERLOOP, "- OpenGL context created");
- w->thread->gl->moveToThread(w->thread);
- qCDebug(QSG_LOG_RENDERLOOP, "- OpenGL context created");
+ w->thread->offscreenSurface = new QOffscreenSurface();
+ w->thread->offscreenSurface->setFormat(w->actualWindowFormat);
+ w->thread->offscreenSurface->create();
+ }
}
- QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController;
+ QQuickAnimatorController *controller
+ = QQuickWindowPrivate::get(w->window)->animationController.get();
if (controller->thread() != w->thread)
controller->moveToThread(w->thread);
@@ -1033,6 +1343,30 @@ void QSGThreadedRenderLoop::handleObscurity(Window *w)
startOrStopAnimationTimer();
}
+bool QSGThreadedRenderLoop::eventFilter(QObject *watched, QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::PlatformSurface:
+ // this is the proper time to tear down the swapchain (while the native window and surface are still around)
+ if (static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
+ QQuickWindow *window = qobject_cast<QQuickWindow *>(watched);
+ if (window) {
+ Window *w = windowFor(m_windows, window);
+ if (w) {
+ w->thread->mutex.lock();
+ w->thread->postEvent(new WMReleaseSwapchainEvent(window));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ w->thread->mutex.unlock();
+ }
+ window->removeEventFilter(this);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return QObject::eventFilter(watched, event);
+}
void QSGThreadedRenderLoop::handleUpdateRequest(QQuickWindow *window)
{
@@ -1062,6 +1396,8 @@ void QSGThreadedRenderLoop::maybeUpdate(Window *w)
return;
QThread *current = QThread::currentThread();
+ if (current == w->thread && w->thread->rhi && w->thread->rhi->isDeviceLost())
+ return;
if (current != QCoreApplication::instance()->thread() && (current != w->thread || !m_lockedForSync)) {
qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
return;
@@ -1125,23 +1461,14 @@ void QSGThreadedRenderLoop::releaseResources(Window *w, bool inDestructor)
QQuickWindow *window = w->window;
// The platform window might have been destroyed before
- // hide/release/windowDestroyed is called, so we need to have a
- // fallback surface to perform the cleanup of the scene graph
- // and the OpenGL resources.
- // QOffscreenSurface must be created on the GUI thread, so we
- // create it here and pass it on to QSGRenderThread::invalidateGL()
- QOffscreenSurface *fallback = nullptr;
- if (!window->handle()) {
- qCDebug(QSG_LOG_RENDERLOOP, "- using fallback surface");
- fallback = new QOffscreenSurface();
- fallback->setFormat(w->actualWindowFormat);
- fallback->create();
- }
+ // hide/release/windowDestroyed is called, so we may need to have a
+ // fallback surface to perform the cleanup of the scene graph and the
+ // OpenGL resources. QOffscreenSurface must be created on the GUI
+ // thread so that is done for us already.
qCDebug(QSG_LOG_RENDERLOOP, "- posting release request to render thread");
- w->thread->postEvent(new WMTryReleaseEvent(window, inDestructor, fallback));
+ w->thread->postEvent(new WMTryReleaseEvent(window, inDestructor, window->handle() == nullptr));
w->thread->waitCondition.wait(&w->thread->mutex);
- delete fallback;
// Avoid a shutdown race condition.
// If SG is invalidated and 'active' becomes false, the thread's run()
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop_p.h b/src/quick/scenegraph/qsgthreadedrenderloop_p.h
index b8fae8e8da..e5e9fa8b48 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop_p.h
+++ b/src/quick/scenegraph/qsgthreadedrenderloop_p.h
@@ -121,7 +121,9 @@ private:
void handleExposure(QQuickWindow *w);
void handleObscurity(Window *w);
+ void releaseSwapchain(QQuickWindow *window);
+ bool eventFilter(QObject *watched, QEvent *event) override;
QSGContext *sg;
// Set of contexts that have been created but are now owned by
diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp
index 95df700a15..5b48b86568 100644
--- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp
+++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp
@@ -62,6 +62,10 @@
QT_BEGIN_NAMESPACE
+// Single-threaded render loop with a custom animation driver. Like a
+// combination of basic+threaded but still working on the main thread. Only
+// compatible with direct OpenGL, no RHI support here.
+
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
#define RLDEBUG(x) qCDebug(QSG_LOG_RENDERLOOP, x)
@@ -188,8 +192,14 @@ void QSGWindowsRenderLoop::show(QQuickWindow *window)
RLDEBUG(" - making current");
bool current = m_gl->makeCurrent(window);
RLDEBUG(" - initializing SG");
- if (current)
- m_rc->initialize(m_gl);
+ if (current) {
+ QSGDefaultRenderContext::InitParams rcParams;
+ rcParams.sampleCount = qMax(1, m_gl->format().samples());
+ rcParams.openGLContext = m_gl;
+ rcParams.initialSurfacePixelSize = window->size() * window->effectiveDevicePixelRatio();
+ rcParams.maybeSurface = window;
+ m_rc->initialize(&rcParams);
+ }
}
WindowData data;
@@ -259,7 +269,7 @@ void QSGWindowsRenderLoop::windowDestroyed(QQuickWindow *window)
m_gl->doneCurrent();
}
- delete d->animationController;
+ d->animationController.reset();
}
bool QSGWindowsRenderLoop::anyoneShowing() const
@@ -440,10 +450,16 @@ void QSGWindowsRenderLoop::renderWindow(QQuickWindow *window)
if (!m_gl->isValid()) {
d->cleanupNodesOnShutdown();
m_rc->invalidate();
- if (m_gl->create() && m_gl->makeCurrent(window))
- m_rc->initialize(m_gl);
- else
+ if (m_gl->create() && m_gl->makeCurrent(window)) {
+ QSGDefaultRenderContext::InitParams rcParams;
+ rcParams.sampleCount = qMax(1, m_gl->format().samples());
+ rcParams.openGLContext = m_gl;
+ rcParams.initialSurfacePixelSize = window->size() * window->effectiveDevicePixelRatio();
+ rcParams.maybeSurface = window;
+ m_rc->initialize(&rcParams);
+ } else {
return;
+ }
}
}
diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri
index ddd7fb7f4c..56d97226fb 100644
--- a/src/quick/scenegraph/scenegraph.pri
+++ b/src/quick/scenegraph/scenegraph.pri
@@ -4,7 +4,11 @@
HEADERS += \
$$PWD/coreapi/qsggeometry.h \
$$PWD/coreapi/qsgmaterial.h \
+ $$PWD/coreapi/qsgmaterialtype.h \
+ $$PWD/coreapi/qsgmaterialshader.h \
$$PWD/coreapi/qsgmaterialshader_p.h \
+ $$PWD/coreapi/qsgmaterialrhishader.h \
+ $$PWD/coreapi/qsgmaterialrhishader_p.h \
$$PWD/coreapi/qsgnode.h \
$$PWD/coreapi/qsgnode_p.h \
$$PWD/coreapi/qsgnodeupdater_p.h \
@@ -14,12 +18,17 @@ HEADERS += \
$$PWD/coreapi/qsgrendernode.h \
$$PWD/coreapi/qsgrendernode_p.h \
$$PWD/coreapi/qsgrendererinterface.h \
- $$PWD/coreapi/qsggeometry_p.h
+ $$PWD/coreapi/qsggeometry_p.h \
+ $$PWD/coreapi/qsgtexture.h \
+ $$PWD/coreapi/qsgtexture_p.h
SOURCES += \
$$PWD/coreapi/qsgabstractrenderer.cpp \
$$PWD/coreapi/qsggeometry.cpp \
$$PWD/coreapi/qsgmaterial.cpp \
+ $$PWD/coreapi/qsgmaterialshader.cpp \
+ $$PWD/coreapi/qsgmaterialrhishader.cpp \
+ $$PWD/coreapi/qsgtexture.cpp \
$$PWD/coreapi/qsgnode.cpp \
$$PWD/coreapi/qsgnodeupdater.cpp \
$$PWD/coreapi/qsgrenderer.cpp \
@@ -28,9 +37,13 @@ SOURCES += \
qtConfig(opengl(es1|es2)?) {
HEADERS += \
- $$PWD/coreapi/qsgbatchrenderer_p.h
+ $$PWD/coreapi/qsgbatchrenderer_p.h \
+ $$PWD/coreapi/qsgopenglvisualizer_p.h \
+ $$PWD/coreapi/qsgrhivisualizer_p.h
SOURCES += \
$$PWD/coreapi/qsgbatchrenderer.cpp \
+ $$PWD/coreapi/qsgopenglvisualizer.cpp \
+ $$PWD/coreapi/qsgrhivisualizer.cpp \
$$PWD/coreapi/qsgshaderrewriter.cpp
}
@@ -39,10 +52,10 @@ HEADERS += \
$$PWD/util/qsgareaallocator_p.h \
$$PWD/util/qsgengine.h \
$$PWD/util/qsgengine_p.h \
+ $$PWD/util/qsgplaintexture_p.h \
+ $$PWD/util/qsgrhinativetextureimporter_p.h \
$$PWD/util/qsgsimplerectnode.h \
$$PWD/util/qsgsimpletexturenode.h \
- $$PWD/util/qsgtexture.h \
- $$PWD/util/qsgtexture_p.h \
$$PWD/util/qsgtextureprovider.h \
$$PWD/util/qsgflatcolormaterial.h \
$$PWD/util/qsgsimplematerial.h \
@@ -56,9 +69,10 @@ HEADERS += \
SOURCES += \
$$PWD/util/qsgareaallocator.cpp \
$$PWD/util/qsgengine.cpp \
+ $$PWD/util/qsgplaintexture.cpp \
+ $$PWD/util/qsgrhinativetextureimporter.cpp \
$$PWD/util/qsgsimplerectnode.cpp \
$$PWD/util/qsgsimpletexturenode.cpp \
- $$PWD/util/qsgtexture.cpp \
$$PWD/util/qsgtextureprovider.cpp \
$$PWD/util/qsgflatcolormaterial.cpp \
$$PWD/util/qsgsimplematerial.cpp \
@@ -72,13 +86,27 @@ qtConfig(opengl(es1|es2)?) {
HEADERS += \
$$PWD/util/qsgdepthstencilbuffer_p.h \
$$PWD/util/qsgshadersourcebuilder_p.h \
- $$PWD/util/qsgatlastexture_p.h
+ $$PWD/util/qsgopenglatlastexture_p.h
SOURCES += \
$$PWD/util/qsgdepthstencilbuffer.cpp \
- $$PWD/util/qsgatlastexture.cpp \
+ $$PWD/util/qsgopenglatlastexture.cpp \
$$PWD/util/qsgshadersourcebuilder.cpp
-}
+ # rhi, still tied to OpenGL-enabled Qt builds for now
+ HEADERS += \
+ $$PWD/qsgrhitextureglyphcache_p.h \
+ $$PWD/util/qsgrhiatlastexture_p.h \
+ $$PWD/qsgrhilayer_p.h \
+ $$PWD/qsgrhishadereffectnode_p.h \
+ $$PWD/qsgrhidistancefieldglyphcache_p.h
+
+ SOURCES += \
+ $$PWD/qsgrhitextureglyphcache.cpp \
+ $$PWD/qsgrhilayer.cpp \
+ $$PWD/qsgrhishadereffectnode.cpp \
+ $$PWD/util/qsgrhiatlastexture.cpp \
+ $$PWD/qsgrhidistancefieldglyphcache.cpp
+}
# QML / Adaptations API
HEADERS += \
@@ -88,7 +116,8 @@ HEADERS += \
$$PWD/qsgbasicinternalrectanglenode_p.h \
$$PWD/qsgbasicinternalimagenode_p.h \
$$PWD/qsgbasicglyphnode_p.h \
- $$PWD/qsgrenderloop_p.h
+ $$PWD/qsgrenderloop_p.h \
+ $$PWD/qsgrhisupport_p.h
SOURCES += \
$$PWD/qsgadaptationlayer.cpp \
@@ -97,13 +126,14 @@ SOURCES += \
$$PWD/qsgbasicinternalrectanglenode.cpp \
$$PWD/qsgbasicinternalimagenode.cpp \
$$PWD/qsgbasicglyphnode.cpp \
- $$PWD/qsgrenderloop.cpp
+ $$PWD/qsgrenderloop.cpp \
+ $$PWD/qsgrhisupport.cpp
qtConfig(opengl(es1|es2)?) {
SOURCES += \
$$PWD/qsgdefaultglyphnode.cpp \
$$PWD/qsgdefaultglyphnode_p.cpp \
- $$PWD/qsgdefaultdistancefieldglyphcache.cpp \
+ $$PWD/qsgopengldistancefieldglyphcache.cpp \
$$PWD/qsgdistancefieldglyphnode.cpp \
$$PWD/qsgdistancefieldglyphnode_p.cpp \
$$PWD/qsgdefaultinternalimagenode.cpp \
@@ -114,11 +144,11 @@ qtConfig(opengl(es1|es2)?) {
$$PWD/util/qsgdefaultrectanglenode.cpp \
$$PWD/util/qsgdefaultimagenode.cpp \
$$PWD/util/qsgdefaultninepatchnode.cpp \
- $$PWD/qsgdefaultlayer.cpp \
+ $$PWD/qsgopengllayer.cpp \
$$PWD/qsgwindowsrenderloop.cpp
HEADERS += \
$$PWD/qsgdefaultglyphnode_p.h \
- $$PWD/qsgdefaultdistancefieldglyphcache_p.h \
+ $$PWD/qsgopengldistancefieldglyphcache_p.h \
$$PWD/qsgdistancefieldglyphnode_p.h \
$$PWD/qsgdistancefieldglyphnode_p_p.h \
$$PWD/qsgdefaultglyphnode_p_p.h \
@@ -130,7 +160,7 @@ qtConfig(opengl(es1|es2)?) {
$$PWD/util/qsgdefaultrectanglenode_p.h \
$$PWD/util/qsgdefaultimagenode_p.h \
$$PWD/util/qsgdefaultninepatchnode_p.h \
- $$PWD/qsgdefaultlayer_p.h \
+ $$PWD/qsgopengllayer_p.h \
$$PWD/qsgwindowsrenderloop_p.h
qtConfig(thread) {
diff --git a/src/quick/scenegraph/scenegraph.qrc b/src/quick/scenegraph/scenegraph.qrc
index 0687530be1..e409f07610 100644
--- a/src/quick/scenegraph/scenegraph.qrc
+++ b/src/quick/scenegraph/scenegraph.qrc
@@ -72,5 +72,57 @@
<file>shaders/sprite.vert</file>
<file>shaders/sprite_core.frag</file>
<file>shaders/sprite_core.vert</file>
+
+ <file>shaders_ng/vertexcolor.vert.qsb</file>
+ <file>shaders_ng/vertexcolor.frag.qsb</file>
+ <file>shaders_ng/flatcolor.vert.qsb</file>
+ <file>shaders_ng/flatcolor.frag.qsb</file>
+ <file>shaders_ng/smoothcolor.vert.qsb</file>
+ <file>shaders_ng/smoothcolor.frag.qsb</file>
+ <file>shaders_ng/stencilclip.vert.qsb</file>
+ <file>shaders_ng/stencilclip.frag.qsb</file>
+ <file>shaders_ng/texture.vert.qsb</file>
+ <file>shaders_ng/texture.frag.qsb</file>
+ <file>shaders_ng/opaquetexture.vert.qsb</file>
+ <file>shaders_ng/opaquetexture.frag.qsb</file>
+ <file>shaders_ng/smoothtexture.vert.qsb</file>
+ <file>shaders_ng/smoothtexture.frag.qsb</file>
+
+ <file>shaders_ng/textmask.vert.qsb</file>
+ <file>shaders_ng/textmask.frag.qsb</file>
+ <file>shaders_ng/8bittextmask.frag.qsb</file>
+ <file>shaders_ng/8bittextmask_a.frag.qsb</file>
+ <file>shaders_ng/24bittextmask.frag.qsb</file>
+ <file>shaders_ng/32bitcolortext.frag.qsb</file>
+ <file>shaders_ng/outlinedtext.vert.qsb</file>
+ <file>shaders_ng/outlinedtext.frag.qsb</file>
+ <file>shaders_ng/outlinedtext_a.frag.qsb</file>
+ <file>shaders_ng/styledtext.vert.qsb</file>
+ <file>shaders_ng/styledtext.frag.qsb</file>
+ <file>shaders_ng/styledtext_a.frag.qsb</file>
+
+ <file>shaders_ng/distancefieldtext.vert.qsb</file>
+ <file>shaders_ng/distancefieldtext.frag.qsb</file>
+ <file>shaders_ng/distancefieldtext_a.frag.qsb</file>
+ <file>shaders_ng/distancefieldshiftedtext.vert.qsb</file>
+ <file>shaders_ng/distancefieldshiftedtext.frag.qsb</file>
+ <file>shaders_ng/distancefieldshiftedtext_a.frag.qsb</file>
+ <file>shaders_ng/distancefieldoutlinetext.vert.qsb</file>
+ <file>shaders_ng/distancefieldoutlinetext.frag.qsb</file>
+ <file>shaders_ng/distancefieldoutlinetext_a.frag.qsb</file>
+ <file>shaders_ng/hiqsubpixeldistancefieldtext.vert.qsb</file>
+ <file>shaders_ng/hiqsubpixeldistancefieldtext.frag.qsb</file>
+ <file>shaders_ng/hiqsubpixeldistancefieldtext_a.frag.qsb</file>
+ <file>shaders_ng/loqsubpixeldistancefieldtext.vert.qsb</file>
+ <file>shaders_ng/loqsubpixeldistancefieldtext.frag.qsb</file>
+ <file>shaders_ng/loqsubpixeldistancefieldtext_a.frag.qsb</file>
+
+ <file>shaders_ng/shadereffect.vert.qsb</file>
+ <file>shaders_ng/shadereffect.frag.qsb</file>
+ <file>shaders_ng/sprite.vert.qsb</file>
+ <file>shaders_ng/sprite.frag.qsb</file>
+
+ <file>shaders_ng/visualization.vert.qsb</file>
+ <file>shaders_ng/visualization.frag.qsb</file>
</qresource>
</RCC>
diff --git a/src/quick/scenegraph/shaders_ng/24bittextmask.frag b/src/quick/scenegraph/shaders_ng/24bittextmask.frag
new file mode 100644
index 0000000000..bc3826a924
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/24bittextmask.frag
@@ -0,0 +1,19 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color; // only alpha is used, but must be vec4 due to layout compat
+ vec2 textureScale;
+ float dpr;
+} ubuf;
+
+void main()
+{
+ vec4 glyph = texture(_qt_texture, sampleCoord);
+ fragColor = vec4(glyph.rgb * ubuf.color.a, glyph.a);
+}
diff --git a/src/quick/scenegraph/shaders_ng/24bittextmask.frag.qsb b/src/quick/scenegraph/shaders_ng/24bittextmask.frag.qsb
new file mode 100644
index 0000000000..b16da4d76a
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/24bittextmask.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/32bitcolortext.frag b/src/quick/scenegraph/shaders_ng/32bitcolortext.frag
new file mode 100644
index 0000000000..63e445f90b
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/32bitcolortext.frag
@@ -0,0 +1,18 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color; // only alpha is used, but must be vec4 due to layout compat
+ vec2 textureScale;
+ float dpr;
+} ubuf;
+
+void main()
+{
+ fragColor = texture(_qt_texture, sampleCoord) * ubuf.color.a;
+}
diff --git a/src/quick/scenegraph/shaders_ng/32bitcolortext.frag.qsb b/src/quick/scenegraph/shaders_ng/32bitcolortext.frag.qsb
new file mode 100644
index 0000000000..1a12a35b49
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/32bitcolortext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/8bittextmask.frag b/src/quick/scenegraph/shaders_ng/8bittextmask.frag
new file mode 100644
index 0000000000..6304e821ff
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/8bittextmask.frag
@@ -0,0 +1,18 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+} ubuf;
+
+void main()
+{
+ fragColor = ubuf.color * texture(_qt_texture, sampleCoord).r;
+}
diff --git a/src/quick/scenegraph/shaders_ng/8bittextmask.frag.qsb b/src/quick/scenegraph/shaders_ng/8bittextmask.frag.qsb
new file mode 100644
index 0000000000..2d0d23d813
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/8bittextmask.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag b/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag
new file mode 100644
index 0000000000..0d0fa1cd3a
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag
@@ -0,0 +1,18 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+} ubuf;
+
+void main()
+{
+ fragColor = ubuf.color * texture(_qt_texture, sampleCoord).a; // take .a instead of .r
+}
diff --git a/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag.qsb b/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag.qsb
new file mode 100644
index 0000000000..65d9af4736
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/compile.bat b/src/quick/scenegraph/shaders_ng/compile.bat
new file mode 100755
index 0000000000..a0c74c22c7
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/compile.bat
@@ -0,0 +1,86 @@
+:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::
+:: Copyright (C) 2019 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$
+::
+:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o vertexcolor.vert.qsb vertexcolor.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o vertexcolor.frag.qsb vertexcolor.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o flatcolor.vert.qsb flatcolor.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o flatcolor.frag.qsb flatcolor.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o smoothcolor.vert.qsb smoothcolor.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o smoothcolor.frag.qsb smoothcolor.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o stencilclip.vert.qsb stencilclip.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o stencilclip.frag.qsb stencilclip.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o texture.vert.qsb texture.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o texture.frag.qsb texture.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o opaquetexture.vert.qsb opaquetexture.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o opaquetexture.frag.qsb opaquetexture.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o smoothtexture.vert.qsb smoothtexture.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o smoothtexture.frag.qsb smoothtexture.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o textmask.vert.qsb textmask.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o textmask.frag.qsb textmask.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o 8bittextmask.frag.qsb 8bittextmask.frag
+qsb --glsl "150,120,100 es" -o 8bittextmask_a.frag.qsb 8bittextmask_a.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o 24bittextmask.frag.qsb 24bittextmask.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o 32bitcolortext.frag.qsb 32bitcolortext.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o outlinedtext.vert.qsb outlinedtext.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o outlinedtext.frag.qsb outlinedtext.frag
+qsb --glsl "150,120,100 es" -o outlinedtext_a.frag.qsb outlinedtext_a.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o styledtext.vert.qsb styledtext.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o styledtext.frag.qsb styledtext.frag
+qsb --glsl "150,120,100 es" -o styledtext_a.frag.qsb styledtext_a.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o distancefieldtext.vert.qsb distancefieldtext.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o distancefieldtext.frag.qsb distancefieldtext.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o distancefieldtext_a.frag.qsb distancefieldtext_a.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o distancefieldshiftedtext.vert.qsb distancefieldshiftedtext.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o distancefieldshiftedtext.frag.qsb distancefieldshiftedtext.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o distancefieldshiftedtext_a.frag.qsb distancefieldshiftedtext_a.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o distancefieldoutlinetext.vert.qsb distancefieldoutlinetext.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o distancefieldoutlinetext.frag.qsb distancefieldoutlinetext.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o distancefieldoutlinetext_a.frag.qsb distancefieldoutlinetext_a.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o hiqsubpixeldistancefieldtext.vert.qsb hiqsubpixeldistancefieldtext.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o hiqsubpixeldistancefieldtext.frag.qsb hiqsubpixeldistancefieldtext.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o hiqsubpixeldistancefieldtext_a.frag.qsb hiqsubpixeldistancefieldtext_a.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o loqsubpixeldistancefieldtext.vert.qsb loqsubpixeldistancefieldtext.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o loqsubpixeldistancefieldtext.frag.qsb loqsubpixeldistancefieldtext.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o loqsubpixeldistancefieldtext_a.frag.qsb loqsubpixeldistancefieldtext_a.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o shadereffect.vert.qsb shadereffect.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o shadereffect.frag.qsb shadereffect.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o sprite.vert.qsb sprite.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o sprite.frag.qsb sprite.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o visualization.vert.qsb visualization.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o visualization.frag.qsb visualization.frag
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag
new file mode 100644
index 0000000000..c8c1ac89dc
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag
@@ -0,0 +1,25 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ vec4 styleColor;
+ float outlineAlphaMax0;
+ float outlineAlphaMax1;
+} ubuf;
+
+void main()
+{
+ float d = texture(_qt_texture, sampleCoord).r;
+ fragColor = mix(ubuf.styleColor, ubuf.color, smoothstep(ubuf.alphaMin, ubuf.alphaMax, d))
+ * smoothstep(ubuf.outlineAlphaMax0, ubuf.outlineAlphaMax1, d);
+}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag.qsb b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag.qsb
new file mode 100644
index 0000000000..5753794649
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert
new file mode 100644
index 0000000000..8f0d618503
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert
@@ -0,0 +1,26 @@
+#version 440
+
+layout(location = 0) in vec4 vCoord;
+layout(location = 1) in vec2 tCoord;
+
+layout(location = 0) out vec2 sampleCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ vec4 styleColor;
+ float outlineAlphaMax0;
+ float outlineAlphaMax1;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ sampleCoord = tCoord * ubuf.textureScale;
+ gl_Position = ubuf.matrix * vCoord;
+}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert.qsb b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert.qsb
new file mode 100644
index 0000000000..6026960d68
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag
new file mode 100644
index 0000000000..70fb80852d
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag
@@ -0,0 +1,25 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ vec4 styleColor;
+ float outlineAlphaMax0;
+ float outlineAlphaMax1;
+} ubuf;
+
+void main()
+{
+ float d = texture(_qt_texture, sampleCoord).a;
+ fragColor = mix(ubuf.styleColor, ubuf.color, smoothstep(ubuf.alphaMin, ubuf.alphaMax, d))
+ * smoothstep(ubuf.outlineAlphaMax0, ubuf.outlineAlphaMax1, d);
+}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag.qsb b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag.qsb
new file mode 100644
index 0000000000..451ccbac5b
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag
new file mode 100644
index 0000000000..aa3390094b
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag
@@ -0,0 +1,27 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 1) in vec2 shiftedSampleCoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ vec4 styleColor;
+ vec2 shift;
+} ubuf;
+
+void main()
+{
+ float a = smoothstep(ubuf.alphaMin, ubuf.alphaMax, texture(_qt_texture, sampleCoord).r);
+ vec4 shifted = ubuf.styleColor * smoothstep(ubuf.alphaMin, ubuf.alphaMax,
+ texture(_qt_texture, shiftedSampleCoord).r);
+ fragColor = mix(shifted, ubuf.color, a);
+}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag.qsb b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag.qsb
new file mode 100644
index 0000000000..41ebc12abf
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert
new file mode 100644
index 0000000000..f3a7671435
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert
@@ -0,0 +1,27 @@
+#version 440
+
+layout(location = 0) in vec4 vCoord;
+layout(location = 1) in vec2 tCoord;
+
+layout(location = 0) out vec2 sampleCoord;
+layout(location = 1) out vec2 shiftedSampleCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ vec4 styleColor;
+ vec2 shift;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ sampleCoord = tCoord * ubuf.textureScale;
+ shiftedSampleCoord = (tCoord - ubuf.shift) * ubuf.textureScale;
+ gl_Position = ubuf.matrix * vCoord;
+}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert.qsb b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert.qsb
new file mode 100644
index 0000000000..0c37ccb6ed
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag
new file mode 100644
index 0000000000..ab3a5f63ff
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag
@@ -0,0 +1,27 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 1) in vec2 shiftedSampleCoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ vec4 styleColor;
+ vec2 shift;
+} ubuf;
+
+void main()
+{
+ float a = smoothstep(ubuf.alphaMin, ubuf.alphaMax, texture(_qt_texture, sampleCoord).a);
+ vec4 shifted = ubuf.styleColor * smoothstep(ubuf.alphaMin, ubuf.alphaMax,
+ texture(_qt_texture, shiftedSampleCoord).a);
+ fragColor = mix(shifted, ubuf.color, a);
+}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag.qsb b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag.qsb
new file mode 100644
index 0000000000..b92235eec3
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext.frag b/src/quick/scenegraph/shaders_ng/distancefieldtext.frag
new file mode 100644
index 0000000000..d594207567
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldtext.frag
@@ -0,0 +1,20 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+} ubuf;
+
+void main()
+{
+ fragColor = ubuf.color * smoothstep(ubuf.alphaMin, ubuf.alphaMax,
+ texture(_qt_texture, sampleCoord).r);
+}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext.frag.qsb b/src/quick/scenegraph/shaders_ng/distancefieldtext.frag.qsb
new file mode 100644
index 0000000000..28ba15e3de
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldtext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext.vert b/src/quick/scenegraph/shaders_ng/distancefieldtext.vert
new file mode 100644
index 0000000000..d56ddddd24
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldtext.vert
@@ -0,0 +1,22 @@
+#version 440
+
+layout(location = 0) in vec4 vCoord;
+layout(location = 1) in vec2 tCoord;
+
+layout(location = 0) out vec2 sampleCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ sampleCoord = tCoord * ubuf.textureScale;
+ gl_Position = ubuf.matrix * vCoord;
+}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext.vert.qsb b/src/quick/scenegraph/shaders_ng/distancefieldtext.vert.qsb
new file mode 100644
index 0000000000..2877ab92db
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldtext.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag b/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag
new file mode 100644
index 0000000000..bb807d86d8
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag
@@ -0,0 +1,20 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+} ubuf;
+
+void main()
+{
+ fragColor = ubuf.color * smoothstep(ubuf.alphaMin, ubuf.alphaMax,
+ texture(_qt_texture, sampleCoord).a);
+}
diff --git a/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag.qsb b/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag.qsb
new file mode 100644
index 0000000000..2e6085aa39
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/distancefieldtext_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/flatcolor.frag b/src/quick/scenegraph/shaders_ng/flatcolor.frag
new file mode 100644
index 0000000000..3a677b7c93
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/flatcolor.frag
@@ -0,0 +1,13 @@
+#version 440
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+} ubuf;
+
+void main()
+{
+ fragColor = ubuf.color;
+}
diff --git a/src/quick/scenegraph/shaders_ng/flatcolor.frag.qsb b/src/quick/scenegraph/shaders_ng/flatcolor.frag.qsb
new file mode 100644
index 0000000000..a528c667fd
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/flatcolor.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/flatcolor.vert b/src/quick/scenegraph/shaders_ng/flatcolor.vert
new file mode 100644
index 0000000000..b5dfd32197
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/flatcolor.vert
@@ -0,0 +1,15 @@
+#version 440
+
+layout(location = 0) in vec4 vertexCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ gl_Position = ubuf.matrix * vertexCoord;
+}
diff --git a/src/quick/scenegraph/shaders_ng/flatcolor.vert.qsb b/src/quick/scenegraph/shaders_ng/flatcolor.vert.qsb
new file mode 100644
index 0000000000..e83de529e6
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/flatcolor.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag
new file mode 100644
index 0000000000..723227a04d
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag
@@ -0,0 +1,40 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 1) in vec3 sampleFarLeft;
+layout(location = 2) in vec3 sampleNearLeft;
+layout(location = 3) in vec3 sampleNearRight;
+layout(location = 4) in vec3 sampleFarRight;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ float fontScale;
+ vec4 vecDelta;
+} ubuf;
+
+void main()
+{
+ vec4 n;
+ n.x = textureProj(_qt_texture, sampleFarLeft).r;
+ n.y = textureProj(_qt_texture, sampleNearLeft).r;
+ float c = texture(_qt_texture, sampleCoord).r;
+ n.z = textureProj(_qt_texture, sampleNearRight).r;
+ n.w = textureProj(_qt_texture, sampleFarRight).r;
+
+ vec2 d = min(abs(n.yw - n.xz) * 2., 0.67);
+ vec2 lo = mix(vec2(ubuf.alphaMin), vec2(0.5), d);
+ vec2 hi = mix(vec2(ubuf.alphaMax), vec2(0.5), d);
+ n = smoothstep(lo.xxyy, hi.xxyy, n);
+ c = smoothstep(lo.x + lo.y, hi.x + hi.y, 2. * c);
+
+ fragColor = vec4(0.333 * (n.xyz + n.yzw + c), c) * ubuf.color.w;
+}
diff --git a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag.qsb b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag.qsb
new file mode 100644
index 0000000000..81c51321bb
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert
new file mode 100644
index 0000000000..9c7281c31c
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert
@@ -0,0 +1,44 @@
+#version 440
+
+layout(location = 0) in vec4 vCoord;
+layout(location = 1) in vec2 tCoord;
+
+layout(location = 0) out vec2 sampleCoord;
+layout(location = 1) out vec3 sampleFarLeft;
+layout(location = 2) out vec3 sampleNearLeft;
+layout(location = 3) out vec3 sampleNearRight;
+layout(location = 4) out vec3 sampleFarRight;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ float fontScale;
+ vec4 vecDelta;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ sampleCoord = tCoord * ubuf.textureScale;
+ gl_Position = ubuf.matrix * vCoord;
+
+ // Calculate neighbor pixel position in item space.
+ vec3 wDelta = gl_Position.w * ubuf.vecDelta.xyw;
+ vec3 farLeft = vCoord.xyw - 0.667 * wDelta;
+ vec3 nearLeft = vCoord.xyw - 0.333 * wDelta;
+ vec3 nearRight = vCoord.xyw + 0.333 * wDelta;
+ vec3 farRight = vCoord.xyw + 0.667 * wDelta;
+
+ // Calculate neighbor texture coordinate.
+ vec2 scale = ubuf.textureScale / ubuf.fontScale;
+ vec2 base = sampleCoord - scale * vCoord.xy;
+ sampleFarLeft = vec3(base * farLeft.z + scale * farLeft.xy, farLeft.z);
+ sampleNearLeft = vec3(base * nearLeft.z + scale * nearLeft.xy, nearLeft.z);
+ sampleNearRight = vec3(base * nearRight.z + scale * nearRight.xy, nearRight.z);
+ sampleFarRight = vec3(base * farRight.z + scale * farRight.xy, farRight.z);
+}
diff --git a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert.qsb b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert.qsb
new file mode 100644
index 0000000000..6bf01658a1
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag
new file mode 100644
index 0000000000..a9d56f6380
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag
@@ -0,0 +1,40 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 1) in vec3 sampleFarLeft;
+layout(location = 2) in vec3 sampleNearLeft;
+layout(location = 3) in vec3 sampleNearRight;
+layout(location = 4) in vec3 sampleFarRight;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ float fontScale;
+ vec4 vecDelta;
+} ubuf;
+
+void main()
+{
+ vec4 n;
+ n.x = textureProj(_qt_texture, sampleFarLeft).a;
+ n.y = textureProj(_qt_texture, sampleNearLeft).a;
+ float c = texture(_qt_texture, sampleCoord).a;
+ n.z = textureProj(_qt_texture, sampleNearRight).a;
+ n.w = textureProj(_qt_texture, sampleFarRight).a;
+
+ vec2 d = min(abs(n.yw - n.xz) * 2., 0.67);
+ vec2 lo = mix(vec2(ubuf.alphaMin), vec2(0.5), d);
+ vec2 hi = mix(vec2(ubuf.alphaMax), vec2(0.5), d);
+ n = smoothstep(lo.xxyy, hi.xxyy, n);
+ c = smoothstep(lo.x + lo.y, hi.x + hi.y, 2. * c);
+
+ fragColor = vec4(0.333 * (n.xyz + n.yzw + c), c) * ubuf.color.w;
+}
diff --git a/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag.qsb b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag.qsb
new file mode 100644
index 0000000000..4a9ac900a6
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag
new file mode 100644
index 0000000000..08b2ce5187
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag
@@ -0,0 +1,29 @@
+#version 440
+
+layout(location = 0) in vec3 sampleNearLeft;
+layout(location = 1) in vec3 sampleNearRight;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ float fontScale;
+ vec4 vecDelta;
+} ubuf;
+
+void main()
+{
+ vec2 n;
+ n.x = textureProj(_qt_texture, sampleNearLeft).r;
+ n.y = textureProj(_qt_texture, sampleNearRight).r;
+ n = smoothstep(ubuf.alphaMin, ubuf.alphaMax, n);
+ float c = 0.5 * (n.x + n.y);
+ fragColor = vec4(n.x, c, n.y, c) * ubuf.color.w;
+}
diff --git a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag.qsb b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag.qsb
new file mode 100644
index 0000000000..76c2459edf
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert
new file mode 100644
index 0000000000..187c384959
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert
@@ -0,0 +1,37 @@
+#version 440
+
+layout(location = 0) in vec4 vCoord;
+layout(location = 1) in vec2 tCoord;
+
+layout(location = 0) out vec3 sampleNearLeft;
+layout(location = 1) out vec3 sampleNearRight;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ float fontScale;
+ vec4 vecDelta;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ vec2 sampleCoord = tCoord * ubuf.textureScale;
+ gl_Position = ubuf.matrix * vCoord;
+
+ // Calculate neighbor pixel position in item space.
+ vec3 wDelta = gl_Position.w * ubuf.vecDelta.xyw;
+ vec3 nearLeft = vCoord.xyw - 0.25 * wDelta;
+ vec3 nearRight = vCoord.xyw + 0.25 * wDelta;
+
+ // Calculate neighbor texture coordinate.
+ vec2 scale = ubuf.textureScale / ubuf.fontScale;
+ vec2 base = sampleCoord - scale * vCoord.xy;
+ sampleNearLeft = vec3(base * nearLeft.z + scale * nearLeft.xy, nearLeft.z);
+ sampleNearRight = vec3(base * nearRight.z + scale * nearRight.xy, nearRight.z);
+}
diff --git a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert.qsb b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert.qsb
new file mode 100644
index 0000000000..7bfa7ccd4a
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag
new file mode 100644
index 0000000000..ef9407491b
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag
@@ -0,0 +1,29 @@
+#version 440
+
+layout(location = 0) in vec3 sampleNearLeft;
+layout(location = 1) in vec3 sampleNearRight;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 textureScale;
+ vec4 color;
+ float alphaMin;
+ float alphaMax;
+ // up to this point it must match distancefieldtext
+ float fontScale;
+ vec4 vecDelta;
+} ubuf;
+
+void main()
+{
+ vec2 n;
+ n.x = textureProj(_qt_texture, sampleNearLeft).a;
+ n.y = textureProj(_qt_texture, sampleNearRight).a;
+ n = smoothstep(ubuf.alphaMin, ubuf.alphaMax, n);
+ float c = 0.5 * (n.x + n.y);
+ fragColor = vec4(n.x, c, n.y, c) * ubuf.color.w;
+}
diff --git a/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag.qsb b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag.qsb
new file mode 100644
index 0000000000..8f8304fb49
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/opaquetexture.frag b/src/quick/scenegraph/shaders_ng/opaquetexture.frag
new file mode 100644
index 0000000000..2cd2175f87
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/opaquetexture.frag
@@ -0,0 +1,11 @@
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D qt_Texture;
+
+void main()
+{
+ fragColor = texture(qt_Texture, qt_TexCoord);
+}
diff --git a/src/quick/scenegraph/shaders_ng/opaquetexture.frag.qsb b/src/quick/scenegraph/shaders_ng/opaquetexture.frag.qsb
new file mode 100644
index 0000000000..0b4554568b
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/opaquetexture.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/opaquetexture.vert b/src/quick/scenegraph/shaders_ng/opaquetexture.vert
new file mode 100644
index 0000000000..5b52a59004
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/opaquetexture.vert
@@ -0,0 +1,18 @@
+#version 440
+
+layout(location = 0) in vec4 qt_VertexPosition;
+layout(location = 1) in vec2 qt_VertexTexCoord;
+
+layout(location = 0) out vec2 qt_TexCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ qt_TexCoord = qt_VertexTexCoord;
+ gl_Position = ubuf.qt_Matrix * qt_VertexPosition;
+}
diff --git a/src/quick/scenegraph/shaders_ng/opaquetexture.vert.qsb b/src/quick/scenegraph/shaders_ng/opaquetexture.vert.qsb
new file mode 100644
index 0000000000..2872af0200
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/opaquetexture.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext.frag b/src/quick/scenegraph/shaders_ng/outlinedtext.frag
new file mode 100644
index 0000000000..0023ed5467
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext.frag
@@ -0,0 +1,33 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 1) in vec2 sCoordUp;
+layout(location = 2) in vec2 sCoordDown;
+layout(location = 3) in vec2 sCoordLeft;
+layout(location = 4) in vec2 sCoordRight;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ // must match styledtext
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+ vec4 styleColor;
+ vec2 shift;
+} ubuf;
+
+void main()
+{
+ float glyph = texture(_qt_texture, sampleCoord).r;
+ float outline = clamp(clamp(texture(_qt_texture, sCoordUp).r +
+ texture(_qt_texture, sCoordDown).r +
+ texture(_qt_texture, sCoordLeft).r +
+ texture(_qt_texture, sCoordRight).r,
+ 0.0, 1.0) - glyph,
+ 0.0, 1.0);
+ fragColor = outline * ubuf.styleColor + glyph * ubuf.color;
+}
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext.frag.qsb b/src/quick/scenegraph/shaders_ng/outlinedtext.frag.qsb
new file mode 100644
index 0000000000..5ab92fecca
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext.vert b/src/quick/scenegraph/shaders_ng/outlinedtext.vert
new file mode 100644
index 0000000000..c683a4273c
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext.vert
@@ -0,0 +1,32 @@
+#version 440
+
+layout(location = 0) in vec4 vCoord;
+layout(location = 1) in vec2 tCoord;
+
+layout(location = 0) out vec2 sampleCoord;
+layout(location = 1) out vec2 sCoordUp;
+layout(location = 2) out vec2 sCoordDown;
+layout(location = 3) out vec2 sCoordLeft;
+layout(location = 4) out vec2 sCoordRight;
+
+layout(std140, binding = 0) uniform buf {
+ // must match styledtext
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+ vec4 styleColor;
+ vec2 shift;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ sampleCoord = tCoord * ubuf.textureScale;
+ sCoordUp = (tCoord - vec2(0.0, -1.0)) * ubuf.textureScale;
+ sCoordDown = (tCoord - vec2(0.0, 1.0)) * ubuf.textureScale;
+ sCoordLeft = (tCoord - vec2(-1.0, 0.0)) * ubuf.textureScale;
+ sCoordRight = (tCoord - vec2(1.0, 0.0)) * ubuf.textureScale;
+ gl_Position = ubuf.matrix * vCoord;
+}
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext.vert.qsb b/src/quick/scenegraph/shaders_ng/outlinedtext.vert.qsb
new file mode 100644
index 0000000000..6aee048faa
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag b/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag
new file mode 100644
index 0000000000..9a3d8cf5ed
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag
@@ -0,0 +1,33 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 1) in vec2 sCoordUp;
+layout(location = 2) in vec2 sCoordDown;
+layout(location = 3) in vec2 sCoordLeft;
+layout(location = 4) in vec2 sCoordRight;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ // must match styledtext
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+ vec4 styleColor;
+ vec2 shift;
+} ubuf;
+
+void main()
+{
+ float glyph = texture(_qt_texture, sampleCoord).a; // take .a instead of .r
+ float outline = clamp(clamp(texture(_qt_texture, sCoordUp).a +
+ texture(_qt_texture, sCoordDown).a +
+ texture(_qt_texture, sCoordLeft).a +
+ texture(_qt_texture, sCoordRight).a,
+ 0.0, 1.0) - glyph,
+ 0.0, 1.0);
+ fragColor = outline * ubuf.styleColor + glyph * ubuf.color;
+}
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag.qsb b/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag.qsb
new file mode 100644
index 0000000000..6e6b1ab6c2
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/shadereffect.frag b/src/quick/scenegraph/shaders_ng/shadereffect.frag
new file mode 100644
index 0000000000..bde493f6ce
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/shadereffect.frag
@@ -0,0 +1,16 @@
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord0;
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform qt_buf {
+ mat4 qt_Matrix;
+ float qt_Opacity;
+} qt_ubuf;
+
+layout(binding = 1) uniform sampler2D source;
+
+void main()
+{
+ fragColor = texture(source, qt_TexCoord0) * qt_ubuf.qt_Opacity;
+}
diff --git a/src/quick/scenegraph/shaders_ng/shadereffect.frag.qsb b/src/quick/scenegraph/shaders_ng/shadereffect.frag.qsb
new file mode 100644
index 0000000000..4b08ee2ce4
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/shadereffect.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/shadereffect.vert b/src/quick/scenegraph/shaders_ng/shadereffect.vert
new file mode 100644
index 0000000000..ae65059f19
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/shadereffect.vert
@@ -0,0 +1,19 @@
+#version 440
+
+layout(location = 0) in vec4 qt_Vertex;
+layout(location = 1) in vec2 qt_MultiTexCoord0;
+
+layout(location = 0) out vec2 qt_TexCoord0;
+
+layout(std140, binding = 0) uniform qt_buf {
+ mat4 qt_Matrix;
+ float qt_Opacity;
+} qt_ubuf; // must use a name that does not clash with custom code when no uniform blocks
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ qt_TexCoord0 = qt_MultiTexCoord0;
+ gl_Position = qt_ubuf.qt_Matrix * qt_Vertex;
+}
diff --git a/src/quick/scenegraph/shaders_ng/shadereffect.vert.qsb b/src/quick/scenegraph/shaders_ng/shadereffect.vert.qsb
new file mode 100644
index 0000000000..4a8c646a21
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/shadereffect.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/smoothcolor.frag b/src/quick/scenegraph/shaders_ng/smoothcolor.frag
new file mode 100644
index 0000000000..ede283be0c
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/smoothcolor.frag
@@ -0,0 +1,9 @@
+#version 440
+
+layout(location = 0) in vec4 color;
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ fragColor = color;
+}
diff --git a/src/quick/scenegraph/shaders_ng/smoothcolor.frag.qsb b/src/quick/scenegraph/shaders_ng/smoothcolor.frag.qsb
new file mode 100644
index 0000000000..f99cdf1176
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/smoothcolor.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/smoothcolor.vert b/src/quick/scenegraph/shaders_ng/smoothcolor.vert
new file mode 100644
index 0000000000..03a3ff8975
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/smoothcolor.vert
@@ -0,0 +1,51 @@
+#version 440
+
+layout(location = 0) in vec4 vertex;
+layout(location = 1) in vec4 vertexColor;
+layout(location = 2) in vec4 vertexOffset;
+
+layout(location = 0) out vec4 color;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 pixelSize;
+ float opacity;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ vec4 pos = ubuf.matrix * vertex;
+ gl_Position = pos;
+
+ if (vertexOffset.x != 0.) {
+ vec4 delta = ubuf.matrix[0] * vertexOffset.x;
+ vec2 dir = delta.xy * pos.w - pos.xy * delta.w;
+ vec2 ndir = .5 * ubuf.pixelSize * normalize(dir / ubuf.pixelSize);
+ dir -= ndir * delta.w * pos.w;
+ float numerator = dot(dir, ndir * pos.w * pos.w);
+ float scale = 0.0;
+ if (numerator < 0.0)
+ scale = 1.0;
+ else
+ scale = min(1.0, numerator / dot(dir, dir));
+ gl_Position += scale * delta;
+ }
+
+ if (vertexOffset.y != 0.) {
+ vec4 delta = ubuf.matrix[1] * vertexOffset.y;
+ vec2 dir = delta.xy * pos.w - pos.xy * delta.w;
+ vec2 ndir = .5 * ubuf.pixelSize * normalize(dir / ubuf.pixelSize);
+ dir -= ndir * delta.w * pos.w;
+ float numerator = dot(dir, ndir * pos.w * pos.w);
+ float scale = 0.0;
+ if (numerator < 0.0)
+ scale = 1.0;
+ else
+ scale = min(1.0, numerator / dot(dir, dir));
+ gl_Position += scale * delta;
+ }
+
+ color = vertexColor * ubuf.opacity;
+}
diff --git a/src/quick/scenegraph/shaders_ng/smoothcolor.vert.qsb b/src/quick/scenegraph/shaders_ng/smoothcolor.vert.qsb
new file mode 100644
index 0000000000..59c4104a2c
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/smoothcolor.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/smoothtexture.frag b/src/quick/scenegraph/shaders_ng/smoothtexture.frag
new file mode 100644
index 0000000000..b06764ad95
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/smoothtexture.frag
@@ -0,0 +1,13 @@
+#version 440
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 1) in float vertexOpacity;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D qt_Texture;
+
+void main()
+{
+ fragColor = texture(qt_Texture, texCoord) * vertexOpacity;
+}
diff --git a/src/quick/scenegraph/shaders_ng/smoothtexture.frag.qsb b/src/quick/scenegraph/shaders_ng/smoothtexture.frag.qsb
new file mode 100644
index 0000000000..ffaecbb56c
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/smoothtexture.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/smoothtexture.vert b/src/quick/scenegraph/shaders_ng/smoothtexture.vert
new file mode 100644
index 0000000000..965c837852
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/smoothtexture.vert
@@ -0,0 +1,61 @@
+#version 440
+
+layout(location = 0) in vec4 vertex;
+layout(location = 1) in vec2 multiTexCoord;
+layout(location = 2) in vec2 vertexOffset;
+layout(location = 3) in vec2 texCoordOffset;
+
+layout(location = 0) out vec2 texCoord;
+layout(location = 1) out float vertexOpacity;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float opacity;
+ vec2 pixelSize;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ vec4 pos = ubuf.qt_Matrix * vertex;
+ gl_Position = pos;
+ texCoord = multiTexCoord;
+
+ if (vertexOffset.x != 0.) {
+ vec4 delta = ubuf.qt_Matrix[0] * vertexOffset.x;
+ vec2 dir = delta.xy * pos.w - pos.xy * delta.w;
+ vec2 ndir = .5 * ubuf.pixelSize * normalize(dir / ubuf.pixelSize);
+ dir -= ndir * delta.w * pos.w;
+ float numerator = dot(dir, ndir * pos.w * pos.w);
+ float scale = 0.0;
+ if (numerator < 0.0)
+ scale = 1.0;
+ else
+ scale = min(1.0, numerator / dot(dir, dir));
+ gl_Position += scale * delta;
+ texCoord.x += scale * texCoordOffset.x;
+ }
+
+ if (vertexOffset.y != 0.) {
+ vec4 delta = ubuf.qt_Matrix[1] * vertexOffset.y;
+ vec2 dir = delta.xy * pos.w - pos.xy * delta.w;
+ vec2 ndir = .5 * ubuf.pixelSize * normalize(dir / ubuf.pixelSize);
+ dir -= ndir * delta.w * pos.w;
+ float numerator = dot(dir, ndir * pos.w * pos.w);
+ float scale = 0.0;
+ if (numerator < 0.0)
+ scale = 1.0;
+ else
+ scale = min(1.0, numerator / dot(dir, dir));
+ gl_Position += scale * delta;
+ texCoord.y += scale * texCoordOffset.y;
+ }
+
+ bool onEdge = any(notEqual(vertexOffset, vec2(0.)));
+ bool outerEdge = all(equal(texCoordOffset, vec2(0.)));
+ if (onEdge && outerEdge)
+ vertexOpacity = 0.;
+ else
+ vertexOpacity = ubuf.opacity;
+}
diff --git a/src/quick/scenegraph/shaders_ng/smoothtexture.vert.qsb b/src/quick/scenegraph/shaders_ng/smoothtexture.vert.qsb
new file mode 100644
index 0000000000..b7715d4dd5
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/smoothtexture.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/sprite.frag b/src/quick/scenegraph/shaders_ng/sprite.frag
new file mode 100644
index 0000000000..338f5e957e
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/sprite.frag
@@ -0,0 +1,22 @@
+#version 440
+
+layout(location = 0) in vec4 fTexS;
+layout(location = 1) in float progress;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D tex;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 animPos;
+ vec3 animData;
+ float opacity;
+} ubuf;
+
+void main()
+{
+ fragColor = mix(texture(tex, fTexS.xy),
+ texture(tex, fTexS.zw),
+ progress) * ubuf.opacity;
+}
diff --git a/src/quick/scenegraph/shaders_ng/sprite.frag.qsb b/src/quick/scenegraph/shaders_ng/sprite.frag.qsb
new file mode 100644
index 0000000000..45d5bc14ee
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/sprite.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/sprite.vert b/src/quick/scenegraph/shaders_ng/sprite.vert
new file mode 100644
index 0000000000..b76e2b206f
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/sprite.vert
@@ -0,0 +1,29 @@
+#version 440
+
+layout(location = 0) in vec2 vPos;
+layout(location = 1) in vec2 vTex;
+
+layout(location = 0) out vec4 fTexS;
+layout(location = 1) out float progress;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 animPos; // x,y, x,y (two frames for interpolation)
+ vec3 animData; // w,h(premultiplied of anim), interpolation progress
+ float opacity;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ progress = ubuf.animData.z;
+
+ // Calculate frame location in texture
+ fTexS.xy = ubuf.animPos.xy + vTex.xy * ubuf.animData.xy;
+
+ // Next frame is also passed, for interpolation
+ fTexS.zw = ubuf.animPos.zw + vTex.xy * ubuf.animData.xy;
+
+ gl_Position = ubuf.matrix * vec4(vPos.x, vPos.y, 0, 1);
+}
diff --git a/src/quick/scenegraph/shaders_ng/sprite.vert.qsb b/src/quick/scenegraph/shaders_ng/sprite.vert.qsb
new file mode 100644
index 0000000000..b55f881734
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/sprite.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/stencilclip.frag b/src/quick/scenegraph/shaders_ng/stencilclip.frag
new file mode 100644
index 0000000000..3f6222389d
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/stencilclip.frag
@@ -0,0 +1,8 @@
+#version 440
+
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ fragColor = vec4(0.81, 0.83, 0.12, 1.0); // Trolltech green ftw!
+}
diff --git a/src/quick/scenegraph/shaders_ng/stencilclip.frag.qsb b/src/quick/scenegraph/shaders_ng/stencilclip.frag.qsb
new file mode 100644
index 0000000000..6ae7a51f7a
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/stencilclip.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/stencilclip.vert b/src/quick/scenegraph/shaders_ng/stencilclip.vert
new file mode 100644
index 0000000000..d8b491f775
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/stencilclip.vert
@@ -0,0 +1,14 @@
+#version 440
+
+layout(location = 0) in vec4 vCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ gl_Position = ubuf.matrix * vCoord;
+}
diff --git a/src/quick/scenegraph/shaders_ng/stencilclip.vert.qsb b/src/quick/scenegraph/shaders_ng/stencilclip.vert.qsb
new file mode 100644
index 0000000000..ce2ed3c5b3
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/stencilclip.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/styledtext.frag b/src/quick/scenegraph/shaders_ng/styledtext.frag
new file mode 100644
index 0000000000..0b16396037
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/styledtext.frag
@@ -0,0 +1,26 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 1) in vec2 shiftedSampleCoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+ // the above must stay compatible with textmask/8bittextmask
+ vec4 styleColor;
+ vec2 shift;
+} ubuf;
+
+void main()
+{
+ float glyph = texture(_qt_texture, sampleCoord).r;
+ float style = clamp(texture(_qt_texture, shiftedSampleCoord).r - glyph,
+ 0.0, 1.0);
+ fragColor = style * ubuf.styleColor + glyph * ubuf.color;
+}
diff --git a/src/quick/scenegraph/shaders_ng/styledtext.frag.qsb b/src/quick/scenegraph/shaders_ng/styledtext.frag.qsb
new file mode 100644
index 0000000000..66ebc5f827
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/styledtext.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/styledtext.vert b/src/quick/scenegraph/shaders_ng/styledtext.vert
new file mode 100644
index 0000000000..10565107c6
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/styledtext.vert
@@ -0,0 +1,26 @@
+#version 440
+
+layout(location = 0) in vec4 vCoord;
+layout(location = 1) in vec2 tCoord;
+
+layout(location = 0) out vec2 sampleCoord;
+layout(location = 1) out vec2 shiftedSampleCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+ // the above must stay compatible with textmask/8bittextmask
+ vec4 styleColor;
+ vec2 shift;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ sampleCoord = tCoord * ubuf.textureScale;
+ shiftedSampleCoord = (tCoord - ubuf.shift) * ubuf.textureScale;
+ gl_Position = ubuf.matrix * floor((vCoord * ubuf.dpr) + 0.5) / ubuf.dpr;
+}
diff --git a/src/quick/scenegraph/shaders_ng/styledtext.vert.qsb b/src/quick/scenegraph/shaders_ng/styledtext.vert.qsb
new file mode 100644
index 0000000000..9a27ed0eb8
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/styledtext.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/styledtext_a.frag b/src/quick/scenegraph/shaders_ng/styledtext_a.frag
new file mode 100644
index 0000000000..b673137895
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/styledtext_a.frag
@@ -0,0 +1,26 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 1) in vec2 shiftedSampleCoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+ // the above must stay compatible with textmask/8bittextmask
+ vec4 styleColor;
+ vec2 shift;
+} ubuf;
+
+void main()
+{
+ float glyph = texture(_qt_texture, sampleCoord).a; // take .a instead of .r
+ float style = clamp(texture(_qt_texture, shiftedSampleCoord).a - glyph,
+ 0.0, 1.0);
+ fragColor = style * ubuf.styleColor + glyph * ubuf.color;
+}
diff --git a/src/quick/scenegraph/shaders_ng/styledtext_a.frag.qsb b/src/quick/scenegraph/shaders_ng/styledtext_a.frag.qsb
new file mode 100644
index 0000000000..9dd4137072
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/styledtext_a.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/textmask.frag b/src/quick/scenegraph/shaders_ng/textmask.frag
new file mode 100644
index 0000000000..518d5c965f
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/textmask.frag
@@ -0,0 +1,19 @@
+#version 440
+
+layout(location = 0) in vec2 sampleCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D _qt_texture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+} ubuf;
+
+void main()
+{
+ vec4 glyph = texture(_qt_texture, sampleCoord);
+ fragColor = vec4(glyph.rgb * ubuf.color.a, glyph.a);
+}
diff --git a/src/quick/scenegraph/shaders_ng/textmask.frag.qsb b/src/quick/scenegraph/shaders_ng/textmask.frag.qsb
new file mode 100644
index 0000000000..b16da4d76a
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/textmask.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/textmask.vert b/src/quick/scenegraph/shaders_ng/textmask.vert
new file mode 100644
index 0000000000..d7d3bf892e
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/textmask.vert
@@ -0,0 +1,21 @@
+#version 440
+
+layout(location = 0) in vec4 vCoord;
+layout(location = 1) in vec2 tCoord;
+
+layout(location = 0) out vec2 sampleCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec4 color;
+ vec2 textureScale;
+ float dpr;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ sampleCoord = tCoord * ubuf.textureScale;
+ gl_Position = ubuf.matrix * floor((vCoord * ubuf.dpr) + 0.5) / ubuf.dpr;
+}
diff --git a/src/quick/scenegraph/shaders_ng/textmask.vert.qsb b/src/quick/scenegraph/shaders_ng/textmask.vert.qsb
new file mode 100644
index 0000000000..ae196ed0ad
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/textmask.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/texture.frag b/src/quick/scenegraph/shaders_ng/texture.frag
new file mode 100644
index 0000000000..bd22f817e0
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/texture.frag
@@ -0,0 +1,16 @@
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float opacity;
+} ubuf;
+
+layout(binding = 1) uniform sampler2D qt_Texture;
+
+void main()
+{
+ fragColor = texture(qt_Texture, qt_TexCoord) * ubuf.opacity;
+}
diff --git a/src/quick/scenegraph/shaders_ng/texture.frag.qsb b/src/quick/scenegraph/shaders_ng/texture.frag.qsb
new file mode 100644
index 0000000000..3f4aa3713c
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/texture.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/texture.vert b/src/quick/scenegraph/shaders_ng/texture.vert
new file mode 100644
index 0000000000..537852d2bd
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/texture.vert
@@ -0,0 +1,19 @@
+#version 440
+
+layout(location = 0) in vec4 qt_VertexPosition;
+layout(location = 1) in vec2 qt_VertexTexCoord;
+
+layout(location = 0) out vec2 qt_TexCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float opacity;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ qt_TexCoord = qt_VertexTexCoord;
+ gl_Position = ubuf.qt_Matrix * qt_VertexPosition;
+}
diff --git a/src/quick/scenegraph/shaders_ng/texture.vert.qsb b/src/quick/scenegraph/shaders_ng/texture.vert.qsb
new file mode 100644
index 0000000000..bf0bc7d9fa
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/texture.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/vertexcolor.frag b/src/quick/scenegraph/shaders_ng/vertexcolor.frag
new file mode 100644
index 0000000000..ede283be0c
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/vertexcolor.frag
@@ -0,0 +1,9 @@
+#version 440
+
+layout(location = 0) in vec4 color;
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ fragColor = color;
+}
diff --git a/src/quick/scenegraph/shaders_ng/vertexcolor.frag.qsb b/src/quick/scenegraph/shaders_ng/vertexcolor.frag.qsb
new file mode 100644
index 0000000000..93965a55dd
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/vertexcolor.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/vertexcolor.vert b/src/quick/scenegraph/shaders_ng/vertexcolor.vert
new file mode 100644
index 0000000000..bfb9a95073
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/vertexcolor.vert
@@ -0,0 +1,19 @@
+#version 440
+
+layout(location = 0) in vec4 vertexCoord;
+layout(location = 1) in vec4 vertexColor;
+
+layout(location = 0) out vec4 color;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ float opacity;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ gl_Position = ubuf.matrix * vertexCoord;
+ color = vertexColor * ubuf.opacity;
+}
diff --git a/src/quick/scenegraph/shaders_ng/vertexcolor.vert.qsb b/src/quick/scenegraph/shaders_ng/vertexcolor.vert.qsb
new file mode 100644
index 0000000000..98abe4ef6f
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/vertexcolor.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/visualization.frag b/src/quick/scenegraph/shaders_ng/visualization.frag
new file mode 100644
index 0000000000..29f718fe5d
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/visualization.frag
@@ -0,0 +1,19 @@
+#version 440
+
+layout(location = 0) in vec2 pos;
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ mat4 rotation;
+ vec4 color;
+ float pattern;
+ int projection;
+} ubuf;
+
+void main(void)
+{
+ vec4 c = ubuf.color;
+ c.xyz += pow(max(sin(pos.x + pos.y), 0.0), 2.0) * ubuf.pattern * 0.25;
+ fragColor = c;
+}
diff --git a/src/quick/scenegraph/shaders_ng/visualization.frag.qsb b/src/quick/scenegraph/shaders_ng/visualization.frag.qsb
new file mode 100644
index 0000000000..eadad927dc
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/visualization.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/visualization.vert b/src/quick/scenegraph/shaders_ng/visualization.vert
new file mode 100644
index 0000000000..e2447948c2
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/visualization.vert
@@ -0,0 +1,30 @@
+#version 440
+
+layout(location = 0) in vec4 v;
+layout(location = 0) out vec2 pos;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ mat4 rotation;
+ vec4 color;
+ float pattern;
+ int projection;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; float gl_PointSize; };
+
+void main()
+{
+ vec4 p = ubuf.matrix * v;
+
+ if (ubuf.projection != 0) {
+ vec4 proj = ubuf.rotation * p;
+ gl_Position = vec4(proj.x, proj.y, 0, proj.z);
+ } else {
+ gl_Position = p;
+ }
+
+ pos = v.xy * 1.37;
+
+ gl_PointSize = 1.0;
+}
diff --git a/src/quick/scenegraph/shaders_ng/visualization.vert.qsb b/src/quick/scenegraph/shaders_ng/visualization.vert.qsb
new file mode 100644
index 0000000000..7ba27cb4b5
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/visualization.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp
index 981ea089be..f15ea67b46 100644
--- a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp
+++ b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp
@@ -55,7 +55,7 @@ QT_BEGIN_NAMESPACE
#define QT_MINIMUM_DYNAMIC_FBO_SIZE 64U
QSGPainterTexture::QSGPainterTexture()
- : QSGPlainTexture()
+ : QSGPlainTexture(*(new QSGPainterTexturePrivate))
{
m_retain_image = true;
}
@@ -73,6 +73,16 @@ void QSGPainterTexture::bind()
m_dirty_rect = QRect();
}
+void QSGPainterTexturePrivate::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_Q(QSGPainterTexture);
+ if (!q->m_dirty_rect.isNull()) {
+ q->setImage(q->m_image);
+ q->m_dirty_rect = QRect();
+ }
+ QSGPlainTexturePrivate::updateRhiTexture(rhi, resourceUpdates);
+}
+
QSGDefaultPainterNode::QSGDefaultPainterNode(QQuickPaintedItem *item)
: QSGPainterNode()
, m_preferredRenderTarget(QQuickPaintedItem::Image)
@@ -126,6 +136,7 @@ void QSGDefaultPainterNode::paint()
return;
painter.begin(&m_image);
} else {
+ Q_ASSERT(!m_context->rhi());
if (!m_gl_device) {
m_gl_device = new QOpenGLPaintDevice(m_fboSize);
m_gl_device->setPaintFlipped(true);
@@ -237,7 +248,7 @@ void QSGDefaultPainterNode::updateGeometry()
void QSGDefaultPainterNode::updateRenderTarget()
{
- if (!m_extensionsChecked) {
+ if (!m_extensionsChecked && !m_context->rhi()) {
QOpenGLExtensions *e = static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions());
m_multisamplingSupported = e->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)
&& e->hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit);
@@ -250,7 +261,10 @@ void QSGDefaultPainterNode::updateRenderTarget()
if (m_preferredRenderTarget == QQuickPaintedItem::Image) {
m_actualRenderTarget = QQuickPaintedItem::Image;
} else {
- if (!m_multisamplingSupported && m_smoothPainting)
+ // Image is the only option when there is no multisample framebuffer
+ // support and smooth painting is wanted, and when using the RHI. The
+ // latter may change in the future.
+ if ((!m_multisamplingSupported && m_smoothPainting) || m_context->rhi())
m_actualRenderTarget = QQuickPaintedItem::Image;
else
m_actualRenderTarget = m_preferredRenderTarget;
@@ -265,7 +279,9 @@ void QSGDefaultPainterNode::updateRenderTarget()
}
if (m_actualRenderTarget == QQuickPaintedItem::FramebufferObject ||
- m_actualRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject) {
+ m_actualRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject)
+ {
+ Q_ASSERT(!m_context->rhi());
const QOpenGLContext *ctx = m_context->openglContext();
if (m_fbo && !m_dirtyGeometry && (!ctx->format().samples() || !m_multisamplingSupported))
return;
diff --git a/src/quick/scenegraph/util/qsgdefaultpainternode_p.h b/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
index 084fc1e004..a86f7397be 100644
--- a/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
+++ b/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
@@ -53,7 +53,7 @@
#include <private/qsgadaptationlayer_p.h>
#include "qsgtexturematerial.h"
-#include "qsgtexture_p.h"
+#include "qsgplaintexture_p.h"
#include <QtQuick/qquickpainteditem.h>
@@ -64,9 +64,11 @@ QT_BEGIN_NAMESPACE
class QOpenGLFramebufferObject;
class QOpenGLPaintDevice;
class QSGDefaultRenderContext;
+class QSGPainterTexturePrivate;
class Q_QUICK_PRIVATE_EXPORT QSGPainterTexture : public QSGPlainTexture
{
+ Q_DECLARE_PRIVATE(QSGPainterTexture)
public:
QSGPainterTexture();
@@ -78,6 +80,13 @@ private:
QRect m_dirty_rect;
};
+class QSGPainterTexturePrivate : public QSGPlainTexturePrivate
+{
+ Q_DECLARE_PUBLIC(QSGPainterTexture)
+public:
+ void updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates) override;
+};
+
class Q_QUICK_PRIVATE_EXPORT QSGDefaultPainterNode : public QSGPainterNode
{
public:
diff --git a/src/quick/scenegraph/util/qsgdepthstencilbuffer.cpp b/src/quick/scenegraph/util/qsgdepthstencilbuffer.cpp
index 94912778f8..1154c06d7c 100644
--- a/src/quick/scenegraph/util/qsgdepthstencilbuffer.cpp
+++ b/src/quick/scenegraph/util/qsgdepthstencilbuffer.cpp
@@ -183,7 +183,9 @@ void QSGDefaultDepthStencilBuffer::free()
QSGDepthStencilBufferManager::~QSGDepthStencilBufferManager()
{
for (Hash::const_iterator it = m_buffers.constBegin(), cend = m_buffers.constEnd(); it != cend; ++it) {
- QSGDepthStencilBuffer *buffer = it.value().data();
+ QSharedPointer<QSGDepthStencilBuffer> buffer = it.value().toStrongRef();
+ Q_ASSERT_X(buffer, "~QSGDepthStencilBufferManager",
+ "~QSGDepthStencilBuffer is supposed to unregister from the manager");
buffer->free();
buffer->m_manager = nullptr;
}
diff --git a/src/quick/scenegraph/util/qsgengine.cpp b/src/quick/scenegraph/util/qsgengine.cpp
index 91fa46033c..4880d98871 100644
--- a/src/quick/scenegraph/util/qsgengine.cpp
+++ b/src/quick/scenegraph/util/qsgengine.cpp
@@ -42,7 +42,7 @@
#include <QtQuick/qsgtexture.h>
#include <private/qsgcontext_p.h>
#include <private/qsgrenderer_p.h>
-#include <private/qsgtexture_p.h>
+#include <private/qsgplaintexture_p.h>
#if QT_CONFIG(opengl)
# include <QtGui/QOpenGLContext>
@@ -69,6 +69,10 @@ QT_BEGIN_NAMESPACE
Most of the time you will instead want to subclass QQuickItem and insert
your QSGNode in a normal QtQuick scene by overriding QQuickItem::updatePaintNode().
+ \warning This class is only suitable when working directly with OpenGL. It
+ is not compatible with the \l{Scene Graph Adaptations}{RHI-based rendering
+ path}.
+
\sa QSGAbstractRenderer
*/
@@ -126,12 +130,29 @@ void QSGEngine::initialize(QOpenGLContext *context)
#endif
if (d->sgRenderContext && !d->sgRenderContext->isValid()) {
d->sgRenderContext->setAttachToGraphicsContext(false);
- d->sgRenderContext->initialize(context);
+#if QT_CONFIG(opengl)
+ QSGDefaultRenderContext *rc = qobject_cast<QSGDefaultRenderContext *>(d->sgRenderContext.data());
+ if (rc) {
+ QSGDefaultRenderContext::InitParams params;
+ params.sampleCount = qMax(1, context->format().samples());
+ params.openGLContext = context;
+ // leave the size hint and surface unset, we do not know, that's fine
+ rc->initialize(&params);
+ } else {
+ d->sgRenderContext->initialize(nullptr);
+ }
+#else
+ d->sgRenderContext->initialize(nullptr);
+#endif
#if QT_CONFIG(opengl)
if (context)
connect(context, &QOpenGLContext::aboutToBeDestroyed, this, &QSGEngine::invalidate);
#endif
}
+
+#if !QT_CONFIG(opengl)
+ Q_UNUSED(context);
+#endif
}
/*!
diff --git a/src/quick/scenegraph/util/qsgengine.h b/src/quick/scenegraph/util/qsgengine.h
index e48b7784ae..c5a59b47e7 100644
--- a/src/quick/scenegraph/util/qsgengine.h
+++ b/src/quick/scenegraph/util/qsgengine.h
@@ -54,6 +54,8 @@ class QSGRectangleNode;
class QSGImageNode;
class QSGNinePatchNode;
+// ### Qt 6: Remove or redesign.
+
class Q_QUICK_EXPORT QSGEngine : public QObject
{
Q_OBJECT
diff --git a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp
index 28f6113a60..87941bf31a 100644
--- a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp
+++ b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp
@@ -116,6 +116,51 @@ void FlatColorMaterialShader::initialize()
}
+class FlatColorMaterialRhiShader : public QSGMaterialRhiShader
+{
+public:
+ FlatColorMaterialRhiShader();
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+FlatColorMaterialRhiShader::FlatColorMaterialRhiShader()
+{
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/flatcolor.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/flatcolor.frag.qsb"));
+}
+
+bool FlatColorMaterialRhiShader::updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial,
+ QSGMaterial *oldMaterial)
+{
+ Q_ASSERT(!oldMaterial || newMaterial->type() == oldMaterial->type());
+ QSGFlatColorMaterial *oldMat = static_cast<QSGFlatColorMaterial *>(oldMaterial);
+ QSGFlatColorMaterial *mat = static_cast<QSGFlatColorMaterial *>(newMaterial);
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ changed = true;
+ }
+
+ const QColor &c = mat->color();
+ if (!oldMat || c != oldMat->color() || state.isOpacityDirty()) {
+ const float opacity = state.opacity() * c.alphaF();
+ QVector4D v(c.redF() * opacity,
+ c.greenF() * opacity,
+ c.blueF() * opacity,
+ opacity);
+ Q_ASSERT(sizeof(v) == 16);
+ memcpy(buf->data() + 64, &v, 16);
+ changed = true;
+ }
+
+ return changed;
+}
+
/*!
\class QSGFlatColorMaterial
@@ -126,7 +171,7 @@ void FlatColorMaterialShader::initialize()
\inmodule QtQuick
\ingroup qtquick-scenegraph-materials
- \warning This utility class is only functional when running with the OpenGL
+ \warning This utility class is only functional when running with the default
backend of the Qt Quick scenegraph.
The flat color material will fill every pixel in a geometry using
@@ -150,10 +195,9 @@ void FlatColorMaterialShader::initialize()
QSGFlatColorMaterial::QSGFlatColorMaterial() : m_color(QColor(255, 255, 255))
{
+ setFlag(SupportsRhiShader, true);
}
-
-
/*!
\fn const QColor &QSGFlatColorMaterial::color() const
@@ -193,7 +237,10 @@ QSGMaterialType *QSGFlatColorMaterial::type() const
QSGMaterialShader *QSGFlatColorMaterial::createShader() const
{
- return new FlatColorMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new FlatColorMaterialRhiShader;
+ else
+ return new FlatColorMaterialShader;
}
diff --git a/src/quick/scenegraph/util/qsgatlastexture.cpp b/src/quick/scenegraph/util/qsgopenglatlastexture.cpp
index 921ed0c1fc..75a874424a 100644
--- a/src/quick/scenegraph/util/qsgatlastexture.cpp
+++ b/src/quick/scenegraph/util/qsgopenglatlastexture.cpp
@@ -37,7 +37,7 @@
**
****************************************************************************/
-#include "qsgatlastexture_p.h"
+#include "qsgopenglatlastexture_p.h"
#include <QtCore/QVarLengthArray>
#include <QtCore/QElapsedTimer>
@@ -71,24 +71,22 @@ static QElapsedTimer qsg_renderer_timer;
DEFINE_BOOL_CONFIG_OPTION(qsgEnableCompressedAtlas, QSG_ENABLE_COMPRESSED_ATLAS)
-namespace QSGAtlasTexture
+namespace QSGOpenGLAtlasTexture
{
-Manager::Manager()
+Manager::Manager(const QSize &surfacePixelSize)
: m_atlas(nullptr)
{
QOpenGLContext *gl = QOpenGLContext::currentContext();
Q_ASSERT(gl);
- QSurface *surface = gl->surface();
- QSize surfaceSize = surface->size();
int max;
gl->functions()->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
- int w = qMin(max, qt_sg_envInt("QSG_ATLAS_WIDTH", qMax(512U, qNextPowerOfTwo(surfaceSize.width() - 1))));
- int h = qMin(max, qt_sg_envInt("QSG_ATLAS_HEIGHT", qMax(512U, qNextPowerOfTwo(surfaceSize.height() - 1))));
+ int w = qMin(max, qt_sg_envInt("QSG_ATLAS_WIDTH", qMax(512U, qNextPowerOfTwo(surfacePixelSize.width() - 1))));
+ int h = qMin(max, qt_sg_envInt("QSG_ATLAS_HEIGHT", qMax(512U, qNextPowerOfTwo(surfacePixelSize.height() - 1))));
- if (surface->surfaceClass() == QSurface::Window) {
- QWindow *window = static_cast<QWindow *>(surface);
+ if (gl->surface()->surfaceClass() == QSurface::Window) {
+ QWindow *window = static_cast<QWindow *>(gl->surface());
// Coverwindows, optimize for memory rather than speed
if ((window->type() & Qt::CoverWindow) == Qt::CoverWindow) {
w /= 2;
@@ -99,10 +97,9 @@ Manager::Manager()
m_atlas_size_limit = qt_sg_envInt("QSG_ATLAS_SIZE_LIMIT", qMax(w, h) / 2);
m_atlas_size = QSize(w, h);
- qCDebug(QSG_LOG_INFO, "texture atlas dimensions: %dx%d", w, h);
+ qCDebug(QSG_LOG_INFO, "opengl texture atlas dimensions: %dx%d", w, h);
}
-
Manager::~Manager()
{
Q_ASSERT(m_atlas == nullptr);
@@ -153,6 +150,10 @@ QSGTexture *Manager::create(const QSGCompressedTextureFactory *factory)
case QOpenGLTexture::RGB8_ETC2:
case QOpenGLTexture::RGBA8_ETC2_EAC:
case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2:
+ case QOpenGLTexture::RGB_DXT1:
+ case QOpenGLTexture::RGBA_DXT1:
+ case QOpenGLTexture::RGBA_DXT3:
+ case QOpenGLTexture::RGBA_DXT5:
break;
default:
return t;
@@ -161,8 +162,12 @@ QSGTexture *Manager::create(const QSGCompressedTextureFactory *factory)
QSize size = factory->m_textureData.size();
if (size.width() < m_atlas_size_limit && size.height() < m_atlas_size_limit) {
QHash<unsigned int, QSGCompressedAtlasTexture::Atlas*>::iterator i = m_atlases.find(format);
- if (i == m_atlases.end())
- i = m_atlases.insert(format, new QSGCompressedAtlasTexture::Atlas(m_atlas_size, format));
+ if (i == m_atlases.end()) {
+ // must be multiple of 4
+ QSize paddedSize(((m_atlas_size.width() + 3) / 4) * 4, ((m_atlas_size.height() + 3) / 4) * 4);
+ i = m_atlases.insert(format, new QSGCompressedAtlasTexture::Atlas(paddedSize, format));
+ }
+
// must be multiple of 4
QSize paddedSize(((size.width() + 3) / 4) * 4, ((size.height() + 3) / 4) * 4);
QByteArray data = factory->m_textureData.data();
@@ -593,4 +598,4 @@ QSGTexture *Texture::removedFromAtlas() const
QT_END_NAMESPACE
-#include "moc_qsgatlastexture_p.cpp"
+#include "moc_qsgopenglatlastexture_p.cpp"
diff --git a/src/quick/scenegraph/util/qsgatlastexture_p.h b/src/quick/scenegraph/util/qsgopenglatlastexture_p.h
index 14dc8f7958..f8dd7cdf02 100644
--- a/src/quick/scenegraph/util/qsgatlastexture_p.h
+++ b/src/quick/scenegraph/util/qsgopenglatlastexture_p.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QSGATLASTEXTURE_P_H
-#define QSGATLASTEXTURE_P_H
+#ifndef QSGOPENGLATLASTEXTURE_P_H
+#define QSGOPENGLATLASTEXTURE_P_H
//
// W A R N I N G
@@ -56,7 +56,7 @@
#include <QtGui/qopengl.h>
#include <QtQuick/QSGTexture>
-#include <QtQuick/private/qsgtexture_p.h>
+#include <QtQuick/private/qsgplaintexture_p.h>
#include <QtQuick/private/qsgareaallocator_p.h>
QT_BEGIN_NAMESPACE
@@ -66,7 +66,7 @@ namespace QSGCompressedAtlasTexture {
}
class QSGCompressedTextureFactory;
-namespace QSGAtlasTexture
+namespace QSGOpenGLAtlasTexture
{
class Texture;
@@ -78,7 +78,7 @@ class Manager : public QObject
Q_OBJECT
public:
- Manager();
+ Manager(const QSize &surfacePixelSize);
~Manager();
QSGTexture *create(const QImage &image, bool hasAlphaChannel);
diff --git a/src/quick/scenegraph/util/qsgplaintexture.cpp b/src/quick/scenegraph/util/qsgplaintexture.cpp
new file mode 100644
index 0000000000..fdebe03494
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgplaintexture.cpp
@@ -0,0 +1,457 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "qsgplaintexture_p.h"
+#include "qsgrhinativetextureimporter_p.h"
+#include <QtQuick/private/qsgcontext_p.h>
+#include <qmath.h>
+#include <private/qquickprofiler_p.h>
+#include <private/qqmlglobal_p.h>
+#include <QtGui/qguiapplication.h>
+#include <QtGui/qpa/qplatformnativeinterface.h>
+#if QT_CONFIG(opengl)
+# include <QtGui/qopenglcontext.h>
+# include <QtGui/qopenglfunctions.h>
+# include <QtGui/private/qopengltextureuploader_p.h>
+# include <private/qsgdefaultrendercontext_p.h>
+#endif
+#include <QtGui/private/qrhi_p.h>
+
+#if QT_CONFIG(opengl)
+static QElapsedTimer qsg_renderer_timer;
+#endif
+
+#ifndef GL_BGRA
+#define GL_BGRA 0x80E1
+#endif
+
+#ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT
+#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QSGPlainTexture::QSGPlainTexture()
+ : QSGTexture(*(new QSGPlainTexturePrivate))
+ , m_texture_id(0)
+ , m_texture(nullptr)
+ , m_has_alpha(false)
+ , m_dirty_texture(false)
+ , m_dirty_bind_options(false)
+ , m_owns_texture(true)
+ , m_mipmaps_generated(false)
+ , m_retain_image(false)
+ , m_mipmap_warned(false)
+{
+}
+
+QSGPlainTexture::QSGPlainTexture(QSGPlainTexturePrivate &dd)
+ : QSGTexture(dd)
+ , m_texture_id(0)
+ , m_texture(nullptr)
+ , m_has_alpha(false)
+ , m_dirty_texture(false)
+ , m_dirty_bind_options(false)
+ , m_owns_texture(true)
+ , m_mipmaps_generated(false)
+ , m_retain_image(false)
+ , m_mipmap_warned(false)
+{
+}
+
+QSGPlainTexture::~QSGPlainTexture()
+{
+#if QT_CONFIG(opengl)
+ if (m_texture_id && m_owns_texture && QOpenGLContext::currentContext())
+ QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_texture_id);
+#endif
+ if (m_texture && m_owns_texture)
+ delete m_texture;
+}
+
+void QSGPlainTexture::setImage(const QImage &image)
+{
+ m_image = image;
+ m_texture_size = image.size();
+ m_has_alpha = image.hasAlphaChannel();
+ m_dirty_texture = true;
+ m_dirty_bind_options = true;
+ m_mipmaps_generated = false;
+ }
+
+int QSGPlainTexture::textureId() const // legacy (GL-only)
+{
+ if (m_dirty_texture) {
+ if (m_image.isNull()) {
+ // The actual texture and id will be updated/deleted in a later bind()
+ // or ~QSGPlainTexture so just keep it minimal here.
+ return 0;
+ } else if (m_texture_id == 0){
+#if QT_CONFIG(opengl)
+ // Generate a texture id for use later and return it.
+ QOpenGLContext::currentContext()->functions()->glGenTextures(1, &const_cast<QSGPlainTexture *>(this)->m_texture_id);
+#endif
+ return m_texture_id;
+ }
+ }
+ return m_texture_id;
+}
+
+void QSGPlainTexture::setTextureId(int id) // legacy (GL-only)
+{
+#if QT_CONFIG(opengl)
+ if (m_texture_id && m_owns_texture)
+ QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_texture_id);
+#endif
+
+ m_texture_id = id;
+ m_dirty_texture = false;
+ m_dirty_bind_options = true;
+ m_image = QImage();
+ m_mipmaps_generated = false;
+}
+
+void QSGPlainTexture::bind() // legacy (GL-only)
+{
+#if QT_CONFIG(opengl)
+ QOpenGLContext *context = QOpenGLContext::currentContext();
+ QOpenGLFunctions *funcs = context->functions();
+ if (!m_dirty_texture) {
+ funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id);
+ if (mipmapFiltering() != QSGTexture::None && !m_mipmaps_generated) {
+ funcs->glGenerateMipmap(GL_TEXTURE_2D);
+ m_mipmaps_generated = true;
+ }
+ updateBindOptions(m_dirty_bind_options);
+ m_dirty_bind_options = false;
+ return;
+ }
+
+ m_dirty_texture = false;
+
+ bool profileFrames = QSG_LOG_TIME_TEXTURE().isDebugEnabled();
+ if (profileFrames)
+ qsg_renderer_timer.start();
+ Q_QUICK_SG_PROFILE_START_SYNCHRONIZED(QQuickProfiler::SceneGraphTexturePrepare,
+ QQuickProfiler::SceneGraphTextureDeletion);
+
+
+ if (m_image.isNull()) {
+ if (m_texture_id && m_owns_texture) {
+ funcs->glDeleteTextures(1, &m_texture_id);
+ qCDebug(QSG_LOG_TIME_TEXTURE, "plain texture deleted in %dms - %dx%d",
+ (int) qsg_renderer_timer.elapsed(),
+ m_texture_size.width(),
+ m_texture_size.height());
+ Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphTextureDeletion,
+ QQuickProfiler::SceneGraphTextureDeletionDelete);
+ }
+ m_texture_id = 0;
+ m_texture_size = QSize();
+ m_has_alpha = false;
+
+ return;
+ }
+
+ if (m_texture_id == 0)
+ funcs->glGenTextures(1, &m_texture_id);
+ funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id);
+
+ qint64 bindTime = 0;
+ if (profileFrames)
+ bindTime = qsg_renderer_timer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare,
+ QQuickProfiler::SceneGraphTexturePrepareBind);
+
+ // ### TODO: check for out-of-memory situations...
+
+ QOpenGLTextureUploader::BindOptions options = QOpenGLTextureUploader::PremultipliedAlphaBindOption;
+
+ // Downscale the texture to fit inside the max texture limit if it is too big.
+ // It would be better if the image was already downscaled to the right size,
+ // but this information is not always available at that time, so as a last
+ // resort we can do it here. Texture coordinates are normalized, so it
+ // won't cause any problems and actual texture sizes will be written
+ // based on QSGTexture::textureSize which is updated after this, so that
+ // should be ok.
+ int max;
+ if (auto rc = QSGDefaultRenderContext::from(context))
+ max = rc->maxTextureSize();
+ else
+ funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
+
+ m_texture_size = m_texture_size.boundedTo(QSize(max, max));
+
+ // Scale to a power of two size if mipmapping is requested and the
+ // texture is npot and npot textures are not properly supported.
+ if (mipmapFiltering() != QSGTexture::None
+ && !funcs->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)) {
+ options |= QOpenGLTextureUploader::PowerOfTwoBindOption;
+ }
+
+ updateBindOptions(m_dirty_bind_options);
+
+ QOpenGLTextureUploader::textureImage(GL_TEXTURE_2D, m_image, options, QSize(max, max));
+
+ qint64 uploadTime = 0;
+ if (profileFrames)
+ uploadTime = qsg_renderer_timer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare,
+ QQuickProfiler::SceneGraphTexturePrepareUpload);
+
+ if (mipmapFiltering() != QSGTexture::None) {
+ funcs->glGenerateMipmap(GL_TEXTURE_2D);
+ m_mipmaps_generated = true;
+ }
+
+ qint64 mipmapTime = 0;
+ if (profileFrames) {
+ mipmapTime = qsg_renderer_timer.nsecsElapsed();
+ qCDebug(QSG_LOG_TIME_TEXTURE,
+ "plain texture uploaded in: %dms (%dx%d), bind=%d, upload=%d, mipmap=%d%s",
+ int(mipmapTime / 1000000),
+ m_texture_size.width(), m_texture_size.height(),
+ int(bindTime / 1000000),
+ int((uploadTime - bindTime)/1000000),
+ int((mipmapTime - uploadTime)/1000000),
+ m_texture_size != m_image.size() ? " (scaled to GL_MAX_TEXTURE_SIZE)" : "");
+ }
+ Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphTexturePrepare,
+ QQuickProfiler::SceneGraphTexturePrepareMipmap);
+
+ m_texture_rect = QRectF(0, 0, 1, 1);
+
+ m_dirty_bind_options = false;
+ if (!m_retain_image)
+ m_image = QImage();
+#endif
+}
+
+void QSGPlainTexture::setTexture(QRhiTexture *texture) // RHI only
+{
+ if (m_texture && m_owns_texture && m_texture != texture)
+ delete m_texture;
+
+ m_texture = texture;
+ m_dirty_texture = false;
+ m_dirty_bind_options = true;
+ m_image = QImage();
+ m_mipmaps_generated = false;
+}
+
+void QSGPlainTexture::setTextureFromNativeObject(QRhi *rhi, QQuickWindow::NativeObjectType type,
+ const void *nativeObjectPtr, int nativeLayout,
+ const QSize &size, bool mipmap)
+{
+ Q_UNUSED(type);
+
+ QRhiTexture::Flags flags = 0;
+ if (mipmap)
+ flags |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
+
+ QRhiTexture *t = rhi->newTexture(QRhiTexture::RGBA8, size, 1, flags);
+
+ // ownership of the native object is never taken
+ QSGRhiNativeTextureImporter::buildWrapper(rhi, t, nativeObjectPtr, nativeLayout);
+
+ setTexture(t);
+}
+
+int QSGPlainTexturePrivate::comparisonKey() const
+{
+ Q_Q(const QSGPlainTexture);
+
+ // not textureId() as that would create an id when not yet done - that's not wanted here
+ if (q->m_texture_id)
+ return q->m_texture_id;
+
+ if (q->m_texture)
+ return int(qintptr(q->m_texture));
+
+ // two textures (and so materials) with not-yet-created texture underneath are never equal
+ return int(qintptr(q));
+}
+
+QRhiTexture *QSGPlainTexturePrivate::rhiTexture() const
+{
+ Q_Q(const QSGPlainTexture);
+ return q->m_texture;
+}
+
+void QSGPlainTexturePrivate::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_Q(QSGPlainTexture);
+
+ const bool hasMipMaps = q->mipmapFiltering() != QSGTexture::None;
+ const bool mipmappingChanged = q->m_texture && ((hasMipMaps && !q->m_texture->flags().testFlag(QRhiTexture::MipMapped)) // did not have it before
+ || (!hasMipMaps && q->m_texture->flags().testFlag(QRhiTexture::MipMapped))); // does not have it anymore
+
+ if (!q->m_dirty_texture) {
+ if (!q->m_texture)
+ return;
+ if (q->m_texture && !mipmappingChanged) {
+ if (hasMipMaps && !q->m_mipmaps_generated) {
+ resourceUpdates->generateMips(q->m_texture);
+ q->m_mipmaps_generated = true;
+ }
+ return;
+ }
+ }
+
+ if (q->m_image.isNull()) {
+ if (!q->m_dirty_texture && mipmappingChanged) {
+ // Full Mipmap Panic!
+ if (!q->m_mipmap_warned) {
+ qWarning("QSGPlainTexture: Mipmap settings changed without having image data available. "
+ "Call setImage() again or enable m_retain_image. "
+ "Falling back to previous mipmap filtering mode.");
+ q->m_mipmap_warned = true;
+ }
+ // leave the texture valid and rather ignore the mipmap mode change attempt
+ q->setMipmapFiltering(m_last_mipmap_filter);
+ return;
+ }
+
+ if (q->m_texture && q->m_owns_texture)
+ delete q->m_texture;
+
+ q->m_texture = nullptr;
+ q->m_texture_size = QSize();
+ q->m_has_alpha = false;
+
+ q->m_dirty_texture = false;
+ return;
+ }
+
+ q->m_dirty_texture = false;
+
+ QImage tmp;
+ bool bgra = false;
+ bool needsConvert = false;
+ if (q->m_image.format() == QImage::Format_RGB32 || q->m_image.format() == QImage::Format_ARGB32_Premultiplied) {
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ if (rhi->isTextureFormatSupported(QRhiTexture::BGRA8)) {
+ tmp = q->m_image;
+ bgra = true;
+ } else {
+ needsConvert = true;
+ }
+#else
+ needsConvert = true;
+#endif
+ } else if (q->m_image.format() == QImage::Format_RGBX8888 || q->m_image.format() == QImage::Format_RGBA8888_Premultiplied) {
+ tmp = q->m_image;
+ } else {
+ needsConvert = true;
+ }
+
+ if (needsConvert)
+ tmp = q->m_image.convertToFormat(QImage::Format_RGBA8888_Premultiplied);
+
+ // Downscale the texture to fit inside the max texture limit if it is too big.
+ // It would be better if the image was already downscaled to the right size,
+ // but this information is not always available at that time, so as a last
+ // resort we can do it here. Texture coordinates are normalized, so it
+ // won't cause any problems and actual texture sizes will be written
+ // based on QSGTexture::textureSize which is updated after this, so that
+ // should be ok.
+ const int max = rhi->resourceLimit(QRhi::TextureSizeMax);
+ if (tmp.width() > max || tmp.height() > max) {
+ tmp = tmp.scaled(qMin(max, tmp.width()), qMin(max, tmp.height()), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ q->m_texture_size = tmp.size();
+ }
+
+ if ((q->mipmapFiltering() != QSGTexture::None
+ || q->horizontalWrapMode() != QSGTexture::ClampToEdge
+ || q->verticalWrapMode() != QSGTexture::ClampToEdge)
+ && !rhi->isFeatureSupported(QRhi::NPOTTextureRepeat))
+ {
+ const int w = qNextPowerOfTwo(tmp.width() - 1);
+ const int h = qNextPowerOfTwo(tmp.height() - 1);
+ if (tmp.width() != w || tmp.height() != h) {
+ tmp = tmp.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ q->m_texture_size = tmp.size();
+ }
+ }
+
+ bool needsRebuild = q->m_texture && q->m_texture->pixelSize() != q->m_texture_size;
+
+ if (mipmappingChanged) {
+ QRhiTexture::Flags f = q->m_texture->flags();
+ f.setFlag(QRhiTexture::MipMapped, hasMipMaps);
+ f.setFlag(QRhiTexture::UsedWithGenerateMips, hasMipMaps);
+ q->m_texture->setFlags(f);
+ needsRebuild = true;
+ }
+
+ if (!q->m_texture) {
+ QRhiTexture::Flags f = 0;
+ if (hasMipMaps)
+ f |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
+
+ q->m_texture = rhi->newTexture(bgra ? QRhiTexture::BGRA8 : QRhiTexture::RGBA8, q->m_texture_size, 1, f);
+ needsRebuild = true;
+ }
+
+ if (needsRebuild) {
+ if (!q->m_texture->build()) {
+ qWarning("Failed to build texture for QSGPlainTexture (size %dx%d)",
+ q->m_texture_size.width(), q->m_texture_size.height());
+ return;
+ }
+ }
+
+ if (tmp.width() * 4 != tmp.bytesPerLine())
+ tmp = tmp.copy();
+
+ resourceUpdates->uploadTexture(q->m_texture, tmp);
+
+ if (hasMipMaps) {
+ resourceUpdates->generateMips(q->m_texture);
+ q->m_mipmaps_generated = true;
+ }
+
+ m_last_mipmap_filter = q->mipmapFiltering();
+ q->m_texture_rect = QRectF(0, 0, 1, 1);
+
+ if (!q->m_retain_image)
+ q->m_image = QImage();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgtexture_p.h b/src/quick/scenegraph/util/qsgplaintexture_p.h
index 18dd5eff68..1eb0b59d2e 100644
--- a/src/quick/scenegraph/util/qsgtexture_p.h
+++ b/src/quick/scenegraph/util/qsgplaintexture_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QSGTEXTURE_P_H
-#define QSGTEXTURE_P_H
+#ifndef QSGPLAINTEXTURE_P_H
+#define QSGPLAINTEXTURE_P_H
//
// W A R N I N G
@@ -51,36 +51,18 @@
// We mean it.
//
-#include <QtQuick/qtquickglobal.h>
-#include <private/qobject_p.h>
-#if QT_CONFIG(opengl)
-# include <QtGui/qopengl.h>
-#endif
-#include "qsgtexture.h"
-#include <QtQuick/private/qsgcontext_p.h>
+#include <QtQuick/private/qtquickglobal_p.h>
+#include <QtQuick/private/qsgtexture_p.h>
+#include <QtQuick/qquickwindow.h>
QT_BEGIN_NAMESPACE
-class QSGTexturePrivate : public QObjectPrivate
-{
- Q_DECLARE_PUBLIC(QSGTexture)
-public:
- QSGTexturePrivate();
-
- uint wrapChanged : 1;
- uint filteringChanged : 1;
- uint anisotropyChanged : 1;
-
- uint horizontalWrap : 2;
- uint verticalWrap : 2;
- uint mipmapMode : 2;
- uint filterMode : 2;
- uint anisotropyLevel: 3;
-};
+class QSGPlainTexturePrivate;
class Q_QUICK_PRIVATE_EXPORT QSGPlainTexture : public QSGTexture
{
Q_OBJECT
+ Q_DECLARE_PRIVATE(QSGPlainTexture)
public:
QSGPlainTexture();
~QSGPlainTexture() override;
@@ -103,6 +85,11 @@ public:
void bind() override;
+ void setTexture(QRhiTexture *texture);
+ void setTextureFromNativeObject(QRhi *rhi, QQuickWindow::NativeObjectType type,
+ const void *nativeObjectPtr, int nativeLayout,
+ const QSize &size, bool mipmap);
+
static QSGPlainTexture *fromImage(const QImage &image) {
QSGPlainTexture *t = new QSGPlainTexture();
t->setImage(image);
@@ -110,22 +97,35 @@ public:
}
protected:
+ QSGPlainTexture(QSGPlainTexturePrivate &dd);
+
QImage m_image;
uint m_texture_id;
QSize m_texture_size;
QRectF m_texture_rect;
+ QRhiTexture *m_texture;
uint m_has_alpha : 1;
uint m_dirty_texture : 1;
- uint m_dirty_bind_options : 1;
+ uint m_dirty_bind_options : 1; // legacy (GL-only)
uint m_owns_texture : 1;
uint m_mipmaps_generated : 1;
- uint m_retain_image: 1;
+ uint m_retain_image : 1;
+ uint m_mipmap_warned : 1; // RHI only
};
-Q_QUICK_PRIVATE_EXPORT bool qsg_safeguard_texture(QSGTexture *);
+class QSGPlainTexturePrivate : public QSGTexturePrivate
+{
+ Q_DECLARE_PUBLIC(QSGPlainTexture)
+public:
+ int comparisonKey() const override;
+ QRhiTexture *rhiTexture() const override;
+ void updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ QSGTexture::Filtering m_last_mipmap_filter = QSGTexture::None;
+};
QT_END_NAMESPACE
-#endif // QSGTEXTURE_P_H
+#endif // QSGPLAINTEXTURE_P_H
diff --git a/src/quick/scenegraph/util/qsgrhiatlastexture.cpp b/src/quick/scenegraph/util/qsgrhiatlastexture.cpp
new file mode 100644
index 0000000000..3dc1f5f526
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgrhiatlastexture.cpp
@@ -0,0 +1,491 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "qsgrhiatlastexture_p.h"
+
+#include <QtCore/QVarLengthArray>
+#include <QtCore/QElapsedTimer>
+#include <QtCore/QtMath>
+
+#include <QtGui/QWindow>
+
+#include <private/qqmlglobal_p.h>
+#include <private/qquickprofiler_p.h>
+#include <private/qsgdefaultrendercontext_p.h>
+#include <private/qsgtexture_p.h>
+#if 0
+#include <private/qsgcompressedtexture_p.h>
+#include <private/qsgcompressedatlastexture_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+int qt_sg_envInt(const char *name, int defaultValue);
+
+static QElapsedTimer qsg_renderer_timer;
+
+//DEFINE_BOOL_CONFIG_OPTION(qsgEnableCompressedAtlas, QSG_ENABLE_COMPRESSED_ATLAS)
+
+namespace QSGRhiAtlasTexture
+{
+
+Manager::Manager(QSGDefaultRenderContext *rc, const QSize &surfacePixelSize, QSurface *maybeSurface)
+ : m_rc(rc)
+ , 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))));
+
+ if (maybeSurface && maybeSurface->surfaceClass() == QSurface::Window) {
+ QWindow *window = static_cast<QWindow *>(maybeSurface);
+ // Coverwindows, optimize for memory rather than speed
+ if ((window->type() & Qt::CoverWindow) == Qt::CoverWindow) {
+ w /= 2;
+ h /= 2;
+ }
+ }
+
+ m_atlas_size_limit = qt_sg_envInt("QSG_ATLAS_SIZE_LIMIT", qMax(w, h) / 2);
+ m_atlas_size = QSize(w, h);
+
+ qCDebug(QSG_LOG_INFO, "rhi texture atlas dimensions: %dx%d", w, h);
+}
+
+Manager::~Manager()
+{
+ Q_ASSERT(m_atlas == nullptr);
+ Q_ASSERT(m_atlases.isEmpty());
+}
+
+void Manager::invalidate()
+{
+ if (m_atlas) {
+ m_atlas->invalidate();
+ m_atlas->deleteLater();
+ m_atlas = nullptr;
+ }
+
+ #if 0
+ QHash<unsigned int, QSGCompressedAtlasTexture::Atlas*>::iterator i = m_atlases.begin();
+ while (i != m_atlases.end()) {
+ i.value()->invalidate();
+ i.value()->deleteLater();
+ ++i;
+ }
+ m_atlases.clear();
+#endif
+}
+
+QSGTexture *Manager::create(const QImage &image, bool hasAlphaChannel)
+{
+ Texture *t = nullptr;
+ if (image.width() < m_atlas_size_limit && image.height() < m_atlas_size_limit) {
+ if (!m_atlas)
+ m_atlas = new Atlas(m_rc, m_atlas_size);
+ t = m_atlas->create(image);
+ if (t && !hasAlphaChannel && t->hasAlphaChannel())
+ t->setHasAlphaChannel(false);
+ }
+ return t;
+}
+
+QSGTexture *Manager::create(const QSGCompressedTextureFactory *factory)
+{
+ Q_UNUSED(factory);
+ return nullptr;
+ // ###
+
+#if 0
+ QSGTexture *t = nullptr;
+ if (!qsgEnableCompressedAtlas() || !factory->m_textureData.isValid())
+ return t;
+
+ // TODO: further abstract the atlas and remove this restriction
+ unsigned int format = factory->m_textureData.glInternalFormat();
+ switch (format) {
+ case QOpenGLTexture::RGB8_ETC1:
+ case QOpenGLTexture::RGB8_ETC2:
+ case QOpenGLTexture::RGBA8_ETC2_EAC:
+ case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2:
+ break;
+ default:
+ return t;
+ }
+
+ QSize size = factory->m_textureData.size();
+ if (size.width() < m_atlas_size_limit && size.height() < m_atlas_size_limit) {
+ QHash<unsigned int, QSGCompressedAtlasTexture::Atlas*>::iterator i = m_atlases.find(format);
+ if (i == m_atlases.end())
+ i = m_atlases.insert(format, new QSGCompressedAtlasTexture::Atlas(m_atlas_size, format));
+ // must be multiple of 4
+ QSize paddedSize(((size.width() + 3) / 4) * 4, ((size.height() + 3) / 4) * 4);
+ QByteArray data = factory->m_textureData.data();
+ t = i.value()->create(data, factory->m_textureData.dataLength(), factory->m_textureData.dataOffset(), size, paddedSize);
+ }
+#endif
+}
+
+AtlasBase::AtlasBase(QSGDefaultRenderContext *rc, const QSize &size)
+ : m_rc(rc)
+ , m_rhi(rc->rhi())
+ , m_allocator(size)
+ , m_size(size)
+{
+}
+
+AtlasBase::~AtlasBase()
+{
+ Q_ASSERT(!m_texture);
+}
+
+void AtlasBase::invalidate()
+{
+ delete m_texture;
+ m_texture = nullptr;
+}
+
+void AtlasBase::updateRhiTexture(QRhiResourceUpdateBatch *resourceUpdates)
+{
+ if (!m_allocated) {
+ m_allocated = true;
+ if (!generateTexture()) {
+ qWarning("QSGTextureAtlas: Failed to create texture");
+ return;
+ }
+ }
+
+ for (TextureBase *t : m_pending_uploads) {
+ // ### this profiling is all wrong, the real work is done elsewhere
+ bool profileFrames = QSG_LOG_TIME_TEXTURE().isDebugEnabled();
+ if (profileFrames)
+ qsg_renderer_timer.start();
+
+ Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphTexturePrepare);
+
+ // Skip bind, convert, swizzle; they're irrelevant
+ Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphTexturePrepare,
+ QQuickProfiler::SceneGraphTexturePrepareStart, 3);
+
+ enqueueTextureUpload(t, resourceUpdates);
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare,
+ QQuickProfiler::SceneGraphTexturePrepareUpload);
+
+ // Skip mipmap; unused
+ Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphTexturePrepare,
+ QQuickProfiler::SceneGraphTexturePrepareUpload, 1);
+ Q_QUICK_SG_PROFILE_REPORT(QQuickProfiler::SceneGraphTexturePrepare,
+ QQuickProfiler::SceneGraphTexturePrepareMipmap);
+ }
+
+ m_pending_uploads.clear();
+}
+
+void AtlasBase::remove(TextureBase *t)
+{
+ QRect atlasRect = t->atlasSubRect();
+ m_allocator.deallocate(atlasRect);
+ m_pending_uploads.removeOne(t);
+}
+
+Atlas::Atlas(QSGDefaultRenderContext *rc, const QSize &size)
+ : AtlasBase(rc, size)
+{
+ // use RGBA texture internally as that is the only one guaranteed to be always supported
+ m_format = QRhiTexture::RGBA8;
+
+ m_debug_overlay = qt_sg_envInt("QSG_ATLAS_OVERLAY", 0);
+
+ // images smaller than this will retain their QImage.
+ // by default no images are retained (favoring memory)
+ // set to a very large value to retain all images (allowing quick removal from the atlas)
+ m_atlas_transient_image_threshold = qt_sg_envInt("QSG_ATLAS_TRANSIENT_IMAGE_THRESHOLD", 0);
+}
+
+Atlas::~Atlas()
+{
+}
+
+Texture *Atlas::create(const QImage &image)
+{
+ // No need to lock, as manager already locked it.
+ QRect rect = m_allocator.allocate(QSize(image.width() + 2, image.height() + 2));
+ if (rect.width() > 0 && rect.height() > 0) {
+ Texture *t = new Texture(this, rect, image);
+ m_pending_uploads << t;
+ return t;
+ }
+ return nullptr;
+}
+
+bool Atlas::generateTexture()
+{
+ m_texture = m_rhi->newTexture(m_format, m_size, 1, QRhiTexture::UsedAsTransferSource);
+ if (!m_texture)
+ return false;
+
+ if (!m_texture->build()) {
+ delete m_texture;
+ m_texture = nullptr;
+ return false;
+ }
+
+ return true;
+}
+
+void Atlas::enqueueTextureUpload(TextureBase *t, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Texture *tex = static_cast<Texture *>(t);
+ const QRect &r = tex->atlasSubRect();
+ QImage image = tex->image();
+
+ if (image.isNull())
+ return;
+
+ if (image.format() != QImage::Format_RGBA8888_Premultiplied)
+ image = std::move(image).convertToFormat(QImage::Format_RGBA8888_Premultiplied);
+
+ if (m_debug_overlay) {
+ QPainter p(&image);
+ p.setCompositionMode(QPainter::CompositionMode_SourceAtop);
+ p.fillRect(0, 0, image.width(), image.height(), QBrush(QColor::fromRgbF(0, 1, 1, 0.5)));
+ }
+
+ const int iw = image.width();
+ const int ih = image.height();
+ const int bpl = image.bytesPerLine() / 4;
+ QVarLengthArray<quint32, 1024> tmpBits(qMax(iw + 2, ih + 2));
+ const int tmpBitsSize = tmpBits.size() * 4;
+ const quint32 *src = reinterpret_cast<const quint32 *>(image.constBits());
+ quint32 *dst = tmpBits.data();
+ QVarLengthArray<QRhiTextureUploadEntry, 5> entries;
+
+ // top row, padding corners
+ dst[0] = src[0];
+ memcpy(dst + 1, src, iw * sizeof(quint32));
+ dst[1 + iw] = src[iw - 1];
+ {
+ QRhiTextureSubresourceUploadDescription subresDesc(dst, tmpBitsSize);
+ subresDesc.setDestinationTopLeft(QPoint(r.x(), r.y()));
+ subresDesc.setSourceSize(QSize(iw + 2, 1));
+ entries.append(QRhiTextureUploadEntry(0, 0, subresDesc));
+ }
+
+ // bottom row, padded corners
+ const quint32 *lastRow = src + bpl * (ih - 1);
+ dst[0] = lastRow[0];
+ memcpy(dst + 1, lastRow, iw * sizeof(quint32));
+ dst[1 + iw] = lastRow[iw - 1];
+ {
+ QRhiTextureSubresourceUploadDescription subresDesc(dst, tmpBitsSize);
+ subresDesc.setDestinationTopLeft(QPoint(r.x(), r.y() + ih + 1));
+ subresDesc.setSourceSize(QSize(iw + 2, 1));
+ entries.append(QRhiTextureUploadEntry(0, 0, subresDesc));
+ }
+
+ // left column
+ for (int i = 0; i < ih; ++i)
+ dst[i] = src[i * bpl];
+ {
+ QRhiTextureSubresourceUploadDescription subresDesc(dst, tmpBitsSize);
+ subresDesc.setDestinationTopLeft(QPoint(r.x(), r.y() + 1));
+ subresDesc.setSourceSize(QSize(1, ih));
+ entries.append(QRhiTextureUploadEntry(0, 0, subresDesc));
+ }
+
+
+ // right column
+ for (int i = 0; i < ih; ++i)
+ dst[i] = src[i * bpl + iw - 1];
+ {
+ QRhiTextureSubresourceUploadDescription subresDesc(dst, tmpBitsSize);
+ subresDesc.setDestinationTopLeft(QPoint(r.x() + iw + 1, r.y() + 1));
+ subresDesc.setSourceSize(QSize(1, ih));
+ entries.append(QRhiTextureUploadEntry(0, 0, subresDesc));
+ }
+
+ // Inner part of the image....
+ if (bpl != iw) {
+ int sy = r.y() + 1;
+ int ey = sy + r.height() - 2;
+ entries.reserve(4 + (ey - sy));
+ for (int y = sy; y < ey; ++y) {
+ QRhiTextureSubresourceUploadDescription subresDesc(src, image.bytesPerLine());
+ subresDesc.setDestinationTopLeft(QPoint(r.x() + 1, y));
+ subresDesc.setSourceSize(QSize(r.width() - 2, 1));
+ entries.append(QRhiTextureUploadEntry(0, 0, subresDesc));
+ src += bpl;
+ }
+ } else {
+ QRhiTextureSubresourceUploadDescription subresDesc(src, image.sizeInBytes());
+ subresDesc.setDestinationTopLeft(QPoint(r.x() + 1, r.y() + 1));
+ subresDesc.setSourceSize(QSize(r.width() - 2, r.height() - 2));
+ entries.append(QRhiTextureUploadEntry(0, 0, subresDesc));
+ }
+
+ QRhiTextureUploadDescription desc;
+ desc.setEntries(entries.cbegin(), entries.cend());
+ resourceUpdates->uploadTexture(m_texture, desc);
+
+ const QSize textureSize = t->textureSize();
+ if (textureSize.width() > m_atlas_transient_image_threshold || textureSize.height() > m_atlas_transient_image_threshold)
+ tex->releaseImage();
+
+ qCDebug(QSG_LOG_TIME_TEXTURE, "atlastexture upload enqueued in: %lldms (%dx%d)",
+ qsg_renderer_timer.elapsed(),
+ t->textureSize().width(),
+ t->textureSize().height());
+}
+
+TextureBase::TextureBase(AtlasBase *atlas, const QRect &textureRect)
+ : QSGTexture(*(new TextureBasePrivate))
+ , m_allocated_rect(textureRect)
+ , m_atlas(atlas)
+{
+}
+
+TextureBase::~TextureBase()
+{
+ m_atlas->remove(this);
+}
+
+QRhiResourceUpdateBatch *TextureBase::workResourceUpdateBatch() const
+{
+ Q_D(const TextureBase);
+ return d->workResourceUpdateBatch;
+}
+
+int TextureBasePrivate::comparisonKey() const
+{
+ Q_Q(const TextureBase);
+
+ // We need special care here: a typical comparisonKey() implementation
+ // returns a unique result when there is no underlying texture yet. This is
+ // not quite ideal for atlasing however since textures with the same atlas
+ // should be considered equal regardless of the state of the underlying
+ // graphics resources.
+
+ // base the comparison on the atlas ptr; this way textures for the same
+ // atlas are considered equal
+ return int(qintptr(q->m_atlas));
+}
+
+QRhiTexture *TextureBasePrivate::rhiTexture() const
+{
+ Q_Q(const TextureBase);
+ return q->m_atlas->m_texture;
+}
+
+void TextureBasePrivate::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_Q(TextureBase);
+#ifdef QT_NO_DEBUG
+ Q_UNUSED(rhi);
+#endif
+ Q_ASSERT(rhi == q->m_atlas->m_rhi);
+ q->m_atlas->updateRhiTexture(resourceUpdates);
+}
+
+Texture::Texture(Atlas *atlas, const QRect &textureRect, const QImage &image)
+ : TextureBase(atlas, textureRect)
+ , m_image(image)
+ , m_has_alpha(image.hasAlphaChannel())
+{
+ float w = atlas->size().width();
+ float h = atlas->size().height();
+ QRect nopad = atlasSubRectWithoutPadding();
+ m_texture_coords_rect = QRectF(nopad.x() / w,
+ nopad.y() / h,
+ nopad.width() / w,
+ nopad.height() / h);
+}
+
+Texture::~Texture()
+{
+ if (m_nonatlas_texture)
+ delete m_nonatlas_texture;
+}
+
+QSGTexture *Texture::removedFromAtlas() const
+{
+ if (!m_nonatlas_texture) {
+ m_nonatlas_texture = new QSGPlainTexture;
+ if (!m_image.isNull()) {
+ m_nonatlas_texture->setImage(m_image);
+ m_nonatlas_texture->setFiltering(filtering());
+ } else {
+ QSGDefaultRenderContext *rc = m_atlas->renderContext();
+ QRhi *rhi = m_atlas->rhi();
+ Q_ASSERT(rhi->isRecordingFrame());
+ const QRect r = atlasSubRectWithoutPadding();
+
+ QRhiTexture *extractTex = rhi->newTexture(m_atlas->texture()->format(), r.size());
+ if (extractTex->build()) {
+ bool ownResUpd = false;
+ QRhiResourceUpdateBatch *resUpd = workResourceUpdateBatch(); // ### Qt 6: should be an arg to this function
+ if (!resUpd) {
+ ownResUpd = true;
+ resUpd = rhi->nextResourceUpdateBatch();
+ }
+ QRhiTextureCopyDescription desc;
+ desc.setSourceTopLeft(r.topLeft());
+ desc.setPixelSize(r.size());
+ resUpd->copyTexture(extractTex, m_atlas->texture(), desc);
+ if (ownResUpd)
+ rc->currentFrameCommandBuffer()->resourceUpdate(resUpd);
+ }
+
+ m_nonatlas_texture->setTexture(extractTex);
+ m_nonatlas_texture->setOwnsTexture(true);
+ m_nonatlas_texture->setHasAlphaChannel(m_has_alpha);
+ m_nonatlas_texture->setTextureSize(r.size());
+ }
+ }
+
+ m_nonatlas_texture->setMipmapFiltering(mipmapFiltering());
+ m_nonatlas_texture->setFiltering(filtering());
+ return m_nonatlas_texture;
+}
+
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qsgrhiatlastexture_p.cpp"
diff --git a/src/quick/scenegraph/util/qsgrhiatlastexture_p.h b/src/quick/scenegraph/util/qsgrhiatlastexture_p.h
new file mode 100644
index 0000000000..50d7b2a53f
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgrhiatlastexture_p.h
@@ -0,0 +1,217 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 QSGRHIATLASTEXTURE_P_H
+#define QSGRHIATLASTEXTURE_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/QSize>
+#include <QtQuick/private/qsgplaintexture_p.h>
+#include <QtQuick/private/qsgareaallocator_p.h>
+#include <QtGui/QSurface>
+#include <QtGui/private/qrhi_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGDefaultRenderContext;
+
+namespace QSGCompressedAtlasTexture {
+ class Atlas;
+}
+class QSGCompressedTextureFactory;
+
+namespace QSGRhiAtlasTexture
+{
+
+class Texture;
+class TextureBase;
+class TextureBasePrivate;
+class Atlas;
+
+class Manager : public QObject
+{
+ Q_OBJECT
+
+public:
+ Manager(QSGDefaultRenderContext *rc, const QSize &surfacePixelSize, QSurface *maybeSurface);
+ ~Manager();
+
+ QSGTexture *create(const QImage &image, bool hasAlphaChannel);
+ QSGTexture *create(const QSGCompressedTextureFactory *factory);
+ void invalidate();
+
+private:
+ QSGDefaultRenderContext *m_rc;
+ QRhi *m_rhi;
+ Atlas *m_atlas = nullptr;
+ // set of atlases for different compressed formats
+ QHash<unsigned int, QSGCompressedAtlasTexture::Atlas*> m_atlases;
+
+ QSize m_atlas_size;
+ int m_atlas_size_limit;
+};
+
+class AtlasBase : public QObject
+{
+ Q_OBJECT
+public:
+ AtlasBase(QSGDefaultRenderContext *rc, const QSize &size);
+ ~AtlasBase();
+
+ void invalidate();
+ void updateRhiTexture(QRhiResourceUpdateBatch *resourceUpdates);
+ void remove(TextureBase *t);
+
+ QSGDefaultRenderContext *renderContext() const { return m_rc; }
+ QRhi *rhi() const { return m_rhi; }
+ QRhiTexture *texture() const { return m_texture; }
+ QSize size() const { return m_size; }
+
+protected:
+ virtual bool generateTexture() = 0;
+ virtual void enqueueTextureUpload(TextureBase *t, QRhiResourceUpdateBatch *resourceUpdates) = 0;
+
+protected:
+ QSGDefaultRenderContext *m_rc;
+ QRhi *m_rhi;
+ QSGAreaAllocator m_allocator;
+ QRhiTexture *m_texture = nullptr;
+ QSize m_size;
+ QVector<TextureBase *> m_pending_uploads;
+ friend class TextureBase;
+ friend class TextureBasePrivate;
+
+private:
+ bool m_allocated = false;
+};
+
+class Atlas : public AtlasBase
+{
+public:
+ Atlas(QSGDefaultRenderContext *rc, const QSize &size);
+ ~Atlas();
+
+ bool generateTexture() override;
+ void enqueueTextureUpload(TextureBase *t, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ Texture *create(const QImage &image);
+
+ QRhiTexture::Format format() const { return m_format; }
+
+private:
+ QRhiTexture::Format m_format;
+ int m_atlas_transient_image_threshold = 0;
+
+ uint m_debug_overlay : 1;
+};
+
+class TextureBase : public QSGTexture
+{
+ Q_DECLARE_PRIVATE(TextureBase)
+ Q_OBJECT
+public:
+ TextureBase(AtlasBase *atlas, const QRect &textureRect);
+ ~TextureBase();
+
+ int textureId() const override { return 0; } // not used
+ void bind() override { } // not used
+
+ bool isAtlasTexture() const override { return true; }
+ QRect atlasSubRect() const { return m_allocated_rect; }
+
+ QRhiResourceUpdateBatch *workResourceUpdateBatch() const;
+
+protected:
+ QRect m_allocated_rect;
+ AtlasBase *m_atlas;
+};
+
+class TextureBasePrivate : public QSGTexturePrivate
+{
+ Q_DECLARE_PUBLIC(TextureBase)
+public:
+ int comparisonKey() const override;
+ QRhiTexture *rhiTexture() const override;
+ void updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates) override;
+};
+
+class Texture : public TextureBase
+{
+ Q_OBJECT
+public:
+ Texture(Atlas *atlas, const QRect &textureRect, const QImage &image);
+ ~Texture();
+
+ QSize textureSize() const override { return atlasSubRectWithoutPadding().size(); }
+ void setHasAlphaChannel(bool alpha) { m_has_alpha = alpha; }
+ bool hasAlphaChannel() const override { return m_has_alpha; }
+ bool hasMipmaps() const override { return false; }
+
+ QRectF normalizedTextureSubRect() const override { return m_texture_coords_rect; }
+
+ QRect atlasSubRect() const { return m_allocated_rect; }
+ QRect atlasSubRectWithoutPadding() const { return m_allocated_rect.adjusted(1, 1, -1, -1); }
+
+ QSGTexture *removedFromAtlas() const override;
+
+ void releaseImage() { m_image = QImage(); }
+ const QImage &image() const { return m_image; }
+
+private:
+ QRectF m_texture_coords_rect;
+ QImage m_image;
+ mutable QSGPlainTexture *m_nonatlas_texture = nullptr;
+ bool m_has_alpha;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/util/qsgrhinativetextureimporter.cpp b/src/quick/scenegraph/util/qsgrhinativetextureimporter.cpp
new file mode 100644
index 0000000000..7a7c19f587
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgrhinativetextureimporter.cpp
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "qsgrhinativetextureimporter_p.h"
+#include <private/qsgrhisupport_p.h> // to get all the relevant qrhi headers
+
+QT_BEGIN_NAMESPACE
+
+void QSGRhiNativeTextureImporter::buildWrapper(QRhi *rhi, QRhiTexture *t,
+ const void *nativeObjectPtr, int nativeLayout)
+{
+#if !QT_CONFIG(vulkan)
+ Q_UNUSED(nativeLayout);
+#endif
+#if !QT_CONFIG(opengl) && !QT_CONFIG(vulkan) && !defined(Q_OS_WIN) && !defined(Q_OS_DARWIN)
+ Q_UNUSED(nativeObjectPtr);
+#endif
+
+ switch (rhi->backend()) {
+ case QRhi::OpenGLES2:
+ {
+#if QT_CONFIG(opengl)
+ QRhiGles2TextureNativeHandles h;
+ h.texture = *reinterpret_cast<const uint *>(nativeObjectPtr);
+ t->buildFrom(&h);
+#endif
+ }
+ break;
+ case QRhi::Vulkan:
+ {
+#if QT_CONFIG(vulkan)
+ QRhiVulkanTextureNativeHandles h;
+ h.image = *reinterpret_cast<const VkImage *>(nativeObjectPtr);
+ h.layout = VkImageLayout(nativeLayout);
+ t->buildFrom(&h);
+#endif
+ }
+ break;
+ case QRhi::D3D11:
+ {
+#ifdef Q_OS_WIN
+ QRhiD3D11TextureNativeHandles h;
+ h.texture = *reinterpret_cast<void * const *>(nativeObjectPtr);
+ t->buildFrom(&h);
+#endif
+ }
+ break;
+ case QRhi::Metal:
+ {
+#ifdef Q_OS_DARWIN
+ QRhiMetalTextureNativeHandles h;
+ h.texture = *reinterpret_cast<void * const *>(nativeObjectPtr);
+ t->buildFrom(&h);
+#endif
+ }
+ break;
+ case QRhi::Null:
+ t->build();
+ break;
+ default:
+ qWarning("QSGRhiNativeTextureImporter: encountered an unsupported QRhi backend (%d)",
+ int(rhi->backend()));
+ t->build();
+ break;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgrhinativetextureimporter_p.h b/src/quick/scenegraph/util/qsgrhinativetextureimporter_p.h
new file mode 100644
index 0000000000..e811109a94
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgrhinativetextureimporter_p.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 QSGRHINATIVETEXTUREIMPORTER_P_H
+#define QSGRHINATIVETEXTUREIMPORTER_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 <QtQuick/private/qtquickglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRhi;
+class QRhiTexture;
+
+class QSGRhiNativeTextureImporter
+{
+public:
+ static void buildWrapper(QRhi *rhi, QRhiTexture *t,
+ const void *nativeObjectPtr, int nativeLayout);
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGRHINATIVETEXTUREIMPORTER_P_H
diff --git a/src/quick/scenegraph/util/qsgsimplematerial.cpp b/src/quick/scenegraph/util/qsgsimplematerial.cpp
index 376f7dce5c..1064caccc7 100644
--- a/src/quick/scenegraph/util/qsgsimplematerial.cpp
+++ b/src/quick/scenegraph/util/qsgsimplematerial.cpp
@@ -46,8 +46,9 @@
\inmodule QtQuick
\ingroup qtquick-scenegraph-materials
- \warning This utility class is only functional when running with the OpenGL
- backend of the Qt Quick scenegraph.
+ \warning This utility class is only functional when running with the legacy
+ OpenGL renderer of the Qt Quick scenegraph. Its usage is not recommended in
+ new application code.
Where the QSGMaterial and QSGMaterialShader API requires a bit of
boilerplate code to create a functioning material, the
diff --git a/src/quick/scenegraph/util/qsgsimplerectnode.cpp b/src/quick/scenegraph/util/qsgsimplerectnode.cpp
index 28b177be84..7b96a3fdde 100644
--- a/src/quick/scenegraph/util/qsgsimplerectnode.cpp
+++ b/src/quick/scenegraph/util/qsgsimplerectnode.cpp
@@ -49,10 +49,10 @@ QT_BEGIN_NAMESPACE
solid filled rectangles using scenegraph.
\inmodule QtQuick
- \warning This utility class is only functional when running with the OpenGL
- or software backends of the Qt Quick scenegraph. For a proper cross-platform
- alternative prefer using QSGRectangleNode via
- QQuickWindow::createRectangleNode() or QSGEngine::createRectangleNode().
+ \warning This utility class is only functional when running with the default
+ or software backends of the Qt Quick scenegraph. As an alternative, prefer
+ using QSGRectangleNode via QQuickWindow::createRectangleNode() or
+ QSGEngine::createRectangleNode().
\deprecated
*/
diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp
index 0c49ca9aa5..1d0a423aa9 100644
--- a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp
+++ b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp
@@ -97,10 +97,10 @@ static void qsgsimpletexturenode_update(QSGGeometry *g,
\warning The simple texture node class must have a texture before being
added to the scene graph to be rendered.
- \warning This utility class is only functional when running with the OpenGL
- or software backends of the Qt Quick scenegraph. For a proper cross-platform
- alternative prefer using QSGImageNode via
- QQuickWindow::createImageNode() or QSGEngine::createImageNode().
+ \warning This utility class is only functional when running with the default
+ or software backends of the Qt Quick scenegraph. As an alternative, prefer
+ using QSGImageNode via QQuickWindow::createImageNode() or
+ QSGEngine::createImageNode().
\deprecated
*/
diff --git a/src/quick/scenegraph/util/qsgtexturematerial.cpp b/src/quick/scenegraph/util/qsgtexturematerial.cpp
index 7b1d5abb26..67b8748119 100644
--- a/src/quick/scenegraph/util/qsgtexturematerial.cpp
+++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp
@@ -38,23 +38,20 @@
****************************************************************************/
#include "qsgtexturematerial_p.h"
-#include "qsgtexture_p.h"
+#include <private/qsgtexture_p.h>
#if QT_CONFIG(opengl)
# include <QtGui/qopenglshaderprogram.h>
# include <QtGui/qopenglfunctions.h>
#endif
+#include <QtGui/private/qrhi_p.h>
QT_BEGIN_NAMESPACE
-#if QT_CONFIG(opengl)
inline static bool isPowerOfTwo(int x)
{
// Assumption: x >= 1
return x == (x & -x);
}
-#endif
-
-QSGMaterialType QSGOpaqueTextureMaterialShader::type;
QSGOpaqueTextureMaterialShader::QSGOpaqueTextureMaterialShader()
{
@@ -122,6 +119,60 @@ void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMa
}
+QSGOpaqueTextureMaterialRhiShader::QSGOpaqueTextureMaterialRhiShader()
+{
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/opaquetexture.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/opaquetexture.frag.qsb"));
+}
+
+bool QSGOpaqueTextureMaterialRhiShader::updateUniformData(RenderState &state, QSGMaterial *, QSGMaterial *)
+{
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ changed = true;
+ }
+
+ return changed;
+}
+
+void QSGOpaqueTextureMaterialRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ if (binding != 1)
+ return;
+
+#ifdef QT_NO_DEBUG
+ Q_UNUSED(oldMaterial);
+#endif
+ Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
+ QSGOpaqueTextureMaterial *tx = static_cast<QSGOpaqueTextureMaterial *>(newMaterial);
+ QSGTexture *t = tx->texture();
+
+ t->setFiltering(tx->filtering());
+ t->setMipmapFiltering(tx->mipmapFiltering());
+ t->setAnisotropyLevel(tx->anisotropyLevel());
+
+ t->setHorizontalWrapMode(tx->horizontalWrapMode());
+ t->setVerticalWrapMode(tx->verticalWrapMode());
+ if (!state.rhi()->isFeatureSupported(QRhi::NPOTTextureRepeat)) {
+ QSize size = t->textureSize();
+ const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
+ if (isNpot) {
+ t->setHorizontalWrapMode(QSGTexture::ClampToEdge);
+ t->setVerticalWrapMode(QSGTexture::ClampToEdge);
+ t->setMipmapFiltering(QSGTexture::None);
+ }
+ }
+
+ t->updateRhiTexture(state.rhi(), state.resourceUpdateBatch());
+ *texture = t;
+}
+
+
/*!
\class QSGOpaqueTextureMaterial
\brief The QSGOpaqueTextureMaterial class provides a convenient way of
@@ -129,8 +180,8 @@ void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMa
\inmodule QtQuick
\ingroup qtquick-scenegraph-materials
- \warning This utility class is only functional when running with the OpenGL
- backend of the Qt Quick scenegraph.
+ \warning This utility class is only functional when running with the
+ default backend of the Qt Quick scenegraph.
The opaque textured material will fill every pixel in a geometry with
the supplied texture. The material does not respect the opacity of the
@@ -175,6 +226,7 @@ QSGOpaqueTextureMaterial::QSGOpaqueTextureMaterial()
, m_vertical_wrap(QSGTexture::ClampToEdge)
, m_anisotropy_level(QSGTexture::AnisotropyNone)
{
+ setFlag(SupportsRhiShader, true);
}
@@ -183,7 +235,8 @@ QSGOpaqueTextureMaterial::QSGOpaqueTextureMaterial()
*/
QSGMaterialType *QSGOpaqueTextureMaterial::type() const
{
- return &QSGOpaqueTextureMaterialShader::type;
+ static QSGMaterialType type;
+ return &type;
}
/*!
@@ -191,11 +244,13 @@ QSGMaterialType *QSGOpaqueTextureMaterial::type() const
*/
QSGMaterialShader *QSGOpaqueTextureMaterial::createShader() const
{
- return new QSGOpaqueTextureMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new QSGOpaqueTextureMaterialRhiShader;
+ else
+ return new QSGOpaqueTextureMaterialShader;
}
-
/*!
\fn QSGTexture *QSGOpaqueTextureMaterial::texture() const
@@ -323,7 +378,7 @@ int QSGOpaqueTextureMaterial::compare(const QSGMaterial *o) const
{
Q_ASSERT(o && type() == o->type());
const QSGOpaqueTextureMaterial *other = static_cast<const QSGOpaqueTextureMaterial *>(o);
- if (int diff = m_texture->textureId() - other->texture()->textureId())
+ if (int diff = m_texture->comparisonKey() - other->texture()->comparisonKey())
return diff;
return int(m_filtering) - int(other->m_filtering);
}
@@ -337,8 +392,8 @@ int QSGOpaqueTextureMaterial::compare(const QSGMaterial *o) const
\inmodule QtQuick
\ingroup qtquick-scenegraph-materials
- \warning This utility class is only functional when running with the OpenGL
- backend of the Qt Quick scenegraph.
+ \warning This utility class is only functional when running with the
+ default backend of the Qt Quick scenegraph.
The textured material will fill every pixel in a geometry with
the supplied texture.
@@ -363,32 +418,30 @@ int QSGOpaqueTextureMaterial::compare(const QSGMaterial *o) const
a material in the scene graph.
*/
-QSGMaterialType QSGTextureMaterialShader::type;
-
-
-
/*!
\internal
*/
QSGMaterialType *QSGTextureMaterial::type() const
{
- return &QSGTextureMaterialShader::type;
+ static QSGMaterialType type;
+ return &type;
}
-
-
/*!
\internal
*/
QSGMaterialShader *QSGTextureMaterial::createShader() const
{
- return new QSGTextureMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new QSGTextureMaterialRhiShader;
+ else
+ return new QSGTextureMaterialShader;
}
+
QSGTextureMaterialShader::QSGTextureMaterialShader()
- : QSGOpaqueTextureMaterialShader()
{
#if QT_CONFIG(opengl)
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/texture.frag"));
@@ -413,4 +466,27 @@ void QSGTextureMaterialShader::initialize()
#endif
}
+
+QSGTextureMaterialRhiShader::QSGTextureMaterialRhiShader()
+{
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/texture.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/texture.frag.qsb"));
+}
+
+bool QSGTextureMaterialRhiShader::updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(buf->data() + 64, &opacity, 4);
+ changed = true;
+ }
+
+ changed |= QSGOpaqueTextureMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
+
+ return changed;
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgtexturematerial_p.h b/src/quick/scenegraph/util/qsgtexturematerial_p.h
index a99e872580..d1ef7d1d7f 100644
--- a/src/quick/scenegraph/util/qsgtexturematerial_p.h
+++ b/src/quick/scenegraph/util/qsgtexturematerial_p.h
@@ -64,15 +64,22 @@ public:
void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
char const *const *attributeNames() const override;
- static QSGMaterialType type;
-
protected:
void initialize() override;
int m_matrix_id;
};
-class Q_QUICK_PRIVATE_EXPORT QSGTextureMaterialShader : public QSGOpaqueTextureMaterialShader
+class Q_QUICK_PRIVATE_EXPORT QSGOpaqueTextureMaterialRhiShader : public QSGMaterialRhiShader
+{
+public:
+ QSGOpaqueTextureMaterialRhiShader();
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+ void updateSampledImage(RenderState &state, int binding, QSGTexture **texture, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
+class QSGTextureMaterialShader : public QSGOpaqueTextureMaterialShader
{
public:
QSGTextureMaterialShader();
@@ -80,12 +87,18 @@ public:
void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
void initialize() override;
- static QSGMaterialType type;
-
protected:
int m_opacity_id;
};
+class QSGTextureMaterialRhiShader : public QSGOpaqueTextureMaterialRhiShader
+{
+public:
+ QSGTextureMaterialRhiShader();
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+
QT_END_NAMESPACE
#endif // QSGTEXTUREMATERIAL_P_H
diff --git a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp
index cb61430e2e..c27dd7d1f0 100644
--- a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp
+++ b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp
@@ -51,8 +51,6 @@ public:
void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
char const *const *attributeNames() const override;
- static QSGMaterialType type;
-
private:
void initialize() override;
#if QT_CONFIG(opengl)
@@ -61,8 +59,6 @@ private:
#endif
};
-QSGMaterialType QSGVertexColorMaterialShader::type;
-
QSGVertexColorMaterialShader::QSGVertexColorMaterialShader()
{
#if QT_CONFIG(opengl)
@@ -99,6 +95,43 @@ void QSGVertexColorMaterialShader::initialize()
}
+class QSGVertexColorMaterialRhiShader : public QSGMaterialRhiShader
+{
+public:
+ QSGVertexColorMaterialRhiShader();
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
+};
+
+QSGVertexColorMaterialRhiShader::QSGVertexColorMaterialRhiShader()
+{
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/vertexcolor.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/vertexcolor.frag.qsb"));
+}
+
+bool QSGVertexColorMaterialRhiShader::updateUniformData(RenderState &state,
+ QSGMaterial * /*newEffect*/,
+ QSGMaterial * /*oldEffect*/)
+{
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ changed = true;
+ }
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(buf->data() + 64, &opacity, 4);
+ changed = true;
+ }
+
+ return changed;
+}
+
+
/*!
\class QSGVertexColorMaterial
\brief The QSGVertexColorMaterial class provides a convenient way of rendering per-vertex
@@ -107,8 +140,8 @@ void QSGVertexColorMaterialShader::initialize()
\inmodule QtQuick
\ingroup qtquick-scenegraph-materials
- \warning This utility class is only functional when running with the OpenGL
- backend of the Qt Quick scenegraph.
+ \warning This utility class is only functional when running with the
+ default backend of the Qt Quick scenegraph.
The vertex color material will give each vertex in a geometry a color. Pixels between
vertices will be linearly interpolated. The colors can contain transparency.
@@ -135,6 +168,7 @@ void QSGVertexColorMaterialShader::initialize()
QSGVertexColorMaterial::QSGVertexColorMaterial()
{
setFlag(Blending, true);
+ setFlag(SupportsRhiShader, true);
}
@@ -158,7 +192,8 @@ int QSGVertexColorMaterial::compare(const QSGMaterial * /* other */) const
QSGMaterialType *QSGVertexColorMaterial::type() const
{
- return &QSGVertexColorMaterialShader::type;
+ static QSGMaterialType type;
+ return &type;
}
@@ -169,7 +204,10 @@ QSGMaterialType *QSGVertexColorMaterial::type() const
QSGMaterialShader *QSGVertexColorMaterial::createShader() const
{
- return new QSGVertexColorMaterialShader;
+ if (flags().testFlag(RhiShaderWanted))
+ return new QSGVertexColorMaterialRhiShader;
+ else
+ return new QSGVertexColorMaterialShader;
}
QT_END_NAMESPACE
diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp
index 2043b50545..fe5b372da8 100644
--- a/src/quick/util/qquickanimation.cpp
+++ b/src/quick/util/qquickanimation.cpp
@@ -2636,7 +2636,7 @@ QQuickStateActions QQuickPropertyAnimation::createTransitionActions(QQuickStateA
for (int j = 0; j < targets.count(); ++j) {
QQuickStateAction myAction;
QString errorMessage;
- const QString propertyName = props.at(i);
+ const QString &propertyName = props.at(i);
myAction.property = d->createProperty(targets.at(j), propertyName, this, &errorMessage);
if (myAction.property.isValid()) {
if (usingDefaultProperties)
diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp
index 03be78ab15..a9caedec4f 100644
--- a/src/quick/util/qquickanimatorjob.cpp
+++ b/src/quick/util/qquickanimatorjob.cpp
@@ -207,7 +207,7 @@ void QQuickAnimatorProxyJob::setWindow(QQuickWindow *window)
stop();
} else if (!m_controller && m_job) {
- m_controller = QQuickWindowPrivate::get(window)->animationController;
+ m_controller = QQuickWindowPrivate::get(window)->animationController.get();
if (window->isSceneGraphInitialized())
readyToAnimate();
else
diff --git a/src/quick/util/qquickbehavior.cpp b/src/quick/util/qquickbehavior.cpp
index 76d464e7f8..ae5c481e2f 100644
--- a/src/quick/util/qquickbehavior.cpp
+++ b/src/quick/util/qquickbehavior.cpp
@@ -143,7 +143,7 @@ void QQuickBehavior::setAnimation(QQuickAbstractAnimation *animation)
void QQuickBehaviorPrivate::animationStateChanged(QAbstractAnimationJob *, QAbstractAnimationJob::State newState,QAbstractAnimationJob::State)
{
- if (!blockRunningChanged)
+ if (!blockRunningChanged && animation)
animation->notifyRunningChanged(newState == QAbstractAnimationJob::Running);
}
diff --git a/src/quick/util/qquickboundaryrule.cpp b/src/quick/util/qquickboundaryrule.cpp
new file mode 100644
index 0000000000..3558c8bfa0
--- /dev/null
+++ b/src/quick/util/qquickboundaryrule.cpp
@@ -0,0 +1,574 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "qquickboundaryrule_p.h"
+
+#include <qqmlcontext.h>
+#include <qqmlinfo.h>
+#include <private/qqmlproperty_p.h>
+#include <private/qqmlengine_p.h>
+#include <private/qobject_p.h>
+#include <private/qquickanimation_p_p.h>
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcBR, "qt.quick.boundaryrule")
+
+class QQuickBoundaryReturnJob;
+class QQuickBoundaryRulePrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickBoundaryRule)
+public:
+ QQuickBoundaryRulePrivate() {}
+
+ QQmlProperty property;
+ QEasingCurve easing = QEasingCurve(QEasingCurve::OutQuad);
+ QQuickBoundaryReturnJob *returnAnimationJob = nullptr;
+ // read-only properties, updated on each write()
+ qreal targetValue = 0; // after easing was applied
+ qreal peakOvershoot = 0;
+ qreal currentOvershoot = 0;
+ // settable properties
+ qreal minimum = 0;
+ qreal maximum = 0;
+ qreal minimumOvershoot = 0;
+ qreal maximumOvershoot = 0;
+ qreal overshootScale = 0.5;
+ int returnDuration = 100;
+ QQuickBoundaryRule::OvershootFilter overshootFilter = QQuickBoundaryRule::OvershootFilter::None;
+ bool enabled = true;
+ bool finalized = false;
+
+ qreal easedOvershoot(qreal overshootingValue);
+ void resetOvershoot();
+};
+
+class QQuickBoundaryReturnJob : public QAbstractAnimationJob
+{
+public:
+ QQuickBoundaryReturnJob(QQuickBoundaryRulePrivate *br, qreal to)
+ : QAbstractAnimationJob()
+ , boundaryRule(br)
+ , fromValue(br->targetValue)
+ , toValue(to) {}
+
+ int duration() const override { return boundaryRule->returnDuration; }
+
+ void updateCurrentTime(int) override;
+
+ void updateState(QAbstractAnimationJob::State newState,
+ QAbstractAnimationJob::State oldState) override;
+
+ QQuickBoundaryRulePrivate *boundaryRule;
+ qreal fromValue; // snapshot of initial value from which we're returning
+ qreal toValue; // target property value to which we're returning
+};
+
+void QQuickBoundaryReturnJob::updateCurrentTime(int t)
+{
+ // The easing property tells how to behave when the property is being
+ // externally manipulated beyond the bounds. During returnToBounds()
+ // we run it in reverse, by reversing time.
+ qreal progress = (duration() - t) / qreal(duration());
+ qreal easingValue = boundaryRule->easing.valueForProgress(progress);
+ qreal delta = qAbs(fromValue - toValue) * easingValue;
+ qreal value = (fromValue > toValue ? toValue + delta : toValue - delta);
+ qCDebug(lcBR) << t << "ms" << qRound(progress * 100) << "% easing" << easingValue << "->" << value;
+ QQmlPropertyPrivate::write(boundaryRule->property, value,
+ QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
+}
+
+void QQuickBoundaryReturnJob::updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState)
+{
+ Q_UNUSED(oldState)
+ if (newState == QAbstractAnimationJob::Stopped) {
+ qCDebug(lcBR) << "return animation done";
+ boundaryRule->resetOvershoot();
+ boundaryRule->returnAnimationJob = nullptr;
+ delete this;
+ }
+}
+
+/*!
+ \qmltype BoundaryRule
+ \instantiates QQuickBoundaryRule
+ \inqmlmodule Qt.labs.animation
+ \ingroup qtquick-transitions-animations
+ \ingroup qtquick-interceptors
+ \brief Defines a restriction on the range of values that can be set on a numeric property.
+ \since 5.14
+
+ A BoundaryRule defines the range of values that a particular property is
+ allowed to have. When an out-of-range value would otherwise be set,
+ it applies "resistance" via an easing curve.
+
+ For example, the following BoundaryRule prevents DragHandler from dragging
+ the Rectangle too far:
+
+ \snippet qml/boundaryRule.qml 0
+
+ Note that a property cannot have more than one assigned BoundaryRule.
+
+ \sa {Animation and Transitions in Qt Quick}, {Qt Quick Examples - Animation#Behaviors}{Behavior example}, {Qt QML}
+*/
+
+QQuickBoundaryRule::QQuickBoundaryRule(QObject *parent)
+ : QObject(*(new QQuickBoundaryRulePrivate), parent)
+ , QQmlPropertyValueInterceptor()
+{
+}
+
+QQuickBoundaryRule::~QQuickBoundaryRule()
+{
+ Q_D(QQuickBoundaryRule);
+ // stop any running animation and
+ // prevent QQuickBoundaryReturnJob::updateState() from accessing QQuickBoundaryRulePrivate
+ delete d->returnAnimationJob;
+}
+
+/*!
+ \qmlproperty bool QtQuick::BoundaryRule::enabled
+
+ This property holds whether the rule will be enforced when the tracked
+ property changes value.
+
+ By default a BoundaryRule is enabled.
+*/
+bool QQuickBoundaryRule::enabled() const
+{
+ Q_D(const QQuickBoundaryRule);
+ return d->enabled;
+}
+
+void QQuickBoundaryRule::setEnabled(bool enabled)
+{
+ Q_D(QQuickBoundaryRule);
+ if (d->enabled == enabled)
+ return;
+ d->enabled = enabled;
+ emit enabledChanged();
+}
+
+/*!
+ \qmlproperty qreal QtQuick::BoundaryRule::minimum
+
+ This property holds the smallest unconstrained value that the property is
+ allowed to have. If the property is set to a smaller value, it will be
+ constrained by \l easing and \l minimumOvershoot.
+
+ The default is \c 0.
+*/
+qreal QQuickBoundaryRule::minimum() const
+{
+ Q_D(const QQuickBoundaryRule);
+ return d->minimum;
+}
+
+void QQuickBoundaryRule::setMinimum(qreal minimum)
+{
+ Q_D(QQuickBoundaryRule);
+ if (qFuzzyCompare(d->minimum, minimum))
+ return;
+ d->minimum = minimum;
+ emit minimumChanged();
+}
+
+/*!
+ \qmlproperty qreal QtQuick::BoundaryRule::minimumOvershoot
+
+ This property holds the amount that the property is allowed to be
+ less than \l minimum. Whenever the value is less than \l minimum
+ and greater than \c {minimum - minimumOvershoot}, it is constrained
+ by the \l easing curve. When the value attempts to go under
+ \c {minimum - minimumOvershoots} there is a hard stop.
+
+ The default is \c 0.
+*/
+qreal QQuickBoundaryRule::minimumOvershoot() const
+{
+ Q_D(const QQuickBoundaryRule);
+ return d->minimumOvershoot;
+}
+
+void QQuickBoundaryRule::setMinimumOvershoot(qreal minimumOvershoot)
+{
+ Q_D(QQuickBoundaryRule);
+ if (qFuzzyCompare(d->minimumOvershoot, minimumOvershoot))
+ return;
+ d->minimumOvershoot = minimumOvershoot;
+ emit minimumOvershootChanged();
+}
+
+/*!
+ \qmlproperty qreal QtQuick::BoundaryRule::maximum
+
+ This property holds the largest unconstrained value that the property is
+ allowed to have. If the property is set to a larger value, it will be
+ constrained by \l easing and \l maximumOvershoot.
+
+ The default is \c 1.
+*/
+qreal QQuickBoundaryRule::maximum() const
+{
+ Q_D(const QQuickBoundaryRule);
+ return d->maximum;
+}
+
+void QQuickBoundaryRule::setMaximum(qreal maximum)
+{
+ Q_D(QQuickBoundaryRule);
+ if (qFuzzyCompare(d->maximum, maximum))
+ return;
+ d->maximum = maximum;
+ emit maximumChanged();
+}
+
+/*!
+ \qmlproperty qreal QtQuick::BoundaryRule::maximumOvershoot
+
+ This property holds the amount that the property is allowed to be
+ more than \l maximum. Whenever the value is greater than \l maximum
+ and less than \c {maximum + maximumOvershoot}, it is constrained
+ by the \l easing curve. When the value attempts to exceed
+ \c {maximum + maximumOvershoot} there is a hard stop.
+
+ The default is 0.
+*/
+qreal QQuickBoundaryRule::maximumOvershoot() const
+{
+ Q_D(const QQuickBoundaryRule);
+ return d->maximumOvershoot;
+}
+
+void QQuickBoundaryRule::setMaximumOvershoot(qreal maximumOvershoot)
+{
+ Q_D(QQuickBoundaryRule);
+ if (qFuzzyCompare(d->maximumOvershoot, maximumOvershoot))
+ return;
+ d->maximumOvershoot = maximumOvershoot;
+ emit maximumOvershootChanged();
+}
+
+/*!
+ \qmlproperty qreal QtQuick::BoundaryRule::overshootScale
+
+ This property holds the amount by which the \l easing is scaled during the
+ overshoot condition. For example if an Item is restricted from moving more
+ than 100 pixels beyond some limit, and the user (by means of some Input
+ Handler) is trying to drag it 100 pixels past the limit, if overshootScale
+ is set to 1, the user will succeed: the only effect of the easing curve is
+ to change the rate at which the item moves from overshoot 0 to overshoot
+ 100. But if it is set to 0.5, the BoundaryRule provides resistance such
+ that when the user tries to move 100 pixels, the Item will only move 50
+ pixels; and the easing curve modulates the rate of movement such that it
+ may move in sync with the user's attempted movement at the beginning, and
+ then slows down, depending on the shape of the easing curve.
+
+ The default is 0.5.
+*/
+qreal QQuickBoundaryRule::overshootScale() const
+{
+ Q_D(const QQuickBoundaryRule);
+ return d->overshootScale;
+}
+
+void QQuickBoundaryRule::setOvershootScale(qreal overshootScale)
+{
+ Q_D(QQuickBoundaryRule);
+ if (qFuzzyCompare(d->overshootScale, overshootScale))
+ return;
+ d->overshootScale = overshootScale;
+ emit overshootScaleChanged();
+}
+
+/*!
+ \qmlproperty qreal QtQuick::BoundaryRule::currentOvershoot
+
+ This property holds the amount by which the most recently set value of the
+ intercepted property exceeds \l maximum or is less than \l minimum.
+
+ It is positive if the property value exceeds \l maximum, negative if the
+ property value is less than \l minimum, or 0 if the property value is
+ within both boundaries.
+*/
+qreal QQuickBoundaryRule::currentOvershoot() const
+{
+ Q_D(const QQuickBoundaryRule);
+ return d->currentOvershoot;
+}
+
+/*!
+ \qmlproperty qreal QtQuick::BoundaryRule::peakOvershoot
+
+ This property holds the most-positive or most-negative value of
+ \l currentOvershoot that has been seen, until \l returnToBounds() is called.
+
+ This can be useful when the intercepted property value is known to
+ fluctuate, and you want to find and react to the maximum amount of
+ overshoot rather than to the fluctuations.
+
+ \sa overshootFilter
+*/
+qreal QQuickBoundaryRule::peakOvershoot() const
+{
+ Q_D(const QQuickBoundaryRule);
+ return d->peakOvershoot;
+}
+
+/*!
+ \qmlproperty enum QtQuick::BoundaryRule::overshootFilter
+
+ This property specifies the aggregation function that will be applied to
+ the intercepted property value.
+
+ If this is set to \c BoundaryRule.None (the default), the intercepted
+ property will hold a value whose overshoot is limited to \l currentOvershoot.
+ If this is set to \c BoundaryRule.Peak, the intercepted property will hold
+ a value whose overshoot is limited to \l peakOvershoot.
+*/
+QQuickBoundaryRule::OvershootFilter QQuickBoundaryRule::overshootFilter() const
+{
+ Q_D(const QQuickBoundaryRule);
+ return d->overshootFilter;
+}
+
+void QQuickBoundaryRule::setOvershootFilter(OvershootFilter overshootFilter)
+{
+ Q_D(QQuickBoundaryRule);
+ if (d->overshootFilter == overshootFilter)
+ return;
+ d->overshootFilter = overshootFilter;
+ emit overshootFilterChanged();
+}
+
+/*!
+ \qmlmethod bool QtQuick::BoundaryRule::returnToBounds
+
+ Returns the intercepted property to a value between \l minimum and
+ \l maximum, such that \l currentOvershoot and \l peakOvershoot are both
+ zero. This will be animated if \l returnDuration is greater than zero.
+
+ Returns true if the value needed to be adjusted, or false if it was already
+ within bounds.
+*/
+bool QQuickBoundaryRule::returnToBounds()
+{
+ Q_D(QQuickBoundaryRule);
+ if (d->returnAnimationJob) {
+ qCDebug(lcBR) << "animation already in progress";
+ return true;
+ }
+ if (currentOvershoot() > 0) {
+ if (d->returnDuration > 0)
+ d->returnAnimationJob = new QQuickBoundaryReturnJob(d, maximum());
+ else
+ write(maximum());
+ } else if (currentOvershoot() < 0) {
+ if (d->returnDuration > 0)
+ d->returnAnimationJob = new QQuickBoundaryReturnJob(d, minimum());
+ else
+ write(minimum());
+ } else {
+ return false;
+ }
+ if (d->returnAnimationJob) {
+ qCDebug(lcBR) << "animating from" << d->returnAnimationJob->fromValue << "to" << d->returnAnimationJob->toValue;
+ d->returnAnimationJob->start();
+ } else {
+ d->resetOvershoot();
+ qCDebug(lcBR) << "returned to" << d->property.read();
+ }
+ return true;
+}
+
+/*!
+ \qmlproperty qreal QtQuick::BoundaryRule::easing
+
+ This property holds the easing curve to be applied in overshoot mode
+ (whenever the \l minimum or \l maximum constraint is violated, while
+ the value is still within the respective overshoot range).
+
+ The default easing curve is \l QEasingCurve::OutQuad.
+*/
+QEasingCurve QQuickBoundaryRule::easing() const
+{
+ Q_D(const QQuickBoundaryRule);
+ return d->easing;
+}
+
+void QQuickBoundaryRule::setEasing(const QEasingCurve &easing)
+{
+ Q_D(QQuickBoundaryRule);
+ if (d->easing == easing)
+ return;
+ d->easing = easing;
+ emit easingChanged();
+}
+
+/*!
+ \qmlproperty int QtQuick::BoundaryRule::returnDuration
+
+ This property holds the amount of time in milliseconds that
+ \l returnToBounds() will take to return the target property to the nearest bound.
+ If it is set to 0, returnToBounds() will set the property immediately
+ rather than creating an animation job.
+
+ The default is 100 ms.
+*/
+int QQuickBoundaryRule::returnDuration() const
+{
+ Q_D(const QQuickBoundaryRule);
+ return d->returnDuration;
+}
+
+void QQuickBoundaryRule::setReturnDuration(int duration)
+{
+ Q_D(QQuickBoundaryRule);
+ if (d->returnDuration == duration)
+ return;
+ d->returnDuration = duration;
+ emit returnDurationChanged();
+}
+
+void QQuickBoundaryRule::write(const QVariant &value)
+{
+ bool conversionOk = false;
+ qreal rValue = value.toReal(&conversionOk);
+ if (!conversionOk) {
+ qWarning() << "BoundaryRule doesn't work with non-numeric values:" << value;
+ return;
+ }
+ Q_D(QQuickBoundaryRule);
+ bool bypass = !d->enabled || !d->finalized || QQmlEnginePrivate::designerMode();
+ if (bypass) {
+ QQmlPropertyPrivate::write(d->property, value,
+ QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
+ return;
+ }
+
+ qmlExecuteDeferred(this);
+ d->targetValue = d->easedOvershoot(rValue);
+ QQmlPropertyPrivate::write(d->property, d->targetValue,
+ QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
+}
+
+void QQuickBoundaryRule::setTarget(const QQmlProperty &property)
+{
+ Q_D(QQuickBoundaryRule);
+ d->property = property;
+
+ QQmlEnginePrivate *engPriv = QQmlEnginePrivate::get(qmlEngine(this));
+ static int finalizedIdx = -1;
+ if (finalizedIdx < 0)
+ finalizedIdx = metaObject()->indexOfSlot("componentFinalized()");
+ engPriv->registerFinalizeCallback(this, finalizedIdx);
+}
+
+void QQuickBoundaryRule::componentFinalized()
+{
+ Q_D(QQuickBoundaryRule);
+ d->finalized = true;
+}
+
+/*!
+ \internal
+ Given that something is trying to set the target property to \a value,
+ this function applies the easing curve and returns the value that the
+ property should actually get instead.
+*/
+qreal QQuickBoundaryRulePrivate::easedOvershoot(qreal value)
+{
+ qreal ret = value;
+ Q_Q(QQuickBoundaryRule);
+ if (value > maximum) {
+ qreal overshootWas = currentOvershoot;
+ currentOvershoot = value - maximum;
+ if (!qFuzzyCompare(overshootWas, currentOvershoot))
+ emit q->currentOvershootChanged();
+ overshootWas = peakOvershoot;
+ peakOvershoot = qMax(currentOvershoot, peakOvershoot);
+ if (!qFuzzyCompare(overshootWas, peakOvershoot))
+ emit q->peakOvershootChanged();
+ ret = maximum + maximumOvershoot * easing.valueForProgress(
+ (overshootFilter == QQuickBoundaryRule::OvershootFilter::Peak ? peakOvershoot : currentOvershoot)
+ * overshootScale / maximumOvershoot);
+ qCDebug(lcBR).nospace() << value << " overshoots maximum " << maximum << " by "
+ << currentOvershoot << " (peak " << peakOvershoot << "): eased to " << ret;
+ } else if (value < minimum) {
+ qreal overshootWas = currentOvershoot;
+ currentOvershoot = value - minimum;
+ if (!qFuzzyCompare(overshootWas, currentOvershoot))
+ emit q->currentOvershootChanged();
+ overshootWas = peakOvershoot;
+ peakOvershoot = qMin(currentOvershoot, peakOvershoot);
+ if (!qFuzzyCompare(overshootWas, peakOvershoot))
+ emit q->peakOvershootChanged();
+ ret = minimum - minimumOvershoot * easing.valueForProgress(
+ -(overshootFilter == QQuickBoundaryRule::OvershootFilter::Peak ? peakOvershoot : currentOvershoot)
+ * overshootScale / minimumOvershoot);
+ qCDebug(lcBR).nospace() << value << " overshoots minimum " << minimum << " by "
+ << currentOvershoot << " (peak " << peakOvershoot << "): eased to " << ret;
+ } else {
+ resetOvershoot();
+ }
+ return ret;
+}
+
+/*!
+ \internal
+ Resets the currentOvershoot and peakOvershoot
+ properties to zero.
+*/
+void QQuickBoundaryRulePrivate::resetOvershoot()
+{
+ Q_Q(QQuickBoundaryRule);
+ if (!qFuzzyCompare(peakOvershoot, 0)) {
+ peakOvershoot = 0;
+ emit q->peakOvershootChanged();
+ }
+ if (!qFuzzyCompare(currentOvershoot, 0)) {
+ currentOvershoot = 0;
+ emit q->currentOvershootChanged();
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickboundaryrule_p.cpp"
diff --git a/src/quick/util/qquickboundaryrule_p.h b/src/quick/util/qquickboundaryrule_p.h
new file mode 100644
index 0000000000..3325b675c5
--- /dev/null
+++ b/src/quick/util/qquickboundaryrule_p.h
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 QQUICKBOUNDARYRULE_H
+#define QQUICKBOUNDARYRULE_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/qtquickglobal_p.h>
+
+#include <private/qqmlpropertyvalueinterceptor_p.h>
+#include <qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickAbstractAnimation;
+class QQuickBoundaryRulePrivate;
+class Q_QUICK_PRIVATE_EXPORT QQuickBoundaryRule : public QObject, public QQmlPropertyValueInterceptor
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QQuickBoundaryRule)
+
+ Q_INTERFACES(QQmlPropertyValueInterceptor)
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
+ Q_PROPERTY(qreal minimum READ minimum WRITE setMinimum NOTIFY minimumChanged)
+ Q_PROPERTY(qreal minimumOvershoot READ minimumOvershoot WRITE setMinimumOvershoot NOTIFY minimumOvershootChanged)
+ Q_PROPERTY(qreal maximum READ maximum WRITE setMaximum NOTIFY maximumChanged)
+ Q_PROPERTY(qreal maximumOvershoot READ maximumOvershoot WRITE setMaximumOvershoot NOTIFY maximumOvershootChanged)
+ Q_PROPERTY(qreal overshootScale READ overshootScale WRITE setOvershootScale NOTIFY overshootScaleChanged)
+ Q_PROPERTY(qreal currentOvershoot READ currentOvershoot NOTIFY currentOvershootChanged)
+ Q_PROPERTY(qreal peakOvershoot READ peakOvershoot NOTIFY peakOvershootChanged)
+ Q_PROPERTY(OvershootFilter overshootFilter READ overshootFilter WRITE setOvershootFilter NOTIFY overshootFilterChanged)
+ Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing NOTIFY easingChanged)
+ Q_PROPERTY(int returnDuration READ returnDuration WRITE setReturnDuration NOTIFY returnDurationChanged)
+
+public:
+ enum OvershootFilter {
+ None,
+ Peak
+ };
+ Q_ENUM(OvershootFilter)
+
+ QQuickBoundaryRule(QObject *parent=nullptr);
+ ~QQuickBoundaryRule();
+
+ void setTarget(const QQmlProperty &) override;
+ void write(const QVariant &value) override;
+
+ bool enabled() const;
+ void setEnabled(bool enabled);
+
+ qreal minimum() const;
+ void setMinimum(qreal minimum);
+ qreal minimumOvershoot() const;
+ void setMinimumOvershoot(qreal minimum);
+
+ qreal maximum() const;
+ void setMaximum(qreal maximum);
+ qreal maximumOvershoot() const;
+ void setMaximumOvershoot(qreal maximum);
+
+ qreal overshootScale() const;
+ void setOvershootScale(qreal scale);
+
+ qreal currentOvershoot() const;
+ qreal peakOvershoot() const;
+
+ OvershootFilter overshootFilter() const;
+ void setOvershootFilter(OvershootFilter overshootFilter);
+
+ Q_INVOKABLE bool returnToBounds();
+
+ QEasingCurve easing() const;
+ void setEasing(const QEasingCurve &easing);
+
+ int returnDuration() const;
+ void setReturnDuration(int duration);
+
+Q_SIGNALS:
+ void enabledChanged();
+ void minimumChanged();
+ void minimumOvershootChanged();
+ void maximumChanged();
+ void maximumOvershootChanged();
+ void overshootScaleChanged();
+ void currentOvershootChanged();
+ void peakOvershootChanged();
+ void overshootFilterChanged();
+ void easingChanged();
+ void returnDurationChanged();
+
+private Q_SLOTS:
+ void componentFinalized();
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickBoundaryRule)
+
+#endif // QQUICKBOUNDARYRULE_H
diff --git a/src/quick/util/qquickglobal.cpp b/src/quick/util/qquickglobal.cpp
index 5337bfd640..527274e6be 100644
--- a/src/quick/util/qquickglobal.cpp
+++ b/src/quick/util/qquickglobal.cpp
@@ -41,7 +41,6 @@
#include <private/qquickvaluetypes_p.h>
#include <private/qquickapplication_p.h>
#include <private/qqmlglobal_p.h>
-#include <private/qv8engine_p.h>
#include <QtGui/QGuiApplication>
#include <QtGui/qdesktopservices.h>
@@ -274,7 +273,7 @@ public:
return QMatrix4x4();
}
- static QFont fontFromObject(QQmlV4Handle object, QV4::ExecutionEngine *v4, bool *ok)
+ static QFont fontFromObject(const QV4::Value &object, QV4::ExecutionEngine *v4, bool *ok)
{
if (ok)
*ok = false;
@@ -373,7 +372,7 @@ public:
return retn;
}
- static QMatrix4x4 matrix4x4FromObject(QQmlV4Handle object, QV4::ExecutionEngine *v4, bool *ok)
+ static QMatrix4x4 matrix4x4FromObject(const QV4::Value &object, QV4::ExecutionEngine *v4, bool *ok)
{
if (ok)
*ok = false;
@@ -639,7 +638,7 @@ public:
return false;
}
- bool variantFromJsObject(int type, QQmlV4Handle object, QV4::ExecutionEngine *v4, QVariant *v) override
+ bool variantFromJsObject(int type, const QV4::Value &object, QV4::ExecutionEngine *v4, QVariant *v) override
{
QV4::Scope scope(v4);
#ifndef QT_NO_DEBUG
diff --git a/src/quick/util/qquickimageprovider_p.h b/src/quick/util/qquickimageprovider_p.h
index b5baf79319..67e17010d4 100644
--- a/src/quick/util/qquickimageprovider_p.h
+++ b/src/quick/util/qquickimageprovider_p.h
@@ -61,7 +61,7 @@ class QQuickImageResponsePrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QQuickImageResponse)
public:
- bool finished = false;
+ QAtomicInteger<qint32> finished = false;
void _q_finished() { finished = true; }
};
diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp
index 60d725d650..61319c388c 100644
--- a/src/quick/util/qquickpath.cpp
+++ b/src/quick/util/qquickpath.cpp
@@ -60,7 +60,7 @@ QT_BEGIN_NAMESPACE
This type is the base for all path types. It cannot
be instantiated.
- \sa Path, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic, PathArc,
+ \sa Path, PathAttribute, PathPercent, PathLine, PathPolyline, PathQuad, PathCubic, PathArc,
PathAngleArc, PathCurve, PathSvg
*/
@@ -71,7 +71,7 @@ QT_BEGIN_NAMESPACE
\ingroup qtquick-animation-paths
\brief Defines a path for use by \l PathView and \l Shape.
- A Path is composed of one or more path segments - PathLine, PathQuad,
+ A Path is composed of one or more path segments - PathLine, PathPolyline, PathQuad,
PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg.
The spacing of the items along the Path can be adjusted via a
@@ -104,6 +104,17 @@ QT_BEGIN_NAMESPACE
\li Yes
\li Yes
\row
+ \li PathPolyline
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
+ \li PathMultiLine
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
\li PathQuad
\li Yes
\li Yes
@@ -156,7 +167,7 @@ QT_BEGIN_NAMESPACE
\note Path is a non-visual type; it does not display anything on its own.
To draw a path, use \l Shape.
- \sa PathView, Shape, PathAttribute, PathPercent, PathLine, PathMove, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg
+ \sa PathView, Shape, PathAttribute, PathPercent, PathLine, PathPolyline, PathMove, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg
*/
QQuickPath::QQuickPath(QObject *parent)
: QObject(*(new QQuickPathPrivate), parent)
@@ -240,6 +251,8 @@ bool QQuickPath::isClosed() const
A path can contain the following path objects:
\list
\li \l PathLine - a straight line to a given position.
+ \li \l PathPolyline - a polyline specified as a list of coordinates.
+ \li \l PathMultiline - a list of polylines specified as a list of lists of coordinates.
\li \l PathQuad - a quadratic Bezier curve to a given position with a control point.
\li \l PathCubic - a cubic Bezier curve to a given position with two control points.
\li \l PathArc - an arc to a given position with a radius.
@@ -407,6 +420,19 @@ void QQuickPath::processPath()
emit changed();
}
+inline static void scalePath(QPainterPath &path, const QSizeF &scale)
+{
+ const qreal xscale = scale.width();
+ const qreal yscale = scale.height();
+ if (xscale == 1 && yscale == 1)
+ return;
+
+ for (int i = 0; i < path.elementCount(); ++i) {
+ const QPainterPath::Element &element = path.elementAt(i);
+ path.setElementPositionAt(i, element.x * xscale, element.y * yscale);
+ }
+}
+
QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &endPoint, const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints, bool *closed)
{
Q_D(QQuickPath);
@@ -465,7 +491,7 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en
d->_attributePoints.last().values[percentString] = 1;
interpolate(d->_attributePoints.count() - 1, percentString, 1);
}
-
+ scalePath(path, d->scale);
// Adjust percent
qreal length = path.length();
@@ -491,7 +517,7 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en
if (closed) {
QPointF end = path.currentPosition();
- *closed = length > 0 && startX == end.x() && startY == end.y();
+ *closed = length > 0 && startX * d->scale.width() == end.x() && startY * d->scale.height() == end.y();
}
pathLength = length;
@@ -525,6 +551,7 @@ QPainterPath QQuickPath::createShapePath(const QPointF &startPoint, const QPoint
QPointF end = path.currentPosition();
*closed = startX == end.x() && startY == end.y();
}
+ scalePath(path, d->scale);
// Note: Length of paths inside ShapePath is not used, so currently
// length is always 0. This avoids potentially heavy path.length()
@@ -570,7 +597,7 @@ void QQuickPath::gatherAttributes()
attributes.insert(attribute->name());
}
- d->_attributes = attributes.toList();
+ d->_attributes = attributes.values();
}
void QQuickPath::componentComplete()
@@ -603,7 +630,7 @@ QStringList QQuickPath::attributes() const
qobject_cast<QQuickPathAttribute *>(pathElement))
attrs.insert(attribute->name());
}
- return attrs.toList();
+ return attrs.values();
}
return d->_attributes;
}
@@ -723,6 +750,33 @@ void QQuickPath::invalidateSequentialHistory() const
d->prevBez.isValid = false;
}
+/*!
+ \qmlproperty size QtQuick::Path::scale
+
+ This property holds the scale factor for the path.
+ The width and height of \a scale can be different, to
+ achieve anisotropic scaling.
+
+ \note Setting this property will not affect the border width.
+
+ \since QtQuick 2.14
+*/
+QSizeF QQuickPath::scale() const
+{
+ Q_D(const QQuickPath);
+ return d->scale;
+}
+
+void QQuickPath::setScale(const QSizeF &scale)
+{
+ Q_D(QQuickPath);
+ if (scale == d->scale)
+ return;
+ d->scale = scale;
+ emit scaleChanged();
+ processPath();
+}
+
QPointF QQuickPath::sequentialPointAt(qreal p, qreal *angle) const
{
Q_D(const QQuickPath);
@@ -852,9 +906,28 @@ QPointF QQuickPath::backwardsPointAt(const QPainterPath &path, const qreal &path
return QPointF(0,0);
}
-QPointF QQuickPath::pointAt(qreal p) const
+/*!
+ \qmlmethod point Path::pointAtPercent(real t)
+
+ Returns the point at the percentage \a t of the current path.
+ The argument \a t has to be between 0 and 1.
+
+ \note Similarly to other percent methods in \l QPainterPath,
+ the percentage measurement is not linear with regards to the length,
+ if curves are present in the path.
+ When curves are present, the percentage argument is mapped to the \c t
+ parameter of the Bezier equations.
+
+ \sa QPainterPath::pointAt
+
+ \since QtQuick 2.14
+*/
+QPointF QQuickPath::pointAtPercent(qreal t) const
{
Q_D(const QQuickPath);
+ if (d->isShapePath) // this since ShapePath does not calculate the length at all,
+ return d->_path.pointAtPercent(t); // in order to be faster.
+
if (d->_pointCache.isEmpty()) {
createPointCache();
if (d->_pointCache.isEmpty())
@@ -862,7 +935,7 @@ QPointF QQuickPath::pointAt(qreal p) const
}
const int segmentCount = d->_pointCache.size() - 1;
- qreal idxf = p*segmentCount;
+ qreal idxf = t*segmentCount;
int idx1 = qFloor(idxf);
qreal delta = idxf - idx1;
if (idx1 > segmentCount)
@@ -1127,7 +1200,7 @@ void QQuickPathAttribute::setValue(qreal value)
}
\endqml
- \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove
+ \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove, PathPolyline
*/
/*!
@@ -2286,6 +2359,266 @@ void QQuickPathPercent::setValue(qreal value)
emit changed();
}
}
+
+/*!
+ \qmltype PathPolyline
+ \instantiates QQuickPathPolyline
+ \inqmlmodule QtQuick
+ \ingroup qtquick-animation-paths
+ \brief Defines a polyline through a list of coordinates.
+ \since QtQuick 2.14
+
+ The example below creates a triangular path consisting of four vertices
+ on the edge of the containing Shape's bounding box.
+ Through the containing shape's \l scale property, the path will be
+ rescaled together with its containing shape.
+
+ \qml
+ PathPolyline {
+ id: ppl
+ path: [ Qt.point(0.0, 0.0),
+ Qt.point(1.0, 0.0),
+ Qt.point(0.5, 1.0),
+ Qt.point(0.0, 0.0)
+ ]
+ }
+ \endqml
+
+ \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove, PathPolyline
+*/
+
+/*!
+ \qmlproperty point QtQuick::PathPolyline::start
+
+ This read-only property contains the beginning of the polyline.
+*/
+
+/*!
+ \qmlproperty list<point> QtQuick::PathPolyline::path
+
+ This property defines the vertices of the polyline.
+
+ It can be a JS array of points constructed with \c Qt.point(),
+ a QList or QVector of QPointF, or QPolygonF.
+ If you are binding this to a custom property in some C++ object,
+ QPolygonF is the most appropriate type to use.
+*/
+
+QQuickPathPolyline::QQuickPathPolyline(QObject *parent) : QQuickCurve(parent)
+{
+}
+
+QVariant QQuickPathPolyline::path() const
+{
+ return QVariant::fromValue(m_path);
+}
+
+void QQuickPathPolyline::setPath(const QVariant &path)
+{
+ if (path.type() == QVariant::PolygonF) {
+ setPath(path.value<QPolygonF>());
+ } else if (path.canConvert<QVector<QPointF>>()) {
+ setPath(path.value<QVector<QPointF>>());
+ } else if (path.canConvert<QVariantList>()) {
+ // This handles cases other than QPolygonF or QVector<QPointF>, such as
+ // QList<QPointF>, QVector<QPoint>, QVariantList of QPointF, QVariantList of QPoint.
+ QVector<QPointF> pathList;
+ QVariantList vl = path.value<QVariantList>();
+ // If path is a QJSValue, e.g. coming from a JS array of Qt.point() in QML,
+ // then path.value<QVariantList>() is inefficient.
+ // TODO We should be able to iterate over path.value<QSequentialIterable>() eventually
+ for (const QVariant &v : vl)
+ pathList.append(v.toPointF());
+ setPath(pathList);
+ } else {
+ qWarning() << "PathPolyline: path of type" << path.type() << "not supported";
+ }
+}
+
+void QQuickPathPolyline::setPath(const QVector<QPointF> &path)
+{
+ if (m_path != path) {
+ const QPointF &oldStart = start();
+ m_path = path;
+ const QPointF &newStart = start();
+ emit pathChanged();
+ if (oldStart != newStart)
+ emit startChanged();
+ emit changed();
+ }
+}
+
+QPointF QQuickPathPolyline::start() const
+{
+ if (m_path.size()) {
+ const QPointF &p = m_path.first();
+ return p;
+ }
+ return QPointF();
+}
+
+void QQuickPathPolyline::addToPath(QPainterPath &path, const QQuickPathData &/*data*/)
+{
+ if (m_path.size() < 2)
+ return;
+
+ path.moveTo(m_path.first());
+ for (int i = 1; i < m_path.size(); ++i)
+ path.lineTo(m_path.at(i));
+}
+
+
+/*!
+ \qmltype PathMultiline
+ \instantiates QQuickPathMultiline
+ \inqmlmodule QtQuick
+ \ingroup qtquick-animation-paths
+ \brief Defines a set of polylines through a list of lists of coordinates.
+ \since QtQuick 2.14
+
+ This element allows to define a list of polylines at once.
+ Each polyline in the list will be preceded by a \l{QPainterPath::moveTo}{moveTo}
+ command, effectively making each polyline a separate one.
+ The polylines in this list are supposed to be non-intersecting with each other.
+ In any case, when used in conjunction with a \l ShapePath, the containing ShapePath's
+ \l ShapePath::fillRule applies.
+ That is, with the default \c OddEvenFill and non intersecting shapes, the largest shape in the list defines an area to be filled;
+ areas where two shapes overlap are holes; areas where three shapes overlap are filled areas inside holes, etc.
+
+ The example below creates a high voltage symbol by adding each path
+ of the symbol to the list of paths.
+ The coordinates of the vertices are normalized, and through the containing shape's
+ \l scale property, the path will be rescaled together with its containing shape.
+
+ \qml
+ PathMultiline {
+ paths: [
+ [Qt.point(0.5, 0.06698),
+ Qt.point(1, 0.93301),
+ Qt.point(0, 0.93301),
+ Qt.point(0.5, 0.06698)],
+
+ [Qt.point(0.5, 0.12472),
+ Qt.point(0.95, 0.90414),
+ Qt.point(0.05, 0.90414),
+ Qt.point(0.5, 0.12472)],
+
+ [Qt.point(0.47131, 0.32986),
+ Qt.point(0.36229, 0.64789),
+ Qt.point(0.51492, 0.58590),
+ Qt.point(0.47563, 0.76014),
+ Qt.point(0.44950, 0.73590),
+ Qt.point(0.46292, 0.83392),
+ Qt.point(0.52162, 0.75190),
+ Qt.point(0.48531, 0.76230),
+ Qt.point(0.57529, 0.53189),
+ Qt.point(0.41261, 0.59189),
+ Qt.point(0.53001, 0.32786),
+ Qt.point(0.47131, 0.32986)]
+ ]
+ }
+ \endqml
+
+ \sa Path, QPainterPath::setFillRule, PathPolyline, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove
+*/
+
+/*!
+ \qmlproperty point QtQuick::PathMultiline::start
+
+ This read-only property contains the beginning of the polylines.
+*/
+
+/*!
+ \qmlproperty list<list<point>> QtQuick::PathMultiline::paths
+
+ This property defines the vertices of the polylines.
+
+ It can be a JS array of JS arrays of points constructed with \c Qt.point(),
+ a QList or QVector of QPolygonF, or QVector<QVector<QPointF>>.
+ If you are binding this to a custom property in some C++ object,
+ QVector<QPolygonF> or QVector<QVector<QPointF>> is the most
+ appropriate type to use.
+*/
+
+QQuickPathMultiline::QQuickPathMultiline(QObject *parent) : QQuickCurve(parent)
+{
+}
+
+QVariant QQuickPathMultiline::paths() const
+{
+ return QVariant::fromValue(m_paths);
+}
+
+void QQuickPathMultiline::setPaths(const QVariant &paths)
+{
+ if (paths.canConvert<QVector<QPolygonF>>()) {
+ const QVector<QPolygonF> pathPolygons = paths.value<QVector<QPolygonF>>();
+ QVector<QVector<QPointF>> pathVectors;
+ for (const QPolygonF &p : pathPolygons)
+ pathVectors << p;
+ setPaths(pathVectors);
+ } else if (paths.canConvert<QVector<QVector<QPointF>>>()) {
+ setPaths(paths.value<QVector<QVector<QPointF>>>());
+ } else if (paths.canConvert<QVariantList>()) {
+ // This handles cases other than QVector<QPolygonF> or QVector<QVector<QPointF>>, such as
+ // QList<QVector<QPointF>>, QList<QList<QPointF>>, QVariantList of QVector<QPointF>,
+ // QVariantList of QVariantList of QPointF, QVector<QList<QPoint>> etc.
+ QVector<QVector<QPointF>> pathsList;
+ QVariantList vll = paths.value<QVariantList>();
+ for (const QVariant &v : vll) {
+ // If we bind a QVector<QPolygonF> property directly, rather than via QVariant,
+ // it will come through as QJSValue that can be converted to QVariantList of QPolygonF.
+ if (v.canConvert<QPolygonF>()) {
+ pathsList.append(v.value<QPolygonF>());
+ } else {
+ QVariantList vl = v.value<QVariantList>();
+ QVector<QPointF> l;
+ for (const QVariant &point : vl) {
+ if (point.canConvert<QPointF>())
+ l.append(point.toPointF());
+ }
+ if (l.size() >= 2)
+ pathsList.append(l);
+ }
+ }
+ setPaths(pathsList);
+ } else {
+ qWarning() << "PathMultiline: paths of type" << paths.type() << "not supported";
+ setPaths(QVector<QVector<QPointF>>());
+ }
+}
+
+void QQuickPathMultiline::setPaths(const QVector<QVector<QPointF>> &paths)
+{
+ if (m_paths != paths) {
+ const QPointF &oldStart = start();
+ m_paths = paths;
+ const QPointF &newStart = start();
+ emit pathsChanged();
+ if (oldStart != newStart)
+ emit startChanged();
+ emit changed();
+ }
+}
+
+QPointF QQuickPathMultiline::start() const
+{
+ if (m_paths.size())
+ return m_paths.first().first();
+ return QPointF();
+}
+
+void QQuickPathMultiline::addToPath(QPainterPath &path, const QQuickPathData &)
+{
+ if (!m_paths.size())
+ return;
+ for (const QVector<QPointF> &p: m_paths) {
+ path.moveTo(p.first());
+ for (int i = 1; i < p.size(); ++i)
+ path.lineTo(p.at(i));
+ }
+}
+
QT_END_NAMESPACE
#include "moc_qquickpath_p.cpp"
diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h
index 6b9a40fe6d..5987ae8f35 100644
--- a/src/quick/util/qquickpath_p.h
+++ b/src/quick/util/qquickpath_p.h
@@ -290,7 +290,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPathArc : public QQuickCurve
Q_PROPERTY(qreal radiusY READ radiusY WRITE setRadiusY NOTIFY radiusYChanged)
Q_PROPERTY(bool useLargeArc READ useLargeArc WRITE setUseLargeArc NOTIFY useLargeArcChanged)
Q_PROPERTY(ArcDirection direction READ direction WRITE setDirection NOTIFY directionChanged)
- Q_PROPERTY(qreal xAxisRotation READ xAxisRotation WRITE setXAxisRotation NOTIFY xAxisRotationChanged REVISION 2)
+ Q_PROPERTY(qreal xAxisRotation READ xAxisRotation WRITE setXAxisRotation NOTIFY xAxisRotationChanged REVISION 9)
public:
QQuickPathArc(QObject *parent=nullptr)
@@ -321,7 +321,7 @@ Q_SIGNALS:
void radiusYChanged();
void useLargeArcChanged();
void directionChanged();
- Q_REVISION(2) void xAxisRotationChanged();
+ Q_REVISION(9) void xAxisRotationChanged();
private:
qreal _radiusX = 0;
@@ -424,6 +424,52 @@ private:
qreal _value = 0;
};
+class Q_QUICK_PRIVATE_EXPORT QQuickPathPolyline : public QQuickCurve
+{
+ Q_OBJECT
+ Q_PROPERTY(QPointF start READ start NOTIFY startChanged)
+ Q_PROPERTY(QVariant path READ path WRITE setPath NOTIFY pathChanged)
+public:
+ QQuickPathPolyline(QObject *parent=nullptr);
+
+ QVariant path() const;
+ void setPath(const QVariant &path);
+ void setPath(const QVector<QPointF> &path);
+ QPointF start() const;
+ void addToPath(QPainterPath &path, const QQuickPathData &data) override;
+
+Q_SIGNALS:
+ void pathChanged();
+ void startChanged();
+
+private:
+ QVector<QPointF> m_path;
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPathMultiline : public QQuickCurve
+{
+ Q_OBJECT
+ Q_PROPERTY(QPointF start READ start NOTIFY startChanged)
+ Q_PROPERTY(QVariant paths READ paths WRITE setPaths NOTIFY pathsChanged)
+public:
+ QQuickPathMultiline(QObject *parent=nullptr);
+
+ QVariant paths() const;
+ void setPaths(const QVariant &paths);
+ void setPaths(const QVector<QVector<QPointF>> &paths);
+ QPointF start() const;
+ void addToPath(QPainterPath &path, const QQuickPathData &) override;
+
+Q_SIGNALS:
+ void pathsChanged();
+ void startChanged();
+
+private:
+ QPointF absolute(const QPointF &relative) const;
+
+ QVector<QVector<QPointF>> m_paths;
+};
+
struct QQuickCachedBezier
{
QQuickCachedBezier() {}
@@ -445,6 +491,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPath : public QObject, public QQmlParserStatu
Q_PROPERTY(qreal startX READ startX WRITE setStartX NOTIFY startXChanged)
Q_PROPERTY(qreal startY READ startY WRITE setStartY NOTIFY startYChanged)
Q_PROPERTY(bool closed READ isClosed NOTIFY changed)
+ Q_PROPERTY(QSizeF scale READ scale WRITE setScale NOTIFY scaleChanged REVISION 14)
Q_CLASSINFO("DefaultProperty", "pathElements")
Q_INTERFACES(QQmlParserStatus)
public:
@@ -466,14 +513,18 @@ public:
QPainterPath path() const;
QStringList attributes() const;
qreal attributeAt(const QString &, qreal) const;
- QPointF pointAt(qreal) const;
+ Q_REVISION(14) Q_INVOKABLE QPointF pointAtPercent(qreal t) const;
QPointF sequentialPointAt(qreal p, qreal *angle = nullptr) const;
void invalidateSequentialHistory() const;
+ QSizeF scale() const;
+ void setScale(const QSizeF &scale);
+
Q_SIGNALS:
void changed();
void startXChanged();
void startYChanged();
+ Q_REVISION(14) void scaleChanged();
protected:
QQuickPath(QQuickPathPrivate &dd, QObject *parent = nullptr);
@@ -540,6 +591,7 @@ QML_DECLARE_TYPE(QQuickPathArc)
QML_DECLARE_TYPE(QQuickPathAngleArc)
QML_DECLARE_TYPE(QQuickPathSvg)
QML_DECLARE_TYPE(QQuickPathPercent)
+QML_DECLARE_TYPE(QQuickPathPolyline)
QML_DECLARE_TYPE(QQuickPath)
#endif // QQUICKPATH_H
diff --git a/src/quick/util/qquickpath_p_p.h b/src/quick/util/qquickpath_p_p.h
index 9735d51264..e26001ec77 100644
--- a/src/quick/util/qquickpath_p_p.h
+++ b/src/quick/util/qquickpath_p_p.h
@@ -84,6 +84,7 @@ public:
QQmlNullableValue<qreal> startX;
QQmlNullableValue<qreal> startY;
qreal pathLength = 0;
+ QSizeF scale = QSizeF(1, 1);
bool closed = false;
bool componentComplete = true;
bool isShapePath = false;
diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp
index ced0acd9ab..56ad8ebf0b 100644
--- a/src/quick/util/qquickpixmapcache.cpp
+++ b/src/quick/util/qquickpixmapcache.cpp
@@ -49,7 +49,7 @@
#include <QtGui/private/qimage_p.h>
#include <qpa/qplatformintegration.h>
-#include <QtQuick/private/qsgtexture_p.h>
+#include <QtQuick/private/qsgcontext_p.h>
#include <QtQuick/private/qsgtexturereader_p.h>
#include <QQuickWindow>
@@ -207,7 +207,7 @@ protected:
private:
friend class QQuickPixmapReaderThreadObject;
void processJobs();
- void processJob(QQuickPixmapReply *, const QUrl &, const QString &, QQuickImageProvider::ImageType, QQuickImageProvider *);
+ void processJob(QQuickPixmapReply *, const QUrl &, const QString &, QQuickImageProvider::ImageType, const QSharedPointer<QQuickImageProvider> &);
#if QT_CONFIG(qml_network)
void networkRequestDone(QNetworkReply *);
#endif
@@ -241,7 +241,7 @@ class QQuickPixmapData
{
public:
QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, const QSize &s, const QQuickImageProviderOptions &po, const QString &e)
- : refCount(1), inCache(false), pixmapStatus(QQuickPixmap::Error),
+ : refCount(1), frameCount(1), frame(0), inCache(false), pixmapStatus(QQuickPixmap::Error),
url(u), errorString(e), requestSize(s),
providerOptions(po), appliedTransform(QQuickImageProviderOptions::UsePluginDefaultTransform),
textureFactory(nullptr), reply(nullptr), prevUnreferenced(nullptr),
@@ -250,8 +250,8 @@ public:
declarativePixmaps.insert(pixmap);
}
- QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, const QSize &r, const QQuickImageProviderOptions &po, QQuickImageProviderOptions::AutoTransform aTransform)
- : refCount(1), inCache(false), pixmapStatus(QQuickPixmap::Loading),
+ QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, const QSize &r, const QQuickImageProviderOptions &po, QQuickImageProviderOptions::AutoTransform aTransform, int frame=0, int frameCount=1)
+ : refCount(1), frameCount(frameCount), frame(frame), inCache(false), pixmapStatus(QQuickPixmap::Loading),
url(u), requestSize(r),
providerOptions(po), appliedTransform(aTransform),
textureFactory(nullptr), reply(nullptr), prevUnreferenced(nullptr), prevUnreferencedPtr(nullptr),
@@ -261,8 +261,8 @@ public:
}
QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, QQuickTextureFactory *texture,
- const QSize &s, const QSize &r, const QQuickImageProviderOptions &po, QQuickImageProviderOptions::AutoTransform aTransform)
- : refCount(1), inCache(false), pixmapStatus(QQuickPixmap::Ready),
+ const QSize &s, const QSize &r, const QQuickImageProviderOptions &po, QQuickImageProviderOptions::AutoTransform aTransform, int frame=0, int frameCount=1)
+ : refCount(1), frameCount(frameCount), frame(frame), inCache(false), pixmapStatus(QQuickPixmap::Ready),
url(u), implicitSize(s), requestSize(r),
providerOptions(po), appliedTransform(aTransform),
textureFactory(texture), reply(nullptr), prevUnreferenced(nullptr),
@@ -272,7 +272,7 @@ public:
}
QQuickPixmapData(QQuickPixmap *pixmap, QQuickTextureFactory *texture)
- : refCount(1), inCache(false), pixmapStatus(QQuickPixmap::Ready),
+ : refCount(1), frameCount(1), frame(0), inCache(false), pixmapStatus(QQuickPixmap::Ready),
appliedTransform(QQuickImageProviderOptions::UsePluginDefaultTransform),
textureFactory(texture), reply(nullptr), prevUnreferenced(nullptr),
prevUnreferencedPtr(nullptr), nextUnreferenced(nullptr)
@@ -299,6 +299,8 @@ public:
void removeFromCache();
uint refCount;
+ int frameCount;
+ int frame;
bool inCache:1;
@@ -396,9 +398,9 @@ static void maybeRemoveAlpha(QImage *image)
}
}
-static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize,
+static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize, int *frameCount,
const QSize &requestSize, const QQuickImageProviderOptions &providerOptions,
- QQuickImageProviderOptions::AutoTransform *appliedTransform = nullptr)
+ QQuickImageProviderOptions::AutoTransform *appliedTransform = nullptr, int frame = 0)
{
QImageReader imgio(dev);
if (providerOptions.autoTransform() != QQuickImageProviderOptions::UsePluginDefaultTransform)
@@ -406,6 +408,12 @@ static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *e
else if (appliedTransform)
*appliedTransform = imgio.autoTransform() ? QQuickImageProviderOptions::ApplyTransform : QQuickImageProviderOptions::DoNotApplyTransform;
+ if (frame < imgio.imageCount())
+ imgio.jumpToImage(frame);
+
+ if (frameCount)
+ *frameCount = imgio.imageCount();
+
QSize scSize = QQuickImageProviderWithOptions::loadSize(imgio.size(), requestSize, imgio.format(), providerOptions);
if (scSize.isValid())
imgio.setScaledSize(scSize);
@@ -558,9 +566,12 @@ void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply)
QByteArray all = reply->readAll();
QBuffer buff(&all);
buff.open(QIODevice::ReadOnly);
- if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->requestSize, job->providerOptions))
+ int frameCount;
+ if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, &frameCount, job->requestSize, job->providerOptions, nullptr, job->data->frame))
error = QQuickPixmapReply::Decoding;
- }
+ else
+ job->data->frameCount = frameCount;
+ }
// send completion event to the QQuickPixmapReply
mutex.lock();
if (!cancelled.contains(job))
@@ -683,10 +694,11 @@ void QQuickPixmapReader::processJobs()
const QUrl url = job->url;
QString localFile;
QQuickImageProvider::ImageType imageType = QQuickImageProvider::Invalid;
- QQuickImageProvider *provider = nullptr;
+ QSharedPointer<QQuickImageProvider> provider;
if (url.scheme() == QLatin1String("image")) {
- provider = static_cast<QQuickImageProvider *>(engine->imageProvider(imageProviderId(url)));
+ QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
+ provider = enginePrivate->imageProvider(imageProviderId(url)).staticCast<QQuickImageProvider>();
if (provider)
imageType = provider->imageType();
@@ -721,7 +733,7 @@ void QQuickPixmapReader::processJobs()
}
void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &url, const QString &localFile,
- QQuickImageProvider::ImageType imageType, QQuickImageProvider *provider)
+ QQuickImageProvider::ImageType imageType, const QSharedPointer<QQuickImageProvider> &provider)
{
// fetch
if (url.scheme() == QLatin1String("image")) {
@@ -737,7 +749,8 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
return;
}
- QQuickImageProviderWithOptions *providerV2 = QQuickImageProviderWithOptions::checkedCast(provider);
+ // This is safe because we ensure that provider does outlive providerV2 and it does not escape the function
+ QQuickImageProviderWithOptions *providerV2 = QQuickImageProviderWithOptions::checkedCast(provider.get());
switch (imageType) {
case QQuickImageProvider::Invalid:
@@ -817,17 +830,24 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
if (providerV2) {
response = providerV2->requestImageResponse(imageId(url), runningJob->requestSize, runningJob->providerOptions);
} else {
- QQuickAsyncImageProvider *asyncProvider = static_cast<QQuickAsyncImageProvider*>(provider);
+ QQuickAsyncImageProvider *asyncProvider = static_cast<QQuickAsyncImageProvider*>(provider.get());
response = asyncProvider->requestImageResponse(imageId(url), runningJob->requestSize);
}
+ {
+ QObject::connect(response, SIGNAL(finished()), threadObject, SLOT(asyncResponseFinished()));
+ // as the response object can outlive the provider QSharedPointer, we have to extend the pointee's lifetime by that of the response
+ // we do this by capturing a copy of the QSharedPointer in a lambda, and dropping it once the lambda has been called
+ auto provider_copy = provider; // capturing provider would capture it as a const reference, and copy capture with initializer is only available in C++14
+ QObject::connect(response, &QQuickImageResponse::destroyed, response, [provider_copy]() {
+ // provider_copy will be deleted when the connection gets deleted
+ });
+ }
// Might be that the async provider was so quick it emitted the signal before we
// could connect to it.
- if (static_cast<QQuickImageResponsePrivate*>(QObjectPrivate::get(response))->finished) {
+ if (static_cast<QQuickImageResponsePrivate*>(QObjectPrivate::get(response))->finished.loadAcquire()) {
QMetaObject::invokeMethod(threadObject, "asyncResponseFinished",
Qt::QueuedConnection, Q_ARG(QQuickImageResponse*, response));
- } else {
- QObject::connect(response, SIGNAL(finished()), threadObject, SLOT(asyncResponseFinished()));
}
asyncResponses.insert(response, runningJob);
@@ -861,10 +881,13 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
mutex.unlock();
return;
} else {
- if (!readImage(url, &f, &image, &errorStr, &readSize, runningJob->requestSize, runningJob->providerOptions)) {
+ int frameCount;
+ if (!readImage(url, &f, &image, &errorStr, &readSize, &frameCount, runningJob->requestSize, runningJob->providerOptions, nullptr, runningJob->data->frame)) {
errorCode = QQuickPixmapReply::Loading;
if (f.fileName() != localFile)
errorStr += QString::fromLatin1(" (%1)").arg(f.fileName());
+ } else if (runningJob->data) {
+ runningJob->data->frameCount = frameCount;
}
}
} else {
@@ -973,17 +996,18 @@ class QQuickPixmapKey
public:
const QUrl *url;
const QSize *size;
+ int frame;
QQuickImageProviderOptions options;
};
inline bool operator==(const QQuickPixmapKey &lhs, const QQuickPixmapKey &rhs)
{
- return *lhs.size == *rhs.size && *lhs.url == *rhs.url && lhs.options == rhs.options;
+ return *lhs.size == *rhs.size && *lhs.url == *rhs.url && lhs.options == rhs.options && lhs.frame == rhs.frame;
}
inline uint qHash(const QQuickPixmapKey &key)
{
- return qHash(*key.url) ^ (key.size->width()*7) ^ (key.size->height()*17) ^ (key.options.autoTransform() * 0x5c5c5c5c);
+ return qHash(*key.url) ^ (key.size->width()*7) ^ (key.size->height()*17) ^ (key.frame*23) ^ (key.options.autoTransform() * 0x5c5c5c5c);
}
class QQuickPixmapStore : public QObject
@@ -1245,7 +1269,7 @@ void QQuickPixmapData::release()
void QQuickPixmapData::addToCache()
{
if (!inCache) {
- QQuickPixmapKey key = { &url, &requestSize, providerOptions };
+ QQuickPixmapKey key = { &url, &requestSize, frame, providerOptions };
pixmapStore()->m_cache.insert(key, this);
inCache = true;
PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>(
@@ -1256,7 +1280,7 @@ void QQuickPixmapData::addToCache()
void QQuickPixmapData::removeFromCache()
{
if (inCache) {
- QQuickPixmapKey key = { &url, &requestSize, providerOptions };
+ QQuickPixmapKey key = { &url, &requestSize, frame, providerOptions };
pixmapStore()->m_cache.remove(key);
inCache = false;
PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>(
@@ -1264,14 +1288,16 @@ void QQuickPixmapData::removeFromCache()
}
}
-static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, QQmlEngine *engine, const QUrl &url, const QSize &requestSize, const QQuickImageProviderOptions &providerOptions, bool *ok)
+static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, QQmlEngine *engine, const QUrl &url, const QSize &requestSize, const QQuickImageProviderOptions &providerOptions, int frame, bool *ok)
{
if (url.scheme() == QLatin1String("image")) {
QSize readSize;
QQuickImageProvider::ImageType imageType = QQuickImageProvider::Invalid;
- QQuickImageProvider *provider = static_cast<QQuickImageProvider *>(engine->imageProvider(imageProviderId(url)));
- QQuickImageProviderWithOptions *providerV2 = QQuickImageProviderWithOptions::checkedCast(provider);
+ QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
+ QSharedPointer<QQuickImageProvider> provider = enginePrivate->imageProvider(imageProviderId(url)).dynamicCast<QQuickImageProvider>();
+ // it is safe to use get() as providerV2 does not escape and is outlived by provider
+ QQuickImageProviderWithOptions *providerV2 = QQuickImageProviderWithOptions::checkedCast(provider.get());
if (provider)
imageType = provider->imageType();
@@ -1285,7 +1311,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q
: provider->requestTexture(imageId(url), &readSize, requestSize);
if (texture) {
*ok = true;
- return new QQuickPixmapData(declarativePixmap, url, texture, readSize, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform);
+ return new QQuickPixmapData(declarativePixmap, url, texture, readSize, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame);
}
break;
}
@@ -1296,7 +1322,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q
: provider->requestImage(imageId(url), &readSize, requestSize);
if (!image.isNull()) {
*ok = true;
- return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform);
+ return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame);
}
break;
}
@@ -1306,7 +1332,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q
: provider->requestPixmap(imageId(url), &readSize, requestSize);
if (!pixmap.isNull()) {
*ok = true;
- return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()), readSize, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform);
+ return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()), readSize, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame);
}
break;
}
@@ -1336,7 +1362,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q
QQuickTextureFactory *factory = texReader.read();
if (factory) {
*ok = true;
- return new QQuickPixmapData(declarativePixmap, url, factory, factory->textureSize(), requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform);
+ return new QQuickPixmapData(declarativePixmap, url, factory, factory->textureSize(), requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame);
} else {
errorString = QQuickPixmap::tr("Error decoding: %1").arg(url.toString());
if (f.fileName() != localFile)
@@ -1345,9 +1371,10 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q
} else {
QImage image;
QQuickImageProviderOptions::AutoTransform appliedTransform = providerOptions.autoTransform();
- if (readImage(url, &f, &image, &errorString, &readSize, requestSize, providerOptions, &appliedTransform)) {
+ int frameCount;
+ if (readImage(url, &f, &image, &errorString, &readSize, &frameCount, requestSize, providerOptions, &appliedTransform, frame)) {
*ok = true;
- return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, providerOptions, appliedTransform);
+ return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, providerOptions, appliedTransform, frame, frameCount);
} else if (f.fileName() != localFile) {
errorString += QString::fromLatin1(" (%1)").arg(f.fileName());
}
@@ -1465,6 +1492,13 @@ QQuickImageProviderOptions::AutoTransform QQuickPixmap::autoTransform() const
return QQuickImageProviderOptions::UsePluginDefaultTransform;
}
+int QQuickPixmap::frameCount() const
+{
+ if (d)
+ return d->frameCount;
+ return 0;
+}
+
QQuickTextureFactory *QQuickPixmap::textureFactory() const
{
if (d)
@@ -1543,7 +1577,7 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &reques
load(engine, url, requestSize, options, QQuickImageProviderOptions());
}
-void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &requestSize, QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions)
+void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &requestSize, QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions, int frame, int frameCount)
{
if (d) {
d->declarativePixmaps.remove(this);
@@ -1551,7 +1585,7 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &reques
d = nullptr;
}
- QQuickPixmapKey key = { &url, &requestSize, providerOptions };
+ QQuickPixmapKey key = { &url, &requestSize, frame, providerOptions };
QQuickPixmapStore *store = pixmapStore();
QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.end();
@@ -1563,14 +1597,15 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &reques
QSize dummy;
if (requestSize != dummy)
qWarning() << "Ignoring sourceSize request for image url that came from grabToImage. Use the targetSize parameter of the grabToImage() function instead.";
- const QQuickPixmapKey grabberKey = { &url, &dummy, QQuickImageProviderOptions() };
+ const QQuickPixmapKey grabberKey = { &url, &dummy, 0, QQuickImageProviderOptions() };
iter = store->m_cache.find(grabberKey);
} else if (options & QQuickPixmap::Cache)
iter = store->m_cache.find(key);
if (iter == store->m_cache.end()) {
if (url.scheme() == QLatin1String("image")) {
- if (QQuickImageProvider *provider = static_cast<QQuickImageProvider *>(engine->imageProvider(imageProviderId(url)))) {
+ QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
+ if (auto provider = enginePrivate->imageProvider(imageProviderId(url)).staticCast<QQuickImageProvider>()) {
const bool threadedPixmaps = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedPixmaps);
if (!threadedPixmaps && provider->imageType() == QQuickImageProvider::Pixmap) {
// pixmaps can only be loaded synchronously
@@ -1584,7 +1619,7 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &reques
if (!(options & QQuickPixmap::Asynchronous)) {
bool ok = false;
PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url));
- d = createPixmapDataSync(this, engine, url, requestSize, providerOptions, &ok);
+ d = createPixmapDataSync(this, engine, url, requestSize, providerOptions, frame, &ok);
if (ok) {
PIXMAP_PROFILE(pixmapLoadingFinished(url, QSize(width(), height())));
if (options & QQuickPixmap::Cache)
@@ -1601,7 +1636,7 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &reques
return;
- d = new QQuickPixmapData(this, url, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform);
+ d = new QQuickPixmapData(this, url, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame, frameCount);
if (options & QQuickPixmap::Cache)
d->addToCache();
@@ -1635,9 +1670,9 @@ void QQuickPixmap::clear(QObject *obj)
}
}
-bool QQuickPixmap::isCached(const QUrl &url, const QSize &requestSize, const QQuickImageProviderOptions &options)
+bool QQuickPixmap::isCached(const QUrl &url, const QSize &requestSize, const int frame, const QQuickImageProviderOptions &options)
{
- QQuickPixmapKey key = { &url, &requestSize, options };
+ QQuickPixmapKey key = { &url, &requestSize, frame, options };
QQuickPixmapStore *store = pixmapStore();
return store->m_cache.contains(key);
diff --git a/src/quick/util/qquickpixmapcache_p.h b/src/quick/util/qquickpixmapcache_p.h
index 91fb1ed3bb..ab5d391fa2 100644
--- a/src/quick/util/qquickpixmapcache_p.h
+++ b/src/quick/util/qquickpixmapcache_p.h
@@ -150,6 +150,7 @@ public:
const QSize &implicitSize() const;
const QSize &requestSize() const;
QQuickImageProviderOptions::AutoTransform autoTransform() const;
+ int frameCount() const;
QImage image() const;
void setImage(const QImage &);
void setPixmap(const QQuickPixmap &other);
@@ -164,7 +165,7 @@ public:
void load(QQmlEngine *, const QUrl &, QQuickPixmap::Options options);
void load(QQmlEngine *, const QUrl &, const QSize &);
void load(QQmlEngine *, const QUrl &, const QSize &, QQuickPixmap::Options options);
- void load(QQmlEngine *, const QUrl &, const QSize &, QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions);
+ void load(QQmlEngine *, const QUrl &, const QSize &, QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions, int frame = 0, int frameCount = 1);
void clear();
void clear(QObject *);
@@ -175,7 +176,7 @@ public:
bool connectDownloadProgress(QObject *, int);
static void purgeCache();
- static bool isCached(const QUrl &url, const QSize &requestSize, const QQuickImageProviderOptions &options);
+ static bool isCached(const QUrl &url, const QSize &requestSize, const int frame, const QQuickImageProviderOptions &options);
static const QLatin1String itemGrabberScheme;
diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp
index d739c8a017..1cb30f5a8d 100644
--- a/src/quick/util/qquickpropertychanges.cpp
+++ b/src/quick/util/qquickpropertychanges.cpp
@@ -51,6 +51,7 @@
#include <private/qqmlcontext_p.h>
#include <private/qquickstate_p_p.h>
#include <private/qqmlboundsignal_p.h>
+#include <private/qv4qmlcontext_p.h>
#include <QtCore/qdebug.h>
@@ -201,14 +202,14 @@ public:
QPointer<QObject> object;
QList<const QV4::CompiledData::Binding *> bindings;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
bool decoded : 1;
bool restore : 1;
bool isExplicit : 1;
void decode();
- void decodeBinding(const QString &propertyPrefix, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &qmlUnit, const QV4::CompiledData::Binding *binding);
+ void decodeBinding(const QString &propertyPrefix, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &qmlUnit, const QV4::CompiledData::Binding *binding);
class ExpressionChange {
public:
@@ -236,7 +237,7 @@ public:
QQmlProperty property(const QString &);
};
-void QQuickPropertyChangesParser::verifyList(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
+void QQuickPropertyChangesParser::verifyList(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
{
if (binding->type == QV4::CompiledData::Binding::Type_Object) {
error(compilationUnit->objectAt(binding->value.objectIndex), QQuickPropertyChanges::tr("PropertyChanges does not support creating state-specific objects."));
@@ -266,7 +267,7 @@ void QQuickPropertyChangesPrivate::decode()
decoded = true;
}
-void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
+void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
{
Q_Q(QQuickPropertyChanges);
@@ -314,7 +315,7 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
QQmlBinding::Identifier id = QQmlBinding::Invalid;
if (!binding->isTranslationBinding()) {
- expression = binding->valueAsString(compilationUnit.data());
+ expression = compilationUnit->bindingValueAsString(binding);
id = binding->value.compiledScriptIndex;
}
expressions << ExpressionChange(propertyName, binding, id, expression, url, line, column);
@@ -328,10 +329,10 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
case QV4::CompiledData::Binding::Type_TranslationById:
Q_UNREACHABLE();
case QV4::CompiledData::Binding::Type_String:
- var = binding->valueAsString(compilationUnit.data());
+ var = compilationUnit->bindingValueAsString(binding);
break;
case QV4::CompiledData::Binding::Type_Number:
- var = binding->valueAsNumber(compilationUnit->constants);
+ var = compilationUnit->bindingValueAsNumber(binding);
break;
case QV4::CompiledData::Binding::Type_Boolean:
var = binding->valueAsBoolean();
@@ -346,13 +347,13 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
properties << qMakePair(propertyName, var);
}
-void QQuickPropertyChangesParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props)
+void QQuickPropertyChangesParser::verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props)
{
for (int ii = 0; ii < props.count(); ++ii)
verifyList(compilationUnit, props.at(ii));
}
-void QQuickPropertyChangesParser::applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
+void QQuickPropertyChangesParser::applyBindings(QObject *obj, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
{
QQuickPropertyChangesPrivate *p =
static_cast<QQuickPropertyChangesPrivate *>(QObjectPrivate::get(obj));
@@ -539,9 +540,7 @@ bool QQuickPropertyChanges::containsValue(const QString &name) const
Q_D(const QQuickPropertyChanges);
typedef QPair<QString, QVariant> PropertyEntry;
- QListIterator<PropertyEntry> propertyIterator(d->properties);
- while (propertyIterator.hasNext()) {
- const PropertyEntry &entry = propertyIterator.next();
+ for (const PropertyEntry &entry : d->properties) {
if (entry.first == name) {
return true;
}
@@ -555,9 +554,7 @@ bool QQuickPropertyChanges::containsExpression(const QString &name) const
Q_D(const QQuickPropertyChanges);
typedef QQuickPropertyChangesPrivate::ExpressionChange ExpressionEntry;
- QListIterator<ExpressionEntry> expressionIterator(d->expressions);
- while (expressionIterator.hasNext()) {
- const ExpressionEntry &entry = expressionIterator.next();
+ for (const ExpressionEntry &entry : d->expressions) {
if (entry.name == name) {
return true;
}
@@ -575,13 +572,10 @@ void QQuickPropertyChanges::changeValue(const QString &name, const QVariant &val
{
Q_D(QQuickPropertyChanges);
typedef QPair<QString, QVariant> PropertyEntry;
- typedef QQuickPropertyChangesPrivate::ExpressionChange ExpressionEntry;
- QMutableListIterator<ExpressionEntry> expressionIterator(d->expressions);
- while (expressionIterator.hasNext()) {
- const ExpressionEntry &entry = expressionIterator.next();
- if (entry.name == name) {
- expressionIterator.remove();
+ for (auto it = d->expressions.begin(), end = d->expressions.end(); it != end; ++it) {
+ if (it->name == name) {
+ d->expressions.erase(it);
if (state() && state()->isStateActive()) {
QQmlPropertyPrivate::removeBinding(d->property(name));
d->property(name).write(value);
@@ -592,11 +586,9 @@ void QQuickPropertyChanges::changeValue(const QString &name, const QVariant &val
}
}
- QMutableListIterator<PropertyEntry> propertyIterator(d->properties);
- while (propertyIterator.hasNext()) {
- PropertyEntry &entry = propertyIterator.next();
- if (entry.first == name) {
- entry.second = value;
+ for (auto it = d->properties.begin(), end = d->properties.end(); it != end; ++it) {
+ if (it->first == name) {
+ it->second = value;
if (state() && state()->isStateActive())
d->property(name).write(value);
return;
@@ -611,7 +603,7 @@ void QQuickPropertyChanges::changeValue(const QString &name, const QVariant &val
action.specifiedProperty = name;
action.toValue = value;
- propertyIterator.insert(PropertyEntry(name, value));
+ d->properties.append(PropertyEntry(name, value));
if (state() && state()->isStateActive()) {
state()->addEntryToRevertList(action);
QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(action.property);
@@ -624,26 +616,21 @@ void QQuickPropertyChanges::changeValue(const QString &name, const QVariant &val
void QQuickPropertyChanges::changeExpression(const QString &name, const QString &expression)
{
Q_D(QQuickPropertyChanges);
- typedef QPair<QString, QVariant> PropertyEntry;
typedef QQuickPropertyChangesPrivate::ExpressionChange ExpressionEntry;
bool hadValue = false;
- QMutableListIterator<PropertyEntry> propertyIterator(d->properties);
- while (propertyIterator.hasNext()) {
- PropertyEntry &entry = propertyIterator.next();
- if (entry.first == name) {
- propertyIterator.remove();
+ for (auto it = d->properties.begin(), end = d->properties.end(); it != end; ++it) {
+ if (it->first == name) {
+ d->properties.erase(it);
hadValue = true;
break;
}
}
- QMutableListIterator<ExpressionEntry> expressionIterator(d->expressions);
- while (expressionIterator.hasNext()) {
- ExpressionEntry &entry = expressionIterator.next();
- if (entry.name == name) {
- entry.expression = expression;
+ for (auto it = d->expressions.begin(), end = d->expressions.end(); it != end; ++it) {
+ if (it->name == name) {
+ it->expression = expression;
if (state() && state()->isStateActive()) {
auto prop = d->property(name);
QQmlBinding *newBinding = QQmlBinding::create(
@@ -657,7 +644,7 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString
}
// adding a new expression.
- expressionIterator.insert(ExpressionEntry(name, nullptr, QQmlBinding::Invalid, expression, QUrl(), -1, -1));
+ d->expressions.append(ExpressionEntry(name, nullptr, QQmlBinding::Invalid, expression, QUrl(), -1, -1));
if (state() && state()->isStateActive()) {
if (hadValue) {
@@ -713,17 +700,13 @@ QVariant QQuickPropertyChanges::property(const QString &name) const
typedef QPair<QString, QVariant> PropertyEntry;
typedef QQuickPropertyChangesPrivate::ExpressionChange ExpressionEntry;
- QListIterator<PropertyEntry> propertyIterator(d->properties);
- while (propertyIterator.hasNext()) {
- const PropertyEntry &entry = propertyIterator.next();
+ for (const PropertyEntry &entry : d->properties) {
if (entry.first == name) {
return entry.second;
}
}
- QListIterator<ExpressionEntry> expressionIterator(d->expressions);
- while (expressionIterator.hasNext()) {
- const ExpressionEntry &entry = expressionIterator.next();
+ for (const ExpressionEntry &entry : d->expressions) {
if (entry.name == name) {
return QVariant(entry.expression);
}
@@ -735,24 +718,18 @@ QVariant QQuickPropertyChanges::property(const QString &name) const
void QQuickPropertyChanges::removeProperty(const QString &name)
{
Q_D(QQuickPropertyChanges);
- typedef QPair<QString, QVariant> PropertyEntry;
- typedef QQuickPropertyChangesPrivate::ExpressionChange ExpressionEntry;
- QMutableListIterator<ExpressionEntry> expressionIterator(d->expressions);
- while (expressionIterator.hasNext()) {
- const ExpressionEntry &entry = expressionIterator.next();
- if (entry.name == name) {
- expressionIterator.remove();
+ for (auto it = d->expressions.begin(), end = d->expressions.end(); it != end; ++it) {
+ if (it->name == name) {
+ d->expressions.erase(it);
state()->removeEntryFromRevertList(object(), name);
return;
}
}
- QMutableListIterator<PropertyEntry> propertyIterator(d->properties);
- while (propertyIterator.hasNext()) {
- const PropertyEntry &entry = propertyIterator.next();
- if (entry.first == name) {
- propertyIterator.remove();
+ for (auto it = d->properties.begin(), end = d->properties.end(); it != end; ++it) {
+ if (it->first == name) {
+ d->properties.erase(it);
state()->removeEntryFromRevertList(object(), name);
return;
}
@@ -764,9 +741,7 @@ QVariant QQuickPropertyChanges::value(const QString &name) const
Q_D(const QQuickPropertyChanges);
typedef QPair<QString, QVariant> PropertyEntry;
- QListIterator<PropertyEntry> propertyIterator(d->properties);
- while (propertyIterator.hasNext()) {
- const PropertyEntry &entry = propertyIterator.next();
+ for (const PropertyEntry &entry : d->properties) {
if (entry.first == name) {
return entry.second;
}
@@ -780,9 +755,7 @@ QString QQuickPropertyChanges::expression(const QString &name) const
Q_D(const QQuickPropertyChanges);
typedef QQuickPropertyChangesPrivate::ExpressionChange ExpressionEntry;
- QListIterator<ExpressionEntry> expressionIterator(d->expressions);
- while (expressionIterator.hasNext()) {
- const ExpressionEntry &entry = expressionIterator.next();
+ for (const ExpressionEntry &entry : d->expressions) {
if (entry.name == name) {
return entry.expression;
}
diff --git a/src/quick/util/qquickpropertychanges_p.h b/src/quick/util/qquickpropertychanges_p.h
index 74fe511d6e..82a6ebffac 100644
--- a/src/quick/util/qquickpropertychanges_p.h
+++ b/src/quick/util/qquickpropertychanges_p.h
@@ -101,10 +101,10 @@ public:
QQuickPropertyChangesParser()
: QQmlCustomParser(AcceptsAttachedProperties) {}
- void verifyList(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding);
+ void verifyList(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding);
- void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) override;
- void applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
+ void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) override;
+ void applyBindings(QObject *obj, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
};
diff --git a/src/quick/util/qquickshortcut.cpp b/src/quick/util/qquickshortcut.cpp
index 730a14369e..5d227b4613 100644
--- a/src/quick/util/qquickshortcut.cpp
+++ b/src/quick/util/qquickshortcut.cpp
@@ -216,7 +216,7 @@ void QQuickShortcut::setSequences(const QVariantList &values)
bool changed = !remainder.isEmpty();
for (int i = 0; i < values.count(); ++i) {
- QVariant value = values.at(i);
+ const QVariant &value = values.at(i);
Shortcut& shortcut = m_shortcuts[i];
if (value == shortcut.userValue)
continue;
diff --git a/src/quick/util/qquickshortcut_p.h b/src/quick/util/qquickshortcut_p.h
index c5d5501cb7..712cca7696 100644
--- a/src/quick/util/qquickshortcut_p.h
+++ b/src/quick/util/qquickshortcut_p.h
@@ -67,8 +67,8 @@ class QQuickShortcut : public QObject, public QQmlParserStatus
Q_INTERFACES(QQmlParserStatus)
Q_PROPERTY(QVariant sequence READ sequence WRITE setSequence NOTIFY sequenceChanged FINAL)
Q_PROPERTY(QVariantList sequences READ sequences WRITE setSequences NOTIFY sequencesChanged FINAL REVISION 9)
- Q_PROPERTY(QString nativeText READ nativeText NOTIFY sequenceChanged FINAL REVISION 1)
- Q_PROPERTY(QString portableText READ portableText NOTIFY sequenceChanged FINAL REVISION 1)
+ Q_PROPERTY(QString nativeText READ nativeText NOTIFY sequenceChanged FINAL REVISION 6)
+ Q_PROPERTY(QString portableText READ portableText NOTIFY sequenceChanged FINAL REVISION 6)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged FINAL)
Q_PROPERTY(bool autoRepeat READ autoRepeat WRITE setAutoRepeat NOTIFY autoRepeatChanged FINAL)
Q_PROPERTY(Qt::ShortcutContext context READ context WRITE setContext NOTIFY contextChanged FINAL)
diff --git a/src/quick/util/qquickstate.cpp b/src/quick/util/qquickstate.cpp
index 3ca6440784..c106528f45 100644
--- a/src/quick/util/qquickstate.cpp
+++ b/src/quick/util/qquickstate.cpp
@@ -192,7 +192,7 @@ bool QQuickState::isNamed() const
bool QQuickState::isWhenKnown() const
{
Q_D(const QQuickState);
- return d->when != nullptr;
+ return d->whenKnown;
}
/*!
@@ -219,15 +219,16 @@ bool QQuickState::isWhenKnown() const
}
\endqml
*/
-QQmlBinding *QQuickState::when() const
+bool QQuickState::when() const
{
Q_D(const QQuickState);
- return d->when.data();
+ return d->when;
}
-void QQuickState::setWhen(QQmlBinding *when)
+void QQuickState::setWhen(bool when)
{
Q_D(QQuickState);
+ d->whenKnown = true;
d->when = when;
if (d->group)
d->group->updateAutoState();
@@ -371,10 +372,7 @@ bool QQuickState::containsPropertyInRevertList(QObject *target, const QString &n
Q_D(const QQuickState);
if (isStateActive()) {
- QListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
-
- while (revertListIterator.hasNext()) {
- const QQuickSimpleAction &simpleAction = revertListIterator.next();
+ for (const QQuickSimpleAction &simpleAction : d->revertList) {
if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
return true;
}
@@ -388,10 +386,7 @@ bool QQuickState::changeValueInRevertList(QObject *target, const QString &name,
Q_D(QQuickState);
if (isStateActive()) {
- QMutableListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
-
- while (revertListIterator.hasNext()) {
- QQuickSimpleAction &simpleAction = revertListIterator.next();
+ for (QQuickSimpleAction &simpleAction : d->revertList) {
if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) {
simpleAction.setValue(revertValue);
return true;
@@ -407,10 +402,7 @@ bool QQuickState::changeBindingInRevertList(QObject *target, const QString &name
Q_D(QQuickState);
if (isStateActive()) {
- QMutableListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
-
- while (revertListIterator.hasNext()) {
- QQuickSimpleAction &simpleAction = revertListIterator.next();
+ for (QQuickSimpleAction &simpleAction : d->revertList) {
if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) {
simpleAction.setBinding(binding);
return true;
@@ -426,10 +418,8 @@ bool QQuickState::removeEntryFromRevertList(QObject *target, const QString &name
Q_D(QQuickState);
if (isStateActive()) {
- QMutableListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
-
- while (revertListIterator.hasNext()) {
- QQuickSimpleAction &simpleAction = revertListIterator.next();
+ for (auto it = d->revertList.begin(), end = d->revertList.end(); it != end; ++it) {
+ QQuickSimpleAction &simpleAction = *it;
if (simpleAction.property().object() == target && simpleAction.property().name() == name) {
QQmlPropertyPrivate::removeBinding(simpleAction.property());
@@ -437,7 +427,7 @@ bool QQuickState::removeEntryFromRevertList(QObject *target, const QString &name
if (simpleAction.binding())
QQmlPropertyPrivate::setBinding(simpleAction.binding());
- revertListIterator.remove();
+ d->revertList.erase(it);
return true;
}
}
@@ -460,10 +450,7 @@ void QQuickState::removeAllEntriesFromRevertList(QObject *target)
Q_D(QQuickState);
if (isStateActive()) {
- QMutableListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
-
- while (revertListIterator.hasNext()) {
- QQuickSimpleAction &simpleAction = revertListIterator.next();
+ const auto actionMatchesTarget = [target](QQuickSimpleAction &simpleAction) {
if (simpleAction.property().object() == target) {
QQmlPropertyPrivate::removeBinding(simpleAction.property());
@@ -471,9 +458,14 @@ void QQuickState::removeAllEntriesFromRevertList(QObject *target)
if (simpleAction.binding())
QQmlPropertyPrivate::setBinding(simpleAction.binding());
- revertListIterator.remove();
+ return true;
}
- }
+ return false;
+ };
+
+ d->revertList.erase(std::remove_if(d->revertList.begin(), d->revertList.end(),
+ actionMatchesTarget),
+ d->revertList.end());
}
}
@@ -484,9 +476,7 @@ void QQuickState::addEntriesToRevertList(const QList<QQuickStateAction> &actionL
QList<QQuickSimpleAction> simpleActionList;
simpleActionList.reserve(actionList.count());
- QListIterator<QQuickStateAction> actionListIterator(actionList);
- while(actionListIterator.hasNext()) {
- const QQuickStateAction &action = actionListIterator.next();
+ for (const QQuickStateAction &action : actionList) {
QQuickSimpleAction simpleAction(action);
action.property.write(action.toValue);
if (action.toBinding)
@@ -504,10 +494,7 @@ QVariant QQuickState::valueInRevertList(QObject *target, const QString &name) co
Q_D(const QQuickState);
if (isStateActive()) {
- QListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
-
- while (revertListIterator.hasNext()) {
- const QQuickSimpleAction &simpleAction = revertListIterator.next();
+ for (const QQuickSimpleAction &simpleAction : d->revertList) {
if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
return simpleAction.value();
}
@@ -521,10 +508,7 @@ QQmlAbstractBinding *QQuickState::bindingInRevertList(QObject *target, const QSt
Q_D(const QQuickState);
if (isStateActive()) {
- QListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
-
- while (revertListIterator.hasNext()) {
- const QQuickSimpleAction &simpleAction = revertListIterator.next();
+ for (const QQuickSimpleAction &simpleAction : d->revertList) {
if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
return simpleAction.binding();
}
diff --git a/src/quick/util/qquickstate_p.h b/src/quick/util/qquickstate_p.h
index 79874ee78e..576ba9834c 100644
--- a/src/quick/util/qquickstate_p.h
+++ b/src/quick/util/qquickstate_p.h
@@ -152,7 +152,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickState : public QObject
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName)
- Q_PROPERTY(QQmlBinding *when READ when WRITE setWhen)
+ Q_PROPERTY(bool when READ when WRITE setWhen)
Q_PROPERTY(QString extend READ extends WRITE setExtends)
Q_PROPERTY(QQmlListProperty<QQuickStateOperation> changes READ changes)
Q_CLASSINFO("DefaultProperty", "changes")
@@ -166,11 +166,9 @@ public:
void setName(const QString &);
bool isNamed() const;
- /*'when' is a QQmlBinding to limit state changes oscillation
- due to the unpredictable order of evaluation of bound expressions*/
bool isWhenKnown() const;
- QQmlBinding *when() const;
- void setWhen(QQmlBinding *);
+ bool when() const;
+ void setWhen(bool);
QString extends() const;
void setExtends(const QString &);
diff --git a/src/quick/util/qquickstate_p_p.h b/src/quick/util/qquickstate_p_p.h
index 61472b4d06..2fa5321165 100644
--- a/src/quick/util/qquickstate_p_p.h
+++ b/src/quick/util/qquickstate_p_p.h
@@ -203,12 +203,13 @@ class QQuickStatePrivate : public QObjectPrivate
public:
QQuickStatePrivate()
- : named(false), inState(false), group(nullptr) {}
+ : when(false), whenKnown(false), named(false), inState(false), group(nullptr) {}
typedef QList<QQuickSimpleAction> SimpleActionList;
QString name;
- QQmlBinding::Ptr when;
+ bool when;
+ bool whenKnown;
bool named;
struct OperationGuard : public QQmlGuard<QQuickStateOperation>
@@ -231,9 +232,8 @@ public:
}
static void operations_clear(QQmlListProperty<QQuickStateOperation> *prop) {
QList<OperationGuard> *list = static_cast<QList<OperationGuard> *>(prop->data);
- QMutableListIterator<OperationGuard> listIterator(*list);
- while(listIterator.hasNext())
- listIterator.next()->setState(nullptr);
+ for (auto &e : *list)
+ e->setState(nullptr);
list->clear();
}
static int operations_count(QQmlListProperty<QQuickStateOperation> *prop) {
diff --git a/src/quick/util/qquickstategroup.cpp b/src/quick/util/qquickstategroup.cpp
index b53949d21c..46e7d62fc1 100644
--- a/src/quick/util/qquickstategroup.cpp
+++ b/src/quick/util/qquickstategroup.cpp
@@ -311,7 +311,7 @@ void QQuickStateGroup::componentComplete()
if (!state->isNamed())
state->setName(QLatin1String("anonymousState") + QString::number(++d->unnamedCount));
- const QString stateName = state->name();
+ QString stateName = state->name();
if (names.contains(stateName)) {
qmlWarning(state->parent()) << "Found duplicate state name: " << stateName;
} else {
@@ -348,10 +348,9 @@ bool QQuickStateGroupPrivate::updateAutoState()
QQuickState *state = states.at(ii);
if (state->isWhenKnown()) {
if (state->isNamed()) {
- if (state->when() && state->when()->evaluate().toBool()) {
+ if (state->when()) {
if (stateChangeDebug())
- qWarning() << "Setting auto state due to:"
- << state->when()->expression();
+ qWarning() << "Setting auto state due to expression";
if (currentState != state->name()) {
q->setState(state->name());
return true;
diff --git a/src/quick/util/qquickutilmodule.cpp b/src/quick/util/qquickutilmodule.cpp
index 5147ebc6f6..93b6599506 100644
--- a/src/quick/util/qquickutilmodule.cpp
+++ b/src/quick/util/qquickutilmodule.cpp
@@ -99,10 +99,13 @@ void QQuickUtilModule::defineModule()
qmlRegisterType<QQuickVector3dAnimation>("QtQuick",2,0,"Vector3dAnimation");
#if QT_CONFIG(validator)
- qmlRegisterType<QValidator>();
+ qmlRegisterAnonymousType<QValidator>("QtQuick", 2);
qmlRegisterType<QQuickIntValidator>("QtQuick",2,0,"IntValidator");
qmlRegisterType<QQuickDoubleValidator>("QtQuick",2,0,"DoubleValidator");
qmlRegisterType<QRegExpValidator>("QtQuick",2,0,"RegExpValidator");
+#if QT_CONFIG(regularexpression)
+ qmlRegisterType<QRegularExpressionValidator>("QtQuick", 2, 14, "RegularExpressionValidator");
+#endif
#endif
qmlRegisterUncreatableType<QQuickAnimator>("QtQuick", 2, 2, "Animator", QQuickAbstractAnimation::tr("Animator is an abstract class"));
@@ -114,7 +117,7 @@ void QQuickUtilModule::defineModule()
#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl)
qmlRegisterType<QQuickUniformAnimator>("QtQuick", 2, 2, "UniformAnimator");
#endif
- qmlRegisterType<QQuickStateOperation>();
+ qmlRegisterAnonymousType<QQuickStateOperation>("QtQuick", 2);
qmlRegisterCustomType<QQuickPropertyChanges>("QtQuick",2,0,"PropertyChanges", new QQuickPropertyChangesParser);
@@ -128,7 +131,7 @@ void QQuickUtilModule::defineModule()
#if QT_CONFIG(shortcut)
qmlRegisterType<QQuickShortcut>("QtQuick", 2, 5, "Shortcut");
- qmlRegisterType<QQuickShortcut,1>("QtQuick", 2, 6, "Shortcut");
+ qmlRegisterType<QQuickShortcut,6>("QtQuick", 2, 6, "Shortcut");
qmlRegisterType<QQuickShortcut,9>("QtQuick", 2, 9, "Shortcut");
#endif
diff --git a/src/quick/util/qquickvalidator.cpp b/src/quick/util/qquickvalidator.cpp
index b2b773cd94..4709b3dda3 100644
--- a/src/quick/util/qquickvalidator.cpp
+++ b/src/quick/util/qquickvalidator.cpp
@@ -206,9 +206,13 @@ void QQuickDoubleValidator::resetLocaleName()
\inqmlmodule QtQuick
\ingroup qtquick-text-utility
\brief Provides a string validator.
+ \deprecated
The RegExpValidator type provides a validator, which counts as valid any string which
matches a specified regular expression.
+
+ RegExpValidator is deprecated since it is based on the deprecated \l {QRegExp}. Use
+ \l RegularExpressionValidator instead.
*/
/*!
\qmlproperty regExp QtQuick::RegExpValidator::regExp
@@ -239,6 +243,48 @@ void QQuickDoubleValidator::resetLocaleName()
\endcode
\endlist
*/
+/*!
+ \qmltype RegularExpressionValidator
+ \instantiates QRegularExpressionValidator
+ \inqmlmodule QtQuick
+ \ingroup qtquick-text-utility
+ \brief Provides a string validator.
+ \since 5.14
+
+ The RegularExpressionValidator type provides a validator, that counts as valid any string which
+ matches a specified regular expression.
+*/
+/*!
+ \qmlproperty regularExpression QtQuick::RegularExpressionValidator::regularExpression
+
+ This property holds the regular expression used for validation.
+
+ Note that this property should be a regular expression in JS syntax, e.g /a/ for the regular
+ expression matching "a".
+
+ By default, this property contains a regular expression with the pattern \c{.*} that matches any
+ string.
+
+ Below you can find an example of a \l TextInput object with a RegularExpressionValidator
+ specified:
+
+ \snippet qml/regularexpression.qml 0
+
+ Some more examples of regular expressions:
+
+ \list
+ \li A list of numbers with one to three positions separated by a comma:
+ \badcode
+ /\d{1,3}(?:,\d{1,3})+$/
+ \endcode
+
+ \li An amount consisting of up to 3 numbers before the decimal point, and
+ 1 to 2 after the decimal point:
+ \badcode
+ /(\d{1,3})([.,]\d{1,2})?$/
+ \endcode
+ \endlist
+*/
#endif // validator
diff --git a/src/quick/util/qquickvalidator_p.h b/src/quick/util/qquickvalidator_p.h
index 812e552d8e..9212efa044 100644
--- a/src/quick/util/qquickvalidator_p.h
+++ b/src/quick/util/qquickvalidator_p.h
@@ -95,6 +95,9 @@ QML_DECLARE_TYPE(QValidator)
QML_DECLARE_TYPE(QQuickIntValidator)
QML_DECLARE_TYPE(QQuickDoubleValidator)
QML_DECLARE_TYPE(QRegExpValidator)
+#if QT_CONFIG(regularexpression)
+QML_DECLARE_TYPE(QRegularExpressionValidator)
+#endif
#endif
#endif // QQUICKVALIDATOR_P_H
diff --git a/src/quick/util/qquickvaluetypes.cpp b/src/quick/util/qquickvaluetypes.cpp
index e4a03f3b52..0af29aed1c 100644
--- a/src/quick/util/qquickvaluetypes.cpp
+++ b/src/quick/util/qquickvaluetypes.cpp
@@ -109,6 +109,11 @@ qreal QQuickColorValueType::hslLightness() const
return v.lightnessF();
}
+bool QQuickColorValueType::isValid() const
+{
+ return v.isValid();
+}
+
void QQuickColorValueType::setR(qreal r)
{
v.setRedF(r);
diff --git a/src/quick/util/qquickvaluetypes_p.h b/src/quick/util/qquickvaluetypes_p.h
index 5a9af970e8..4305006f91 100644
--- a/src/quick/util/qquickvaluetypes_p.h
+++ b/src/quick/util/qquickvaluetypes_p.h
@@ -84,6 +84,7 @@ class QQuickColorValueType
Q_PROPERTY(qreal hslHue READ hslHue WRITE setHslHue FINAL)
Q_PROPERTY(qreal hslSaturation READ hslSaturation WRITE setHslSaturation FINAL)
Q_PROPERTY(qreal hslLightness READ hslLightness WRITE setHslLightness FINAL)
+ Q_PROPERTY(bool valid READ isValid)
Q_GADGET
public:
Q_INVOKABLE QString toString() const;
@@ -98,6 +99,7 @@ public:
qreal hslHue() const;
qreal hslSaturation() const;
qreal hslLightness() const;
+ bool isValid() const;
void setR(qreal);
void setG(qreal);
void setB(qreal);
diff --git a/src/quick/util/util.pri b/src/quick/util/util.pri
index c51f082d03..63d995e34c 100644
--- a/src/quick/util/util.pri
+++ b/src/quick/util/util.pri
@@ -15,6 +15,7 @@ SOURCES += \
$$PWD/qquicktimeline.cpp \
$$PWD/qquickpixmapcache.cpp \
$$PWD/qquickbehavior.cpp \
+ $$PWD/qquickboundaryrule.cpp \
$$PWD/qquickfontloader.cpp \
$$PWD/qquickstyledtext.cpp \
$$PWD/qquickimageprovider.cpp \
@@ -50,6 +51,7 @@ HEADERS += \
$$PWD/qquicktimeline_p_p.h \
$$PWD/qquickpixmapcache_p.h \
$$PWD/qquickbehavior_p.h \
+ $$PWD/qquickboundaryrule_p.h \
$$PWD/qquickfontloader_p.h \
$$PWD/qquickstyledtext_p.h \
$$PWD/qquickimageprovider.h \
diff --git a/src/quickshapes/qquickshape.cpp b/src/quickshapes/qquickshape.cpp
index 47b9ae397e..a7db65f16f 100644
--- a/src/quickshapes/qquickshape.cpp
+++ b/src/quickshapes/qquickshape.cpp
@@ -42,11 +42,12 @@
#include "qquickshapegenericrenderer_p.h"
#include "qquickshapenvprrenderer_p.h"
#include "qquickshapesoftwarerenderer_p.h"
-#include <private/qsgtexture_p.h>
+#include <private/qsgplaintexture_p.h>
#include <private/qquicksvgparser_p.h>
#include <QtGui/private/qdrawhelper_p.h>
#include <QOpenGLFunctions>
#include <QLoggingCategory>
+#include <QtGui/private/qrhi_p.h>
#if defined(QT_STATIC)
static void initResources()
@@ -582,12 +583,14 @@ void QQuickShapePath::resetFillGradient()
\list
- \li When running with the default, OpenGL backend of Qt Quick, both the
- generic, triangulation-based and the NVIDIA-specific
- \c{GL_NV_path_rendering} methods are available. By default only the generic
- approach is used. Setting Shape.vendorExtensionsEnabled property to \c true
- leads to using NV_path_rendering on NVIDIA systems, and the generic method
- on others.
+ \li When running with the OpenGL backend of Qt Quick, both the generic,
+ triangulation-based and the NVIDIA-specific \c{GL_NV_path_rendering}
+ methods are available. By default only the generic approach is used.
+ Setting Shape.vendorExtensionsEnabled property to \c true leads to using
+ NV_path_rendering on NVIDIA systems when running directly on OpenGL, and
+ the generic method on others. When OpenGL is not used directly by the scene
+ graph, for example because it is using the graphics abstraction layer
+ (QRhi), only the generic shape renderer is available.
\li The \c software backend is fully supported. The path is rendered via
QPainter::strokePath() and QPainter::fillPath() in this case.
@@ -966,6 +969,11 @@ void QQuickShape::itemChange(ItemChange change, const ItemChangeData &data)
// sync may have been deferred; do it now if the item became visible
if (change == ItemVisibleHasChanged && data.boolValue)
d->_q_shapePathChanged();
+ else if (change == QQuickItem::ItemSceneChange) {
+ for (int i = 0; i < d->sp.count(); ++i)
+ QQuickShapePathPrivate::get(d->sp[i])->dirty = QQuickShapePathPrivate::DirtyAll;
+ d->_q_shapePathChanged();
+ }
QQuickItem::itemChange(change, data);
}
@@ -1009,7 +1017,12 @@ void QQuickShapePrivate::createRenderer()
renderer = new QQuickShapeSoftwareRenderer;
break;
default:
- qWarning("No path backend for this graphics API yet");
+ if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
+ rendererType = QQuickShape::GeometryRenderer;
+ renderer = new QQuickShapeGenericRenderer(q);
+ } else {
+ qWarning("No path backend for this graphics API yet");
+ }
break;
}
}
@@ -1045,7 +1058,13 @@ QSGNode *QQuickShapePrivate::createNode()
static_cast<QQuickShapeSoftwareRenderNode *>(node));
break;
default:
- qWarning("No path backend for this graphics API yet");
+ if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
+ node = new QQuickShapeGenericNode;
+ static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode(
+ static_cast<QQuickShapeGenericNode *>(node));
+ } else {
+ qWarning("No path backend for this graphics API yet");
+ }
break;
}
@@ -1499,45 +1518,7 @@ void QQuickShapeConicalGradient::setAngle(qreal v)
}
}
-#if QT_CONFIG(opengl)
-
-// contexts sharing with each other get the same cache instance
-class QQuickShapeGradientCacheWrapper
-{
-public:
- QQuickShapeGradientCache *get(QOpenGLContext *context)
- {
- return m_resource.value<QQuickShapeGradientCache>(context);
- }
-
-private:
- QOpenGLMultiGroupSharedResource m_resource;
-};
-
-QQuickShapeGradientCache *QQuickShapeGradientCache::currentCache()
-{
- static QQuickShapeGradientCacheWrapper qt_path_gradient_caches;
- return qt_path_gradient_caches.get(QOpenGLContext::currentContext());
-}
-
-// let QOpenGLContext manage the lifetime of the cached textures
-QQuickShapeGradientCache::~QQuickShapeGradientCache()
-{
- m_cache.clear();
-}
-
-void QQuickShapeGradientCache::invalidateResource()
-{
- m_cache.clear();
-}
-
-void QQuickShapeGradientCache::freeResource(QOpenGLContext *)
-{
- qDeleteAll(m_cache);
- m_cache.clear();
-}
-
-static void generateGradientColorTable(const QQuickShapeGradientCache::Key &gradient,
+static void generateGradientColorTable(const QQuickShapeGradientCacheKey &gradient,
uint *colorTable, int size, float opacity)
{
int pos = 0;
@@ -1588,7 +1569,98 @@ static void generateGradientColorTable(const QQuickShapeGradientCache::Key &grad
colorTable[size-1] = last_color;
}
-QSGTexture *QQuickShapeGradientCache::get(const Key &grad)
+QQuickShapeGradientCache::~QQuickShapeGradientCache()
+{
+ qDeleteAll(m_textures);
+}
+
+QQuickShapeGradientCache *QQuickShapeGradientCache::cacheForRhi(QRhi *rhi)
+{
+ static QHash<QRhi *, QQuickShapeGradientCache *> caches;
+ auto it = caches.constFind(rhi);
+ if (it != caches.constEnd())
+ return *it;
+
+ QQuickShapeGradientCache *cache = new QQuickShapeGradientCache;
+ rhi->addCleanupCallback([cache](QRhi *rhi) {
+ caches.remove(rhi);
+ delete cache;
+ });
+ caches.insert(rhi, cache);
+ return cache;
+}
+
+QSGTexture *QQuickShapeGradientCache::get(const QQuickShapeGradientCacheKey &grad)
+{
+ QSGPlainTexture *tx = m_textures[grad];
+ if (!tx) {
+ static const int W = 1024; // texture size is 1024x1
+ QImage gradTab(W, 1, QImage::Format_RGBA8888_Premultiplied);
+ generateGradientColorTable(grad, reinterpret_cast<uint *>(gradTab.bits()), W, 1.0f);
+ tx = new QSGPlainTexture;
+ tx->setImage(gradTab);
+ switch (grad.spread) {
+ case QQuickShapeGradient::PadSpread:
+ tx->setHorizontalWrapMode(QSGTexture::ClampToEdge);
+ tx->setVerticalWrapMode(QSGTexture::ClampToEdge);
+ break;
+ case QQuickShapeGradient::RepeatSpread:
+ tx->setHorizontalWrapMode(QSGTexture::Repeat);
+ tx->setVerticalWrapMode(QSGTexture::Repeat);
+ break;
+ case QQuickShapeGradient::ReflectSpread:
+ tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat);
+ tx->setVerticalWrapMode(QSGTexture::MirroredRepeat);
+ break;
+ default:
+ qWarning("Unknown gradient spread mode %d", grad.spread);
+ break;
+ }
+ tx->setFiltering(QSGTexture::Linear);
+ m_textures[grad] = tx;
+ }
+ return tx;
+}
+
+#if QT_CONFIG(opengl)
+
+// contexts sharing with each other get the same cache instance
+class QQuickShapeGradientCacheWrapper
+{
+public:
+ QQuickShapeGradientOpenGLCache *get(QOpenGLContext *context)
+ {
+ return m_resource.value<QQuickShapeGradientOpenGLCache>(context);
+ }
+
+private:
+ QOpenGLMultiGroupSharedResource m_resource;
+};
+
+QQuickShapeGradientOpenGLCache *QQuickShapeGradientOpenGLCache::currentCache()
+{
+ static QQuickShapeGradientCacheWrapper qt_path_gradient_caches;
+ return qt_path_gradient_caches.get(QOpenGLContext::currentContext());
+}
+
+// let QOpenGLContext manage the lifetime of the cached textures
+QQuickShapeGradientOpenGLCache::~QQuickShapeGradientOpenGLCache()
+{
+ m_cache.clear();
+}
+
+void QQuickShapeGradientOpenGLCache::invalidateResource()
+{
+ m_cache.clear();
+}
+
+void QQuickShapeGradientOpenGLCache::freeResource(QOpenGLContext *)
+{
+ qDeleteAll(m_cache);
+ m_cache.clear();
+}
+
+QSGTexture *QQuickShapeGradientOpenGLCache::get(const QQuickShapeGradientCacheKey &grad)
{
QSGPlainTexture *tx = m_cache[grad];
if (!tx) {
diff --git a/src/quickshapes/qquickshape_p.h b/src/quickshapes/qquickshape_p.h
index cd242cafc3..7066ea0709 100644
--- a/src/quickshapes/qquickshape_p.h
+++ b/src/quickshapes/qquickshape_p.h
@@ -213,6 +213,7 @@ class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapePath : public QQuickPath
Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged)
Q_PROPERTY(QVector<qreal> dashPattern READ dashPattern WRITE setDashPattern NOTIFY dashPatternChanged)
Q_PROPERTY(QQuickShapeGradient *fillGradient READ fillGradient WRITE setFillGradient RESET resetFillGradient)
+ Q_PROPERTY(QSizeF scale READ scale WRITE setScale NOTIFY scaleChanged REVISION 14)
public:
enum FillRule {
diff --git a/src/quickshapes/qquickshape_p_p.h b/src/quickshapes/qquickshape_p_p.h
index bc06c8fde8..bfeb0d2f9f 100644
--- a/src/quickshapes/qquickshape_p_p.h
+++ b/src/quickshapes/qquickshape_p_p.h
@@ -63,6 +63,7 @@
QT_BEGIN_NAMESPACE
class QSGPlainTexture;
+class QRhi;
class QQuickAbstractPathRenderer
{
@@ -186,45 +187,57 @@ public:
bool syncTimingActive = false;
};
-#if QT_CONFIG(opengl)
+struct QQuickShapeGradientCacheKey
+{
+ QQuickShapeGradientCacheKey(const QGradientStops &stops, QQuickShapeGradient::SpreadMode spread)
+ : stops(stops), spread(spread)
+ { }
+ QGradientStops stops;
+ QQuickShapeGradient::SpreadMode spread;
+ bool operator==(const QQuickShapeGradientCacheKey &other) const
+ {
+ return spread == other.spread && stops == other.stops;
+ }
+};
-class QQuickShapeGradientCache : public QOpenGLSharedResource
+inline uint qHash(const QQuickShapeGradientCacheKey &v, uint seed = 0)
{
-public:
- struct Key {
- Key(const QGradientStops &stops, QQuickShapeGradient::SpreadMode spread)
- : stops(stops), spread(spread)
- { }
- QGradientStops stops;
- QQuickShapeGradient::SpreadMode spread;
- bool operator==(const Key &other) const
- {
- return spread == other.spread && stops == other.stops;
- }
- };
+ uint h = seed + v.spread;
+ for (int i = 0; i < 3 && i < v.stops.count(); ++i)
+ h += v.stops[i].second.rgba();
+ return h;
+}
- QQuickShapeGradientCache(QOpenGLContext *context) : QOpenGLSharedResource(context->shareGroup()) { }
+class QQuickShapeGradientCache
+{
+public:
~QQuickShapeGradientCache();
+ static QQuickShapeGradientCache *cacheForRhi(QRhi *rhi);
+ QSGTexture *get(const QQuickShapeGradientCacheKey &grad);
+
+private:
+ QHash<QQuickShapeGradientCacheKey, QSGPlainTexture *> m_textures;
+};
+
+#if QT_CONFIG(opengl)
+
+class QQuickShapeGradientOpenGLCache : public QOpenGLSharedResource
+{
+public:
+ QQuickShapeGradientOpenGLCache(QOpenGLContext *context) : QOpenGLSharedResource(context->shareGroup()) { }
+ ~QQuickShapeGradientOpenGLCache();
void invalidateResource() override;
void freeResource(QOpenGLContext *) override;
- QSGTexture *get(const Key &grad);
+ QSGTexture *get(const QQuickShapeGradientCacheKey &grad);
- static QQuickShapeGradientCache *currentCache();
+ static QQuickShapeGradientOpenGLCache *currentCache();
private:
- QHash<Key, QSGPlainTexture *> m_cache;
+ QHash<QQuickShapeGradientCacheKey, QSGPlainTexture *> m_cache;
};
-inline uint qHash(const QQuickShapeGradientCache::Key &v, uint seed = 0)
-{
- uint h = seed + v.spread;
- for (int i = 0; i < 3 && i < v.stops.count(); ++i)
- h += v.stops[i].second.rgba();
- return h;
-}
-
#endif // QT_CONFIG(opengl)
QT_END_NAMESPACE
diff --git a/src/quickshapes/qquickshapegenericrenderer.cpp b/src/quickshapes/qquickshapegenericrenderer.cpp
index 604da2a889..06cc442fc7 100644
--- a/src/quickshapes/qquickshapegenericrenderer.cpp
+++ b/src/quickshapes/qquickshapegenericrenderer.cpp
@@ -40,13 +40,13 @@
#include "qquickshapegenericrenderer_p.h"
#include <QtGui/private/qtriangulator_p.h>
#include <QtGui/private/qtriangulatingstroker_p.h>
+#include <QSGVertexColorMaterial>
#if QT_CONFIG(thread)
#include <QThreadPool>
#endif
#if QT_CONFIG(opengl)
-#include <QSGVertexColorMaterial>
#include <QOpenGLContext>
#include <QOffscreenSurface>
#include <QtGui/private/qopenglextensions_p.h>
@@ -361,6 +361,9 @@ void QQuickShapeGenericRenderer::endSync(bool async)
});
didKickOffAsync = true;
#if QT_CONFIG(thread)
+ // qtVectorPathForPath() initializes a unique_ptr without locking.
+ // Do that before starting the threads as otherwise we get a race condition.
+ qtVectorPathForPath(r->path);
pathWorkThreadPool->start(r);
#endif
} else {
@@ -391,6 +394,9 @@ void QQuickShapeGenericRenderer::endSync(bool async)
});
didKickOffAsync = true;
#if QT_CONFIG(thread)
+ // qtVectorPathForPath() initializes a unique_ptr without locking.
+ // Do that before starting the threads as otherwise we get a race condition.
+ qtVectorPathForPath(r->path);
pathWorkThreadPool->start(r);
#endif
} else {
@@ -695,10 +701,8 @@ QSGMaterial *QQuickShapeGenericMaterialFactory::createVertexColor(QQuickWindow *
{
QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
-#if QT_CONFIG(opengl)
- if (api == QSGRendererInterface::OpenGL)
+ if (api == QSGRendererInterface::OpenGL || QSGRendererInterface::isApiRhiBased(api))
return new QSGVertexColorMaterial;
-#endif
qWarning("Vertex-color material: Unsupported graphics API %d", api);
return nullptr;
@@ -709,12 +713,8 @@ QSGMaterial *QQuickShapeGenericMaterialFactory::createLinearGradient(QQuickWindo
{
QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
-#if QT_CONFIG(opengl)
- if (api == QSGRendererInterface::OpenGL)
+ if (api == QSGRendererInterface::OpenGL || QSGRendererInterface::isApiRhiBased(api))
return new QQuickShapeLinearGradientMaterial(node);
-#else
- Q_UNUSED(node);
-#endif
qWarning("Linear gradient material: Unsupported graphics API %d", api);
return nullptr;
@@ -725,12 +725,8 @@ QSGMaterial *QQuickShapeGenericMaterialFactory::createRadialGradient(QQuickWindo
{
QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
-#if QT_CONFIG(opengl)
- if (api == QSGRendererInterface::OpenGL)
+ if (api == QSGRendererInterface::OpenGL || QSGRendererInterface::isApiRhiBased(api))
return new QQuickShapeRadialGradientMaterial(node);
-#else
- Q_UNUSED(node);
-#endif
qWarning("Radial gradient material: Unsupported graphics API %d", api);
return nullptr;
@@ -741,12 +737,8 @@ QSGMaterial *QQuickShapeGenericMaterialFactory::createConicalGradient(QQuickWind
{
QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
-#if QT_CONFIG(opengl)
- if (api == QSGRendererInterface::OpenGL)
+ if (api == QSGRendererInterface::OpenGL || QSGRendererInterface::isApiRhiBased(api))
return new QQuickShapeConicalGradientMaterial(node);
-#else
- Q_UNUSED(node);
-#endif
qWarning("Conical gradient material: Unsupported graphics API %d", api);
return nullptr;
@@ -754,8 +746,6 @@ QSGMaterial *QQuickShapeGenericMaterialFactory::createConicalGradient(QQuickWind
#if QT_CONFIG(opengl)
-QSGMaterialType QQuickShapeLinearGradientShader::type;
-
QQuickShapeLinearGradientShader::QQuickShapeLinearGradientShader()
{
setShaderSourceFile(QOpenGLShader::Vertex,
@@ -786,8 +776,8 @@ void QQuickShapeLinearGradientShader::updateState(const RenderState &state, QSGM
program()->setUniformValue(m_gradStartLoc, QVector2D(node->m_fillGradient.a));
program()->setUniformValue(m_gradEndLoc, QVector2D(node->m_fillGradient.b));
- const QQuickShapeGradientCache::Key cacheKey(node->m_fillGradient.stops, node->m_fillGradient.spread);
- QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(cacheKey);
+ const QQuickShapeGradientCacheKey cacheKey(node->m_fillGradient.stops, node->m_fillGradient.spread);
+ QSGTexture *tx = QQuickShapeGradientOpenGLCache::currentCache()->get(cacheKey);
tx->bind();
}
@@ -797,6 +787,73 @@ char const *const *QQuickShapeLinearGradientShader::attributeNames() const
return attr;
}
+#endif // QT_CONFIG(opengl)
+
+QQuickShapeLinearGradientRhiShader::QQuickShapeLinearGradientRhiShader()
+{
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/lineargradient.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/lineargradient.frag.qsb"));
+}
+
+bool QQuickShapeLinearGradientRhiShader::updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
+ QQuickShapeLinearGradientMaterial *m = static_cast<QQuickShapeLinearGradientMaterial *>(newMaterial);
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 84);
+
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ changed = true;
+ }
+
+ QQuickShapeGenericStrokeFillNode *node = m->node();
+
+ if (!oldMaterial || m_gradA.x() != node->m_fillGradient.a.x() || m_gradA.y() != node->m_fillGradient.a.y()) {
+ m_gradA = QVector2D(node->m_fillGradient.a.x(), node->m_fillGradient.a.y());
+ Q_ASSERT(sizeof(m_gradA) == 8);
+ memcpy(buf->data() + 64, &m_gradA, 8);
+ changed = true;
+ }
+
+ if (!oldMaterial || m_gradB.x() != node->m_fillGradient.b.x() || m_gradB.y() != node->m_fillGradient.b.y()) {
+ m_gradB = QVector2D(node->m_fillGradient.b.x(), node->m_fillGradient.b.y());
+ memcpy(buf->data() + 72, &m_gradB, 8);
+ changed = true;
+ }
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(buf->data() + 80, &opacity, 4);
+ changed = true;
+ }
+
+ return changed;
+}
+
+void QQuickShapeLinearGradientRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *)
+{
+ if (binding != 1)
+ return;
+
+ QQuickShapeLinearGradientMaterial *m = static_cast<QQuickShapeLinearGradientMaterial *>(newMaterial);
+ QQuickShapeGenericStrokeFillNode *node = m->node();
+ const QQuickShapeGradientCacheKey cacheKey(node->m_fillGradient.stops, node->m_fillGradient.spread);
+ QSGTexture *t = QQuickShapeGradientCache::cacheForRhi(state.rhi())->get(cacheKey);
+ t->updateRhiTexture(state.rhi(), state.resourceUpdateBatch());
+ *texture = t;
+}
+
+QSGMaterialType *QQuickShapeLinearGradientMaterial::type() const
+{
+ static QSGMaterialType type;
+ return &type;
+}
+
int QQuickShapeLinearGradientMaterial::compare(const QSGMaterial *other) const
{
Q_ASSERT(other && type() == other->type());
@@ -836,7 +893,19 @@ int QQuickShapeLinearGradientMaterial::compare(const QSGMaterial *other) const
return 0;
}
-QSGMaterialType QQuickShapeRadialGradientShader::type;
+QSGMaterialShader *QQuickShapeLinearGradientMaterial::createShader() const
+{
+ if (flags().testFlag(RhiShaderWanted))
+ return new QQuickShapeLinearGradientRhiShader;
+#if QT_CONFIG(opengl)
+ else
+ return new QQuickShapeLinearGradientShader;
+#else
+ return nullptr;
+#endif
+}
+
+#if QT_CONFIG(opengl)
QQuickShapeRadialGradientShader::QQuickShapeRadialGradientShader()
{
@@ -880,8 +949,8 @@ void QQuickShapeRadialGradientShader::updateState(const RenderState &state, QSGM
program()->setUniformValue(m_focalRadiusLoc, focalRadius);
program()->setUniformValue(m_focalToCenterLoc, focalToCenter);
- const QQuickShapeGradientCache::Key cacheKey(node->m_fillGradient.stops, node->m_fillGradient.spread);
- QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(cacheKey);
+ const QQuickShapeGradientCacheKey cacheKey(node->m_fillGradient.stops, node->m_fillGradient.spread);
+ QSGTexture *tx = QQuickShapeGradientOpenGLCache::currentCache()->get(cacheKey);
tx->bind();
}
@@ -891,6 +960,92 @@ char const *const *QQuickShapeRadialGradientShader::attributeNames() const
return attr;
}
+#endif // QT_CONFIG(opengl)
+
+QQuickShapeRadialGradientRhiShader::QQuickShapeRadialGradientRhiShader()
+{
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/radialgradient.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/radialgradient.frag.qsb"));
+}
+
+bool QQuickShapeRadialGradientRhiShader::updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
+ QQuickShapeLinearGradientMaterial *m = static_cast<QQuickShapeLinearGradientMaterial *>(newMaterial);
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 92);
+
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ changed = true;
+ }
+
+ QQuickShapeGenericStrokeFillNode *node = m->node();
+
+ const QPointF centerPoint = node->m_fillGradient.a;
+ const QPointF focalPoint = node->m_fillGradient.b;
+ const QPointF focalToCenter = centerPoint - focalPoint;
+ const float centerRadius = node->m_fillGradient.v0;
+ const float focalRadius = node->m_fillGradient.v1;
+
+ if (!oldMaterial || m_focalPoint.x() != focalPoint.x() || m_focalPoint.y() != focalPoint.y()) {
+ m_focalPoint = QVector2D(focalPoint.x(), focalPoint.y());
+ Q_ASSERT(sizeof(m_focalPoint) == 8);
+ memcpy(buf->data() + 64, &m_focalPoint, 8);
+ changed = true;
+ }
+
+ if (!oldMaterial || m_focalToCenter.x() != focalToCenter.x() || m_focalToCenter.y() != focalToCenter.y()) {
+ m_focalToCenter = QVector2D(focalToCenter.x(), focalToCenter.y());
+ Q_ASSERT(sizeof(m_focalToCenter) == 8);
+ memcpy(buf->data() + 72, &m_focalToCenter, 8);
+ changed = true;
+ }
+
+ if (!oldMaterial || m_centerRadius != centerRadius) {
+ m_centerRadius = centerRadius;
+ memcpy(buf->data() + 80, &m_centerRadius, 4);
+ changed = true;
+ }
+
+ if (!oldMaterial || m_focalRadius != focalRadius) {
+ m_focalRadius = focalRadius;
+ memcpy(buf->data() + 84, &m_focalRadius, 4);
+ changed = true;
+ }
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(buf->data() + 88, &opacity, 4);
+ changed = true;
+ }
+
+ return changed;
+}
+
+void QQuickShapeRadialGradientRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *)
+{
+ if (binding != 1)
+ return;
+
+ QQuickShapeLinearGradientMaterial *m = static_cast<QQuickShapeLinearGradientMaterial *>(newMaterial);
+ QQuickShapeGenericStrokeFillNode *node = m->node();
+ const QQuickShapeGradientCacheKey cacheKey(node->m_fillGradient.stops, node->m_fillGradient.spread);
+ QSGTexture *t = QQuickShapeGradientCache::cacheForRhi(state.rhi())->get(cacheKey);
+ t->updateRhiTexture(state.rhi(), state.resourceUpdateBatch());
+ *texture = t;
+}
+
+QSGMaterialType *QQuickShapeRadialGradientMaterial::type() const
+{
+ static QSGMaterialType type;
+ return &type;
+}
+
int QQuickShapeRadialGradientMaterial::compare(const QSGMaterial *other) const
{
Q_ASSERT(other && type() == other->type());
@@ -935,7 +1090,19 @@ int QQuickShapeRadialGradientMaterial::compare(const QSGMaterial *other) const
return 0;
}
-QSGMaterialType QQuickShapeConicalGradientShader::type;
+QSGMaterialShader *QQuickShapeRadialGradientMaterial::createShader() const
+{
+ if (flags().testFlag(RhiShaderWanted))
+ return new QQuickShapeRadialGradientRhiShader;
+#if QT_CONFIG(opengl)
+ else
+ return new QQuickShapeRadialGradientShader;
+#else
+ return nullptr;
+#endif
+}
+
+#if QT_CONFIG(opengl)
QQuickShapeConicalGradientShader::QQuickShapeConicalGradientShader()
{
@@ -972,8 +1139,8 @@ void QQuickShapeConicalGradientShader::updateState(const RenderState &state, QSG
program()->setUniformValue(m_angleLoc, angle);
program()->setUniformValue(m_translationPointLoc, centerPoint);
- const QQuickShapeGradientCache::Key cacheKey(node->m_fillGradient.stops, QQuickShapeGradient::RepeatSpread);
- QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(cacheKey);
+ const QQuickShapeGradientCacheKey cacheKey(node->m_fillGradient.stops, QQuickShapeGradient::RepeatSpread);
+ QSGTexture *tx = QQuickShapeGradientOpenGLCache::currentCache()->get(cacheKey);
tx->bind();
}
@@ -983,6 +1150,76 @@ char const *const *QQuickShapeConicalGradientShader::attributeNames() const
return attr;
}
+#endif // QT_CONFIG(opengl)
+
+QQuickShapeConicalGradientRhiShader::QQuickShapeConicalGradientRhiShader()
+{
+ setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/conicalgradient.vert.qsb"));
+ setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/conicalgradient.frag.qsb"));
+}
+
+bool QQuickShapeConicalGradientRhiShader::updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
+ QQuickShapeLinearGradientMaterial *m = static_cast<QQuickShapeLinearGradientMaterial *>(newMaterial);
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 80);
+
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ changed = true;
+ }
+
+ QQuickShapeGenericStrokeFillNode *node = m->node();
+
+ const QPointF centerPoint = node->m_fillGradient.a;
+ const float angle = -qDegreesToRadians(node->m_fillGradient.v0);
+
+ if (!oldMaterial || m_centerPoint.x() != centerPoint.x() || m_centerPoint.y() != centerPoint.y()) {
+ m_centerPoint = QVector2D(centerPoint.x(), centerPoint.y());
+ Q_ASSERT(sizeof(m_centerPoint) == 8);
+ memcpy(buf->data() + 64, &m_centerPoint, 8);
+ changed = true;
+ }
+
+ if (!oldMaterial || m_angle != angle) {
+ m_angle = angle;
+ memcpy(buf->data() + 72, &m_angle, 4);
+ changed = true;
+ }
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(buf->data() + 76, &opacity, 4);
+ changed = true;
+ }
+
+ return changed;
+}
+
+void QQuickShapeConicalGradientRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *)
+{
+ if (binding != 1)
+ return;
+
+ QQuickShapeLinearGradientMaterial *m = static_cast<QQuickShapeLinearGradientMaterial *>(newMaterial);
+ QQuickShapeGenericStrokeFillNode *node = m->node();
+ const QQuickShapeGradientCacheKey cacheKey(node->m_fillGradient.stops, node->m_fillGradient.spread);
+ QSGTexture *t = QQuickShapeGradientCache::cacheForRhi(state.rhi())->get(cacheKey);
+ t->updateRhiTexture(state.rhi(), state.resourceUpdateBatch());
+ *texture = t;
+}
+
+QSGMaterialType *QQuickShapeConicalGradientMaterial::type() const
+{
+ static QSGMaterialType type;
+ return &type;
+}
+
int QQuickShapeConicalGradientMaterial::compare(const QSGMaterial *other) const
{
Q_ASSERT(other && type() == other->type());
@@ -1018,6 +1255,16 @@ int QQuickShapeConicalGradientMaterial::compare(const QSGMaterial *other) const
return 0;
}
-#endif // QT_CONFIG(opengl)
+QSGMaterialShader *QQuickShapeConicalGradientMaterial::createShader() const
+{
+ if (flags().testFlag(RhiShaderWanted))
+ return new QQuickShapeConicalGradientRhiShader;
+#if QT_CONFIG(opengl)
+ else
+ return new QQuickShapeConicalGradientShader;
+#else
+ return nullptr;
+#endif
+}
QT_END_NAMESPACE
diff --git a/src/quickshapes/qquickshapegenericrenderer_p.h b/src/quickshapes/qquickshapegenericrenderer_p.h
index 9928d7ab72..4590c662c1 100644
--- a/src/quickshapes/qquickshapegenericrenderer_p.h
+++ b/src/quickshapes/qquickshapegenericrenderer_p.h
@@ -244,7 +244,7 @@ public:
#if QT_CONFIG(opengl)
-class QQuickShapeLinearGradientShader : public QSGMaterialShader
+ class QQuickShapeLinearGradientShader : public QSGMaterialShader
{
public:
QQuickShapeLinearGradientShader();
@@ -253,8 +253,6 @@ public:
void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
char const *const *attributeNames() const override;
- static QSGMaterialType type;
-
private:
int m_opacityLoc = -1;
int m_matrixLoc = -1;
@@ -262,6 +260,23 @@ private:
int m_gradEndLoc = -1;
};
+#endif // QT_CONFIG(opengl)
+
+class QQuickShapeLinearGradientRhiShader : public QSGMaterialRhiShader
+{
+public:
+ QQuickShapeLinearGradientRhiShader();
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newMaterial,
+ QSGMaterial *oldMaterial) override;
+ void updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+
+private:
+ QVector2D m_gradA;
+ QVector2D m_gradB;
+};
+
class QQuickShapeLinearGradientMaterial : public QSGMaterial
{
public:
@@ -273,20 +288,12 @@ public:
// the vertex data. The shader will rely on the fact that
// vertexCoord.xy is the Shape-space coordinate and so no modifications
// are welcome.
- setFlag(Blending | RequiresFullMatrix);
- }
-
- QSGMaterialType *type() const override
- {
- return &QQuickShapeLinearGradientShader::type;
+ setFlag(Blending | RequiresFullMatrix | SupportsRhiShader);
}
+ QSGMaterialType *type() const override;
int compare(const QSGMaterial *other) const override;
-
- QSGMaterialShader *createShader() const override
- {
- return new QQuickShapeLinearGradientShader;
- }
+ QSGMaterialShader *createShader() const override;
QQuickShapeGenericStrokeFillNode *node() const { return m_node; }
@@ -294,6 +301,8 @@ private:
QQuickShapeGenericStrokeFillNode *m_node;
};
+#if QT_CONFIG(opengl)
+
class QQuickShapeRadialGradientShader : public QSGMaterialShader
{
public:
@@ -303,8 +312,6 @@ public:
void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
char const *const *attributeNames() const override;
- static QSGMaterialType type;
-
private:
int m_opacityLoc = -1;
int m_matrixLoc = -1;
@@ -314,26 +321,37 @@ private:
int m_focalRadiusLoc = -1;
};
+#endif // QT_CONFIG(opengl)
+
+class QQuickShapeRadialGradientRhiShader : public QSGMaterialRhiShader
+{
+public:
+ QQuickShapeRadialGradientRhiShader();
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newMaterial,
+ QSGMaterial *oldMaterial) override;
+ void updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+
+private:
+ QVector2D m_focalPoint;
+ QVector2D m_focalToCenter;
+ float m_centerRadius;
+ float m_focalRadius;
+};
+
class QQuickShapeRadialGradientMaterial : public QSGMaterial
{
public:
QQuickShapeRadialGradientMaterial(QQuickShapeGenericStrokeFillNode *node)
: m_node(node)
{
- setFlag(Blending | RequiresFullMatrix);
- }
-
- QSGMaterialType *type() const override
- {
- return &QQuickShapeRadialGradientShader::type;
+ setFlag(Blending | RequiresFullMatrix | SupportsRhiShader);
}
+ QSGMaterialType *type() const override;
int compare(const QSGMaterial *other) const override;
-
- QSGMaterialShader *createShader() const override
- {
- return new QQuickShapeRadialGradientShader;
- }
+ QSGMaterialShader *createShader() const override;
QQuickShapeGenericStrokeFillNode *node() const { return m_node; }
@@ -341,6 +359,8 @@ private:
QQuickShapeGenericStrokeFillNode *m_node;
};
+#if QT_CONFIG(opengl)
+
class QQuickShapeConicalGradientShader : public QSGMaterialShader
{
public:
@@ -350,8 +370,6 @@ public:
void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
char const *const *attributeNames() const override;
- static QSGMaterialType type;
-
private:
int m_opacityLoc = -1;
int m_matrixLoc = -1;
@@ -359,26 +377,35 @@ private:
int m_translationPointLoc = -1;
};
+#endif // QT_CONFIG(opengl)
+
+class QQuickShapeConicalGradientRhiShader : public QSGMaterialRhiShader
+{
+public:
+ QQuickShapeConicalGradientRhiShader();
+
+ bool updateUniformData(RenderState &state, QSGMaterial *newMaterial,
+ QSGMaterial *oldMaterial) override;
+ void updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+
+private:
+ QVector2D m_centerPoint;
+ float m_angle;
+};
+
class QQuickShapeConicalGradientMaterial : public QSGMaterial
{
public:
QQuickShapeConicalGradientMaterial(QQuickShapeGenericStrokeFillNode *node)
: m_node(node)
{
- setFlag(Blending | RequiresFullMatrix);
- }
-
- QSGMaterialType *type() const override
- {
- return &QQuickShapeConicalGradientShader::type;
+ setFlag(Blending | RequiresFullMatrix | SupportsRhiShader);
}
+ QSGMaterialType *type() const override;
int compare(const QSGMaterial *other) const override;
-
- QSGMaterialShader *createShader() const override
- {
- return new QQuickShapeConicalGradientShader;
- }
+ QSGMaterialShader *createShader() const override;
QQuickShapeGenericStrokeFillNode *node() const { return m_node; }
@@ -386,8 +413,6 @@ private:
QQuickShapeGenericStrokeFillNode *m_node;
};
-#endif // QT_CONFIG(opengl)
-
QT_END_NAMESPACE
#endif // QQUICKSHAPEGENERICRENDERER_P_H
diff --git a/src/quickshapes/qquickshapenvprrenderer.cpp b/src/quickshapes/qquickshapenvprrenderer.cpp
index 51af0d8961..721091b669 100644
--- a/src/quickshapes/qquickshapenvprrenderer.cpp
+++ b/src/quickshapes/qquickshapenvprrenderer.cpp
@@ -177,7 +177,7 @@ QDebug operator<<(QDebug debug, const QQuickShapeNvprRenderer::NvprPath &path)
debug << "Path with" << path.cmd.count() << "commands";
int ci = 0;
for (GLubyte cmd : path.cmd) {
- static struct { GLubyte cmd; const char *s; int coordCount; } nameTab[] = {
+ static struct { GLubyte cmd; const char *s; int coordCount; } nameTabs[] = {
{ GL_MOVE_TO_NV, "moveTo", 2 },
{ GL_LINE_TO_NV, "lineTo", 2 },
{ GL_QUADRATIC_CURVE_TO_NV, "quadTo", 4 },
@@ -187,14 +187,14 @@ QDebug operator<<(QDebug debug, const QQuickShapeNvprRenderer::NvprPath &path)
{ GL_SMALL_CW_ARC_TO_NV, "arcTo-small-CW", 5 },
{ GL_SMALL_CCW_ARC_TO_NV, "arcTo-small-CCW", 5 },
{ GL_CLOSE_PATH_NV, "closePath", 0 } };
- for (size_t i = 0; i < sizeof(nameTab) / sizeof(nameTab[0]); ++i) {
- if (nameTab[i].cmd == cmd) {
+ for (const auto &nameTab : nameTabs) {
+ if (nameTab.cmd == cmd) {
QByteArray cs;
- for (int j = 0; j < nameTab[i].coordCount; ++j) {
+ for (int j = 0; j < nameTab.coordCount; ++j) {
cs.append(QByteArray::number(path.coord[ci++]));
cs.append(' ');
}
- debug << "\n " << nameTab[i].s << " " << cs;
+ debug << "\n " << nameTab.s << " " << cs;
break;
}
}
@@ -712,8 +712,8 @@ void QQuickShapeNvprRenderNode::renderFill(ShapePathRenderData *d)
} else {
Q_UNREACHABLE();
}
- const QQuickShapeGradientCache::Key cacheKey(d->fillGradient.stops, spread);
- QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(cacheKey);
+ const QQuickShapeGradientCacheKey cacheKey(d->fillGradient.stops, spread);
+ QSGTexture *tx = QQuickShapeGradientOpenGLCache::currentCache()->get(cacheKey);
tx->bind();
} else {
mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid);
diff --git a/src/quickshapes/qtquickshapes.qrc b/src/quickshapes/qtquickshapes.qrc
index f139861693..1e3dcdef4e 100644
--- a/src/quickshapes/qtquickshapes.qrc
+++ b/src/quickshapes/qtquickshapes.qrc
@@ -8,13 +8,19 @@
<file>shaders/lineargradient.frag</file>
<file>shaders/lineargradient_core.vert</file>
<file>shaders/lineargradient_core.frag</file>
+ <file>shaders_ng/lineargradient.vert.qsb</file>
+ <file>shaders_ng/lineargradient.frag.qsb</file>
<file>shaders/radialgradient.vert</file>
<file>shaders/radialgradient.frag</file>
<file>shaders/radialgradient_core.vert</file>
<file>shaders/radialgradient_core.frag</file>
+ <file>shaders_ng/radialgradient.vert.qsb</file>
+ <file>shaders_ng/radialgradient.frag.qsb</file>
<file>shaders/conicalgradient.vert</file>
<file>shaders/conicalgradient.frag</file>
<file>shaders/conicalgradient_core.vert</file>
<file>shaders/conicalgradient_core.frag</file>
+ <file>shaders_ng/conicalgradient.vert.qsb</file>
+ <file>shaders_ng/conicalgradient.frag.qsb</file>
</qresource>
</RCC>
diff --git a/src/quickshapes/shaders_ng/compile.bat b/src/quickshapes/shaders_ng/compile.bat
new file mode 100755
index 0000000000..860dc7a07a
--- /dev/null
+++ b/src/quickshapes/shaders_ng/compile.bat
@@ -0,0 +1,45 @@
+:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::
+:: Copyright (C) 2019 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$
+::
+:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o lineargradient.vert.qsb lineargradient.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o lineargradient.frag.qsb lineargradient.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o radialgradient.vert.qsb radialgradient.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o radialgradient.frag.qsb radialgradient.frag
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o conicalgradient.vert.qsb conicalgradient.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o conicalgradient.frag.qsb conicalgradient.frag
diff --git a/src/quickshapes/shaders_ng/conicalgradient.frag b/src/quickshapes/shaders_ng/conicalgradient.frag
new file mode 100644
index 0000000000..0b1e01bae2
--- /dev/null
+++ b/src/quickshapes/shaders_ng/conicalgradient.frag
@@ -0,0 +1,25 @@
+#version 440
+
+layout(location = 0) in vec2 coord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D gradTabTexture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 translationPoint;
+ float angle;
+ float opacity;
+} ubuf;
+
+#define INVERSE_2PI 0.1591549430918953358
+
+void main()
+{
+ float t;
+ if (abs(coord.y) == abs(coord.x))
+ t = (atan(-coord.y + 0.002, coord.x) + ubuf.angle) * INVERSE_2PI;
+ else
+ t = (atan(-coord.y, coord.x) + ubuf.angle) * INVERSE_2PI;
+ fragColor = texture(gradTabTexture, vec2(t - floor(t), 0.5)) * ubuf.opacity;
+}
diff --git a/src/quickshapes/shaders_ng/conicalgradient.frag.qsb b/src/quickshapes/shaders_ng/conicalgradient.frag.qsb
new file mode 100644
index 0000000000..9aa59ca651
--- /dev/null
+++ b/src/quickshapes/shaders_ng/conicalgradient.frag.qsb
Binary files differ
diff --git a/src/quickshapes/shaders_ng/conicalgradient.vert b/src/quickshapes/shaders_ng/conicalgradient.vert
new file mode 100644
index 0000000000..3db027294b
--- /dev/null
+++ b/src/quickshapes/shaders_ng/conicalgradient.vert
@@ -0,0 +1,21 @@
+#version 440
+
+layout(location = 0) in vec4 vertexCoord;
+layout(location = 1) in vec4 vertexColor;
+
+layout(location = 0) out vec2 coord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 translationPoint;
+ float angle;
+ float opacity;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ coord = vertexCoord.xy - ubuf.translationPoint;
+ gl_Position = ubuf.matrix * vertexCoord;
+}
diff --git a/src/quickshapes/shaders_ng/conicalgradient.vert.qsb b/src/quickshapes/shaders_ng/conicalgradient.vert.qsb
new file mode 100644
index 0000000000..7f5985ef64
--- /dev/null
+++ b/src/quickshapes/shaders_ng/conicalgradient.vert.qsb
Binary files differ
diff --git a/src/quickshapes/shaders_ng/lineargradient.frag b/src/quickshapes/shaders_ng/lineargradient.frag
new file mode 100644
index 0000000000..16894fc764
--- /dev/null
+++ b/src/quickshapes/shaders_ng/lineargradient.frag
@@ -0,0 +1,18 @@
+#version 440
+
+layout(location = 0) in float gradTabIndex;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D gradTabTexture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 gradStart;
+ vec2 gradEnd;
+ float opacity;
+} ubuf;
+
+void main()
+{
+ fragColor = texture(gradTabTexture, vec2(gradTabIndex, 0.5)) * ubuf.opacity;
+}
diff --git a/src/quickshapes/shaders_ng/lineargradient.frag.qsb b/src/quickshapes/shaders_ng/lineargradient.frag.qsb
new file mode 100644
index 0000000000..470de007b5
--- /dev/null
+++ b/src/quickshapes/shaders_ng/lineargradient.frag.qsb
Binary files differ
diff --git a/src/quickshapes/shaders_ng/lineargradient.vert b/src/quickshapes/shaders_ng/lineargradient.vert
new file mode 100644
index 0000000000..b4eb868186
--- /dev/null
+++ b/src/quickshapes/shaders_ng/lineargradient.vert
@@ -0,0 +1,22 @@
+#version 440
+
+layout(location = 0) in vec4 vertexCoord;
+layout(location = 1) in vec4 vertexColor;
+
+layout(location = 0) out float gradTabIndex;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 gradStart;
+ vec2 gradEnd;
+ float opacity;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ vec2 gradVec = ubuf.gradEnd - ubuf.gradStart;
+ gradTabIndex = dot(gradVec, vertexCoord.xy - ubuf.gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);
+ gl_Position = ubuf.matrix * vertexCoord;
+}
diff --git a/src/quickshapes/shaders_ng/lineargradient.vert.qsb b/src/quickshapes/shaders_ng/lineargradient.vert.qsb
new file mode 100644
index 0000000000..8d054efbb4
--- /dev/null
+++ b/src/quickshapes/shaders_ng/lineargradient.vert.qsb
Binary files differ
diff --git a/src/quickshapes/shaders_ng/radialgradient.frag b/src/quickshapes/shaders_ng/radialgradient.frag
new file mode 100644
index 0000000000..411e589295
--- /dev/null
+++ b/src/quickshapes/shaders_ng/radialgradient.frag
@@ -0,0 +1,32 @@
+#version 440
+
+layout(location = 0) in vec2 coord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D gradTabTexture;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 translationPoint;
+ vec2 focalToCenter;
+ float centerRadius;
+ float focalRadius;
+ float opacity;
+} ubuf;
+
+void main()
+{
+ float rd = ubuf.centerRadius - ubuf.focalRadius;
+ float b = 2.0 * (rd * ubuf.focalRadius + dot(coord, ubuf.focalToCenter));
+ float fmp2_m_radius2 = -ubuf.focalToCenter.x * ubuf.focalToCenter.x - ubuf.focalToCenter.y * ubuf.focalToCenter.y + rd * rd;
+ float inverse_2_fmp2_m_radius2 = 1.0 / (2.0 * fmp2_m_radius2);
+ float det = b * b - 4.0 * fmp2_m_radius2 * ((ubuf.focalRadius * ubuf.focalRadius) - dot(coord, coord));
+ vec4 result = vec4(0.0);
+ if (det >= 0.0) {
+ float detSqrt = sqrt(det);
+ float w = max((-b - detSqrt) * inverse_2_fmp2_m_radius2, (-b + detSqrt) * inverse_2_fmp2_m_radius2);
+ if (ubuf.focalRadius + w * (ubuf.centerRadius - ubuf.focalRadius) >= 0.0)
+ result = texture(gradTabTexture, vec2(w, 0.5)) * ubuf.opacity;
+ }
+ fragColor = result;
+}
diff --git a/src/quickshapes/shaders_ng/radialgradient.frag.qsb b/src/quickshapes/shaders_ng/radialgradient.frag.qsb
new file mode 100644
index 0000000000..166eb2f92d
--- /dev/null
+++ b/src/quickshapes/shaders_ng/radialgradient.frag.qsb
Binary files differ
diff --git a/src/quickshapes/shaders_ng/radialgradient.vert b/src/quickshapes/shaders_ng/radialgradient.vert
new file mode 100644
index 0000000000..08f15c4f8c
--- /dev/null
+++ b/src/quickshapes/shaders_ng/radialgradient.vert
@@ -0,0 +1,23 @@
+#version 440
+
+layout(location = 0) in vec4 vertexCoord;
+layout(location = 1) in vec4 vertexColor;
+
+layout(location = 0) out vec2 coord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ vec2 translationPoint;
+ vec2 focalToCenter;
+ float centerRadius;
+ float focalRadius;
+ float opacity;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ coord = vertexCoord.xy - ubuf.translationPoint;
+ gl_Position = ubuf.matrix * vertexCoord;
+}
diff --git a/src/quickshapes/shaders_ng/radialgradient.vert.qsb b/src/quickshapes/shaders_ng/radialgradient.vert.qsb
new file mode 100644
index 0000000000..293e23947a
--- /dev/null
+++ b/src/quickshapes/shaders_ng/radialgradient.vert.qsb
Binary files differ
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index 55c095af8e..083b7dbae1 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -50,7 +50,6 @@
#include <private/qqmldebugconnector_p.h>
#include <private/qquickprofiler_p.h>
#include <private/qqmldebugserviceinterfaces_p.h>
-#include <private/qqmlmemoryprofiler_p.h>
#include <QtQml/qqmlengine.h>
#include <private/qqmlengine_p.h>
@@ -129,7 +128,7 @@ void QQuickWidgetPrivate::init(QQmlEngine* e)
if (!engine.isNull() && !engine.data()->incubationController())
engine.data()->setIncubationController(offscreenWindow->incubationController());
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
q->setAcceptDrops(true);
#endif
@@ -252,7 +251,6 @@ void QQuickWidgetPrivate::execute()
component = nullptr;
}
if (!source.isEmpty()) {
- QML_MEMORY_SCOPE_URL(engine.data()->baseUrl().resolved(source));
component = new QQmlComponent(engine.data(), source, q);
if (!component->isLoading()) {
q->continueExecute();
@@ -285,7 +283,16 @@ void QQuickWidgetPrivate::render(bool needsSync)
Q_ASSERT(context);
- if (!context->makeCurrent(offscreenSurface)) {
+ bool current = context->makeCurrent(offscreenSurface);
+
+ if (!current && !context->isValid()) {
+ renderControl->invalidate();
+ current = context->create() && context->makeCurrent(offscreenSurface);
+ if (current)
+ renderControl->initialize(context);
+ }
+
+ if (!current) {
qWarning("QQuickWidget: Cannot render due to failing makeCurrent()");
return;
}
@@ -1123,6 +1130,13 @@ GLuint QQuickWidgetPrivate::textureId() const
return resolvedFbo ? resolvedFbo->texture()
: (fbo ? fbo->texture() : 0);
}
+
+QPlatformTextureList::Flags QQuickWidgetPrivate::textureListFlags()
+{
+ QPlatformTextureList::Flags flags = QWidgetPrivate::textureListFlags();
+ flags |= QPlatformTextureList::NeedsPremultipliedAlphaBlending;
+ return flags;
+}
#endif
/*!
@@ -1537,7 +1551,7 @@ bool QQuickWidget::event(QEvent *e)
return QWidget::event(e);
}
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
/*! \reimp */
void QQuickWidget::dragEnterEvent(QDragEnterEvent *e)
@@ -1572,7 +1586,7 @@ void QQuickWidget::dropEvent(QDropEvent *e)
d->offscreenWindow->event(e);
}
-#endif // draganddrop
+#endif // quick_draganddrop
// TODO: try to separate the two cases of
// 1. render() unconditionally without sync
diff --git a/src/quickwidgets/qquickwidget.h b/src/quickwidgets/qquickwidget.h
index 5543705f13..1ceb7449b3 100644
--- a/src/quickwidgets/qquickwidget.h
+++ b/src/quickwidgets/qquickwidget.h
@@ -134,7 +134,7 @@ protected:
void wheelEvent(QWheelEvent *) override;
#endif
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
void dragEnterEvent(QDragEnterEvent *) override;
void dragMoveEvent(QDragMoveEvent *) override;
void dragLeaveEvent(QDragLeaveEvent *) override;
diff --git a/src/quickwidgets/qquickwidget_p.h b/src/quickwidgets/qquickwidget_p.h
index 03571e8dc7..f4f9db7772 100644
--- a/src/quickwidgets/qquickwidget_p.h
+++ b/src/quickwidgets/qquickwidget_p.h
@@ -101,6 +101,7 @@ public:
#if QT_CONFIG(opengl)
GLuint textureId() const override;
+ QPlatformTextureList::Flags textureListFlags() override;
QImage grabFramebuffer() override;
#else
QImage grabFramebuffer();
diff --git a/src/src.pro b/src/src.pro
index 1b2b4ef6f8..98e1779dc5 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -4,12 +4,17 @@ include($$OUT_PWD/qml/qtqml-config.pri)
include($$OUT_PWD/quick/qtquick-config.pri)
QT_FOR_CONFIG += qml qml-private quick-private
SUBDIRS += \
- qml
+ qml \
+ qmlmodels
+
+qtConfig(qml-worker-script): \
+ SUBDIRS += qmlworkerscript
qtHaveModule(gui):qtConfig(qml-animation) {
- SUBDIRS += \
- quick \
- quickshapes
+ SUBDIRS += quick
+
+ qtConfig(quick-path): \
+ SUBDIRS += quickshapes
qtConfig(testlib): \
SUBDIRS += qmltest
diff --git a/sync.profile b/sync.profile
index 642942d152..06cff0f960 100644
--- a/sync.profile
+++ b/sync.profile
@@ -7,7 +7,9 @@
"QtQuickTest" => "$basedir/src/qmltest",
"QtPacketProtocol" => "$basedir/src/plugins/qmltooling/packetprotocol",
"QtQmlDebug" => "$basedir/src/qmldebug",
+ "QtQmlModels" => "$basedir/src/qmlmodels",
+ "QtQmlWorkerScript" => "$basedir/src/qmlworkerscript",
);
%inject_headers = (
- "$basedir/src/qml" => [ "^qqmljsgrammar_p.h", "^qqmljsparser_p.h" ],
+ "$basedir/src/qml" => [ "^qqmljsgrammar_p.h", "^qqmljsparser_p.h", "^qml_compile_hash_p.h" ],
);
diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt
index f304a99705..bda5d626a9 100644
--- a/tests/auto/cmake/CMakeLists.txt
+++ b/tests/auto/cmake/CMakeLists.txt
@@ -27,3 +27,15 @@ add_test(qtquickcompiler ${CMAKE_CTEST_COMMAND}
--build-options "-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}" ${BUILD_OPTIONS_LIST}
--test-command qqc_test
)
+
+add_test(qmlimportscanner ${CMAKE_CTEST_COMMAND}
+ --build-and-test
+ "${CMAKE_CURRENT_SOURCE_DIR}/qmlimportscanner/"
+ "${CMAKE_CURRENT_BINARY_DIR}/qmlimportscanner"
+ --build-config "${CMAKE_BUILD_TYPE}"
+ --build-generator ${CMAKE_GENERATOR}
+ --build-makeprogram ${CMAKE_MAKE_PROGRAM}
+ --build-project qis_test
+ --build-options "-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}" ${BUILD_OPTIONS_LIST}
+ --test-command qis_test
+)
diff --git a/tests/auto/cmake/qmlimportscanner/CMakeLists.txt b/tests/auto/cmake/qmlimportscanner/CMakeLists.txt
new file mode 100644
index 0000000000..354b0f8dfc
--- /dev/null
+++ b/tests/auto/cmake/qmlimportscanner/CMakeLists.txt
@@ -0,0 +1,18 @@
+
+cmake_minimum_required(VERSION 3.1)
+project(qis_test)
+
+find_package(Qt5Qml 5.0.0 REQUIRED)
+find_package(Qt5Gui 5.0.0 REQUIRED)
+find_package(Qt5Test 5.0.0 REQUIRED)
+find_package(Qt5QmlImportScanner REQUIRED)
+
+set(CMAKE_CXXFLAGS "${CMAKE_CXXFLAGS} ${Qt5Core_EXECUTABLE_COMPILE_FLAGS}")
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+
+add_executable(qis_test "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/qis_test.qrc")
+target_link_libraries(qis_test PRIVATE Qt5::Gui Qt5::Qml Qt5::Test)
+qt5_import_qml_plugins(qis_test)
diff --git a/tests/auto/cmake/qmlimportscanner/main.cpp b/tests/auto/cmake/qmlimportscanner/main.cpp
new file mode 100644
index 0000000000..370b10e113
--- /dev/null
+++ b/tests/auto/cmake/qmlimportscanner/main.cpp
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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 <QtCore>
+#include <QtQml>
+#include <QtTest>
+
+class tst_QQC : public QObject
+{
+ Q_OBJECT
+private slots:
+ void staticBuildTest();
+};
+
+void tst_QQC::staticBuildTest()
+{
+#ifdef QT_STATIC
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl("qrc:/main.qml"));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ QCOMPARE(obj->property("success").toInt(), 42);
+#endif
+}
+
+QTEST_MAIN(tst_QQC)
+
+#include "main.moc"
diff --git a/tests/auto/cmake/qmlimportscanner/main.qml b/tests/auto/cmake/qmlimportscanner/main.qml
new file mode 100644
index 0000000000..e0101958ea
--- /dev/null
+++ b/tests/auto/cmake/qmlimportscanner/main.qml
@@ -0,0 +1,5 @@
+import QtQml 2.0
+import QtQuick 2.0
+QtObject {
+ property int success: 42
+}
diff --git a/tests/auto/cmake/qmlimportscanner/qis_test.qrc b/tests/auto/cmake/qmlimportscanner/qis_test.qrc
new file mode 100644
index 0000000000..1f88fc4e71
--- /dev/null
+++ b/tests/auto/cmake/qmlimportscanner/qis_test.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+<file>./main.qml</file>
+<file alias="main.cpp">./main.cpp</file>
+</qresource>
+</RCC>
diff --git a/tests/auto/qml/bindingdependencyapi/dummy_imports.qml b/tests/auto/qml/bindingdependencyapi/dummy_imports.qml
new file mode 100644
index 0000000000..b9a196e188
--- /dev/null
+++ b/tests/auto/qml/bindingdependencyapi/dummy_imports.qml
@@ -0,0 +1,8 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in C++
+// code in tst_parserstress.cpp
+
+import QtQuick 2.0
+
+QtObject { } // This is needed in order to keep importscanner happy
diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/quit.js b/tests/auto/qml/debugger/qqmldebugjs/data/quit.js
new file mode 100644
index 0000000000..1a45fd5538
--- /dev/null
+++ b/tests/auto/qml/debugger/qqmldebugjs/data/quit.js
@@ -0,0 +1,4 @@
+function quit() {
+ console.log("hit");
+ Qt.quit();
+}
diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/quitInJS.qml b/tests/auto/qml/debugger/qqmldebugjs/data/quitInJS.qml
new file mode 100644
index 0000000000..6e4183100b
--- /dev/null
+++ b/tests/auto/qml/debugger/qqmldebugjs/data/quitInJS.qml
@@ -0,0 +1,41 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import "quit.js" as Quit;
+
+//DO NOT CHANGE
+
+Item {
+ Timer {
+ running: true
+ triggeredOnStart: true
+ onTriggered: Quit.quit();
+ }
+}
+
diff --git a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp
index 1ac28c473b..5b6c43bc0c 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp
+++ b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp
@@ -38,7 +38,6 @@
#include <QtTest/qtest.h>
#include <QtTest/qtestsystem.h>
#include <QtCore/qprocess.h>
-#include <QtCore/qtimer.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qdir.h>
#include <QtCore/qmutex.h>
@@ -59,6 +58,8 @@ const char *ONCOMPLETED_QMLFILE = "oncompleted.qml";
const char *CREATECOMPONENT_QMLFILE = "createComponent.qml";
const char *CONDITION_QMLFILE = "condition.qml";
const char *QUIT_QMLFILE = "quit.qml";
+const char *QUITINJS_QMLFILE = "quitInJS.qml";
+const char *QUIT_JSFILE = "quit.js";
const char *CHANGEBREAKPOINT_QMLFILE = "changeBreakpoint.qml";
const char *STEPACTION_QMLFILE = "stepAction.qml";
const char *BREAKPOINTRELOCATION_QMLFILE = "breakpointRelocation.qml";
@@ -110,8 +111,10 @@ private slots:
void setBreakpointInScriptOnOptimizedBinding();
void setBreakpointInScriptWithCondition_data() { targetData(); }
void setBreakpointInScriptWithCondition();
- void setBreakpointInScriptThatQuits_data() { targetData(); }
+ void setBreakpointInScriptThatQuits_data() { targetData(); };
void setBreakpointInScriptThatQuits();
+ void setBreakpointInJavaScript_data();
+ void setBreakpointInJavaScript();
void setBreakpointWhenAttaching();
void clearBreakpoint_data() { targetData(); }
@@ -162,8 +165,6 @@ private:
void targetData();
bool waitForClientSignal(const char *signal, int timeout = 30000);
void checkVersionParameters();
-
- QTime t;
};
@@ -171,7 +172,6 @@ private:
void tst_QQmlDebugJS::initTestCase()
{
QQmlDebugTest::initTestCase();
- t.start();
}
QQmlDebugTest::ConnectResult tst_QQmlDebugJS::init(bool qmlscene, const QString &qmlFile,
@@ -454,6 +454,48 @@ void tst_QQmlDebugJS::setBreakpointInScriptThatQuits()
QCOMPARE(m_process->exitStatus(), QProcess::NormalExit);
}
+void tst_QQmlDebugJS::setBreakpointInJavaScript_data()
+{
+ QTest::addColumn<bool>("qmlscene");
+ QTest::addColumn<bool>("seedCache");
+ QTest::newRow("custom / immediate") << false << false;
+ QTest::newRow("qmlscene / immediate") << true << false;
+ QTest::newRow("custom / seeded") << false << true;
+ QTest::newRow("qmlscene / seeded") << true << true;
+}
+
+void tst_QQmlDebugJS::setBreakpointInJavaScript()
+{
+ QFETCH(bool, qmlscene);
+ QFETCH(bool, seedCache);
+
+ if (seedCache) { // Make sure there is a qmlc file that the engine should _not_ laod.
+ QProcess process;
+ process.start(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene",
+ { testFile(QUITINJS_QMLFILE) });
+ QTRY_COMPARE(process.state(), QProcess::NotRunning);
+ }
+
+ QCOMPARE(init(qmlscene, QUITINJS_QMLFILE), ConnectSuccess);
+
+ const int sourceLine = 2;
+
+ m_client->setBreakpoint(QLatin1String(QUIT_JSFILE), sourceLine, -1, true);
+ m_client->connect();
+ QVERIFY(waitForClientSignal(SIGNAL(stopped())));
+
+ const QJsonObject body = m_client->response().body.toObject();
+
+ QCOMPARE(body.value("sourceLine").toInt(), sourceLine);
+ QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(),
+ QLatin1String(QUIT_JSFILE));
+
+ m_client->continueDebugging(QV4DebugClient::Continue);
+
+ QVERIFY(m_process->waitForFinished());
+ QCOMPARE(m_process->exitStatus(), QProcess::NormalExit);
+}
+
void tst_QQmlDebugJS::setBreakpointWhenAttaching()
{
int sourceLine = 35;
diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp
index 99c90c142f..0ebf43eb6f 100644
--- a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp
+++ b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp
@@ -524,7 +524,7 @@ void tst_QQmlEngineDebugService::watch_property()
QCOMPARE(spy.count(), 1);
QCOMPARE(spy.at(0).at(0).value<QByteArray>(), prop.name.toUtf8());
- QCOMPARE(spy.at(0).at(1).value<QVariant>(), qVariantFromValue(origWidth*2));
+ QCOMPARE(spy.at(0).at(1).value<QVariant>(), QVariant::fromValue(origWidth*2));
}
void tst_QQmlEngineDebugService::watch_object()
@@ -772,11 +772,11 @@ void tst_QQmlEngineDebugService::queryObject()
}
// test specific property values
- QCOMPARE(findProperty(rect.properties, "width").value, qVariantFromValue(500));
- QCOMPARE(findProperty(rect.properties, "height").value, qVariantFromValue(600));
- QCOMPARE(findProperty(rect.properties, "color").value, qVariantFromValue(QColor("blue")));
+ QCOMPARE(findProperty(rect.properties, "width").value, QVariant::fromValue(500));
+ QCOMPARE(findProperty(rect.properties, "height").value, QVariant::fromValue(600));
+ QCOMPARE(findProperty(rect.properties, "color").value, QVariant::fromValue(QColor("blue")));
- QCOMPARE(findProperty(text.properties, "color").value, qVariantFromValue(QColor("blue")));
+ QCOMPARE(findProperty(text.properties, "color").value, QVariant::fromValue(QColor("blue")));
} else {
foreach (const QQmlEngineDebugObjectReference &child, obj.children) {
QVERIFY(!child.className.isEmpty());
@@ -851,11 +851,11 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation()
}
// test specific property values
- QCOMPARE(findProperty(rect.properties, "width").value, qVariantFromValue(500));
- QCOMPARE(findProperty(rect.properties, "height").value, qVariantFromValue(600));
- QCOMPARE(findProperty(rect.properties, "color").value, qVariantFromValue(QColor("blue")));
+ QCOMPARE(findProperty(rect.properties, "width").value, QVariant::fromValue(500));
+ QCOMPARE(findProperty(rect.properties, "height").value, QVariant::fromValue(600));
+ QCOMPARE(findProperty(rect.properties, "color").value, QVariant::fromValue(QColor("blue")));
- QCOMPARE(findProperty(text.properties, "color").value, qVariantFromValue(QColor("blue")));
+ QCOMPARE(findProperty(text.properties, "color").value, QVariant::fromValue(QColor("blue")));
} else {
foreach (const QQmlEngineDebugObjectReference &child, obj.children) {
QVERIFY(!child.className.isEmpty());
@@ -1004,15 +1004,15 @@ void tst_QQmlEngineDebugService::queryExpressionResult_data()
QTest::addColumn<QString>("expr");
QTest::addColumn<QVariant>("result");
- QTest::newRow("width + 50") << "width + 50" << qVariantFromValue(60);
- QTest::newRow("blueRect.width") << "blueRect.width" << qVariantFromValue(500);
- QTest::newRow("bad expr") << "aeaef" << qVariantFromValue(QString("<undefined>"));
- QTest::newRow("QObject*") << "varObj" << qVariantFromValue(QString("<unnamed object>"));
- QTest::newRow("list of QObject*") << "varObjList" << qVariantFromValue(QVariantList() << QVariant(QString("<unnamed object>")));
+ QTest::newRow("width + 50") << "width + 50" << QVariant::fromValue(60);
+ QTest::newRow("blueRect.width") << "blueRect.width" << QVariant::fromValue(500);
+ QTest::newRow("bad expr") << "aeaef" << QVariant::fromValue(QString("<undefined>"));
+ QTest::newRow("QObject*") << "varObj" << QVariant::fromValue(QString("<unnamed object>"));
+ QTest::newRow("list of QObject*") << "varObjList" << QVariant::fromValue(QVariantList() << QVariant(QString("<unnamed object>")));
QVariantMap map;
map.insert(QLatin1String("rect"), QVariant(QLatin1String("<unnamed object>")));
- QTest::newRow("varObjMap") << "varObjMap" << qVariantFromValue(map);
- QTest::newRow("simpleVar") << "simpleVar" << qVariantFromValue(10.05);
+ QTest::newRow("varObjMap") << "varObjMap" << QVariant::fromValue(map);
+ QTest::newRow("simpleVar") << "simpleVar" << QVariant::fromValue(10.05);
}
void tst_QQmlEngineDebugService::queryExpressionResultInRootContext()
@@ -1052,15 +1052,15 @@ void tst_QQmlEngineDebugService::queryExpressionResultBC_data()
QTest::addColumn<QString>("expr");
QTest::addColumn<QVariant>("result");
- QTest::newRow("width + 50") << "width + 50" << qVariantFromValue(60);
- QTest::newRow("blueRect.width") << "blueRect.width" << qVariantFromValue(500);
- QTest::newRow("bad expr") << "aeaef" << qVariantFromValue(QString("<undefined>"));
- QTest::newRow("QObject*") << "varObj" << qVariantFromValue(QString("<unnamed object>"));
- QTest::newRow("list of QObject*") << "varObjList" << qVariantFromValue(QVariantList() << QVariant(QString("<unnamed object>")));
+ QTest::newRow("width + 50") << "width + 50" << QVariant::fromValue(60);
+ QTest::newRow("blueRect.width") << "blueRect.width" << QVariant::fromValue(500);
+ QTest::newRow("bad expr") << "aeaef" << QVariant::fromValue(QString("<undefined>"));
+ QTest::newRow("QObject*") << "varObj" << QVariant::fromValue(QString("<unnamed object>"));
+ QTest::newRow("list of QObject*") << "varObjList" << QVariant::fromValue(QVariantList() << QVariant(QString("<unnamed object>")));
QVariantMap map;
map.insert(QLatin1String("rect"), QVariant(QLatin1String("<unnamed object>")));
- QTest::newRow("varObjMap") << "varObjMap" << qVariantFromValue(map);
- QTest::newRow("simpleVar") << "simpleVar" << qVariantFromValue(10.05);
+ QTest::newRow("varObjMap") << "varObjMap" << QVariant::fromValue(map);
+ QTest::newRow("simpleVar") << "simpleVar" << QVariant::fromValue(10.05);
}
void tst_QQmlEngineDebugService::setBindingForObject()
diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
index 497c721f50..84f5eebd10 100644
--- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
+++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
@@ -36,7 +36,6 @@
#include <QQmlComponent>
#include <private/qv4engine_p.h>
#include <private/qv4debugging_p.h>
-#include <private/qv8engine_p.h>
#include <private/qv4objectiterator_p.h>
#include <private/qv4string_p.h>
#include <private/qqmlbuiltinfunctions_p.h>
diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations
index a5246c8792..4c04afe886 100644
--- a/tests/auto/qml/ecmascripttests/TestExpectations
+++ b/tests/auto/qml/ecmascripttests/TestExpectations
@@ -211,7 +211,6 @@ built-ins/Promise/prototype/then/ctor-throws.js fails
built-ins/Promise/race/ctx-ctor.js fails
built-ins/Proxy/ownKeys/return-duplicate-entries-throws.js fails
built-ins/Proxy/ownKeys/return-duplicate-symbol-entries-throws.js fails
-built-ins/RegExp/S15.10.2.12_A2_T1.js fails
built-ins/RegExp/prototype/Symbol.match/builtin-success-u-return-val-groups.js fails
built-ins/RegExp/prototype/Symbol.split/species-ctor.js fails
built-ins/RegExp/prototype/exec/S15.10.6.2_A5_T3.js fails
@@ -219,7 +218,6 @@ built-ins/RegExp/prototype/exec/failure-lastindex-access.js fails
built-ins/RegExp/prototype/exec/success-lastindex-access.js fails
built-ins/RegExp/prototype/source/value-line-terminator.js fails
built-ins/RegExp/prototype/test/S15.10.6.3_A1_T22.js fails
-built-ins/RegExp/u180e.js fails
built-ins/RegExp/unicode_restricted_brackets.js fails
built-ins/RegExp/unicode_restricted_character_class_escape.js fails
built-ins/RegExp/unicode_restricted_identity_escape.js fails
diff --git a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp
index 2f41e57324..9fe2de5368 100644
--- a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp
+++ b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp
@@ -509,7 +509,7 @@ static bool executeTest(const QByteArray &data, bool runAsModule = false, const
QVector<QUrl> modulesToLoad = { rootModuleUrl };
while (!modulesToLoad.isEmpty()) {
QUrl url = modulesToLoad.takeFirst();
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> module;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> module;
QFile f(url.toLocalFile());
if (f.open(QIODevice::ReadOnly)) {
diff --git a/tests/auto/qml/parserstress/dummy_imports.qml b/tests/auto/qml/parserstress/dummy_imports.qml
new file mode 100644
index 0000000000..b9a196e188
--- /dev/null
+++ b/tests/auto/qml/parserstress/dummy_imports.qml
@@ -0,0 +1,8 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in C++
+// code in tst_parserstress.cpp
+
+import QtQuick 2.0
+
+QtObject { } // This is needed in order to keep importscanner happy
diff --git a/tests/auto/qml/parserstress/tst_parserstress.cpp b/tests/auto/qml/parserstress/tst_parserstress.cpp
index e32fcabaf3..11851de76e 100644
--- a/tests/auto/qml/parserstress/tst_parserstress.cpp
+++ b/tests/auto/qml/parserstress/tst_parserstress.cpp
@@ -130,8 +130,7 @@ void tst_parserstress::ecmascript()
QCOMPARE(component.errors().at(1).line(), 142);
} else {
-
- QVERIFY(!component.isError());
+ QVERIFY2(!component.isError(), qPrintable(component.errorString()));
}
}
diff --git a/tests/auto/qml/qjsengine/dummy_imports.qml b/tests/auto/qml/qjsengine/dummy_imports.qml
new file mode 100644
index 0000000000..8d86f3583b
--- /dev/null
+++ b/tests/auto/qml/qjsengine/dummy_imports.qml
@@ -0,0 +1,8 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in C++
+// code in tst_parserstress.cpp
+
+import QtQml 2.0
+
+QtObject { } // This is needed in order to keep importscanner happy
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index cd7796827d..f1ff396d4f 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -66,6 +66,10 @@ private slots:
void newArray();
void newArray_HooliganTask218092();
void newArray_HooliganTask233836();
+ void toScriptValue_data();
+ void toScriptValue();
+ void toScriptValuenotroundtripped_data();
+ void toScriptValuenotroundtripped();
void newVariant();
void newVariant_valueOfToString();
void newVariant_valueOfEnum();
@@ -94,6 +98,7 @@ private slots:
void valueConversion_basic2();
void valueConversion_dateTime();
void valueConversion_regExp();
+ void valueConversion_RegularExpression();
void castWithMultipleInheritance();
void collectGarbage();
void gcWithNestedDataStructure();
@@ -135,6 +140,8 @@ private slots:
void qRegExpInport_data();
void qRegExpInport();
+ void qRegularExpressionImport_data();
+ void qRegularExpressionImport();
void dateRoundtripJSQtJS();
void dateRoundtripQtJSQt();
void dateConversionJSQt();
@@ -194,7 +201,9 @@ private slots:
void engineForObject();
void intConversion_QTBUG43309();
+#ifdef QT_DEPRECATED
void toFixed();
+#endif
void argumentEvaluationOrder();
@@ -239,6 +248,9 @@ private slots:
void aggressiveGc();
void noAccumulatorInTemplateLiteral();
+ void interrupt_data();
+ void interrupt();
+
void triggerBackwardJumpWithDestructuring();
public:
@@ -484,17 +496,97 @@ void tst_QJSEngine::newArray_HooliganTask233836()
}
}
+void tst_QJSEngine::toScriptValue_data()
+{
+ QTest::addColumn<QVariant>("input");
+
+ QTest::newRow("UnknownType") << QVariant(int(QMetaType::UnknownType), nullptr);
+ QTest::newRow("Nullptr") << QVariant(int(QMetaType::Nullptr), nullptr);
+ QTest::newRow("true") << QVariant(true);
+ QTest::newRow("false") << QVariant(false);
+ QTest::newRow("int") << QVariant(int(42));
+ QTest::newRow("uint") << QVariant(uint(42));
+ QTest::newRow("longlong") << QVariant(qlonglong(4242));
+ QTest::newRow("ulonglong") << QVariant(qulonglong(4242));
+ QTest::newRow("double") << QVariant(double(42.42));
+ QTest::newRow("float") << QVariant(float(42.42));
+ QTest::newRow("qstring") << QVariant(QString::fromLatin1("hello"));
+ QTest::newRow("qbytearray") << QVariant(QByteArray("hello"));
+ QTest::newRow("short") << QVariant(short('r'));
+ QTest::newRow("ushort") << QVariant(short('b'));
+ QTest::newRow("char") << QVariant(char('r'));
+ QTest::newRow("uchar") << QVariant(uchar('b'));
+ QTest::newRow("qchar") << QVariant(QString::fromUtf8("å").at(0));
+ QTest::newRow("qdate") << QVariant(QDate(1925, 5, 8));
+ QTest::newRow("qtime") << QVariant(QTime(4, 5, 6));
+ QTest::newRow("qregularexpression") << QVariant(QRegularExpression(".*"));
+ QTest::newRow("qpointf") << QVariant(QPointF(42, 24));
+ QTest::newRow("qvariantlist") << QVariant(QVariantList() << 42.24 << 5 << "hello");
+ QTest::newRow("qvariantlist_point") << QVariant(QVariantList() << 42.24 << QPointF(42.24, 24.42) << QPointF(24.42, 42.24));
+ QVariantMap vm; vm.insert("test", 55); vm.insert("abc", 42.42);;
+ QTest::newRow("qvariantmap") << QVariant(vm);
+ vm.clear(); vm.insert("point1", QPointF(42.24, 24.42)); vm.insert("point2", QPointF(42.24, 24.42));
+ QTest::newRow("qvariantmap_point") << QVariant(vm);
+ QTest::newRow("qvariant") << QVariant(QVariant(42));
+ QTest::newRow("QList<int>") << QVariant::fromValue(QList<int>() << 1 << 2 << 3 << 4);
+ QTest::newRow("QVector<int>") << QVariant::fromValue(QVector<int>() << 1 << 2 << 3 << 4);
+ QTest::newRow("QList<QString>") << QVariant::fromValue(QVector<QString>() << "1" << "2" << "3" << "4");
+ QTest::newRow("QStringList") << QVariant::fromValue(QStringList() << "1" << "2" << "3" << "4");
+ QTest::newRow("QMap<QString, QString>") << QVariant::fromValue(QMap<QString, QString>{{ "1", "2" }, { "3", "4" }});
+ QTest::newRow("QHash<QString, QString>") << QVariant::fromValue(QHash<QString, QString>{{ "1", "2" }, { "3", "4" }});
+ QTest::newRow("QMap<QString, QPointF>") << QVariant::fromValue(QMap<QString, QPointF>{{ "1", { 42.24, 24.42 } }, { "3", { 24.42, 42.24 } }});
+ QTest::newRow("QHash<QString, QPointF>") << QVariant::fromValue(QHash<QString, QPointF>{{ "1", { 42.24, 24.42 } }, { "3", { 24.42, 42.24 } }});
+}
+
+void tst_QJSEngine::toScriptValue()
+{
+ QFETCH(QVariant, input);
+
+ QJSEngine engine;
+ QJSValue outputJS = engine.toScriptValue(input);
+ QVariant output = engine.fromScriptValue<QVariant>(outputJS);
+
+ QCOMPARE(input, output);
+}
+
+void tst_QJSEngine::toScriptValuenotroundtripped_data()
+{
+ QTest::addColumn<QVariant>("input");
+ QTest::addColumn<QVariant>("output");
+
+ QTest::newRow("QList<QObject*>") << QVariant::fromValue(QList<QObject*>() << this) << QVariant(QVariantList() << QVariant::fromValue(this));
+ QTest::newRow("QObjectList") << QVariant::fromValue(QObjectList() << this) << QVariant(QVariantList() << QVariant::fromValue(this));
+ QTest::newRow("QList<QPoint>") << QVariant::fromValue(QList<QPointF>() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)) << QVariant(QVariantList() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42));
+ QTest::newRow("QVector<QPoint>") << QVariant::fromValue(QVector<QPointF>() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42)) << QVariant(QVariantList() << QPointF(42.24, 24.42) << QPointF(42.24, 24.42));
+ QTest::newRow("VoidStar") << QVariant(int(QMetaType::VoidStar), nullptr) << QVariant(int(QMetaType::Nullptr), nullptr);
+ QTest::newRow("qregex") << QVariant(QRegExp(".*", Qt::CaseSensitive, QRegExp::RegExp2)) << QVariant(QRegularExpression(".*"));
+}
+
+// This is almost the same as toScriptValue, but the inputs don't roundtrip to
+// exactly the same value.
+void tst_QJSEngine::toScriptValuenotroundtripped()
+{
+ QFETCH(QVariant, input);
+ QFETCH(QVariant, output);
+
+ QJSEngine engine;
+ QJSValue outputJS = engine.toScriptValue(input);
+ QVariant actualOutput = engine.fromScriptValue<QVariant>(outputJS);
+
+ QCOMPARE(actualOutput, output);
+}
+
void tst_QJSEngine::newVariant()
{
QJSEngine eng;
{
QJSValue opaque = eng.toScriptValue(QVariant(QPoint(1, 2)));
QVERIFY(!opaque.isUndefined());
- QCOMPARE(opaque.isVariant(), true);
+ QCOMPARE(opaque.isVariant(), false);
QVERIFY(!opaque.isCallable());
QCOMPARE(opaque.isObject(), true);
QVERIFY(!opaque.prototype().isUndefined());
- QCOMPARE(opaque.prototype().isVariant(), true);
+ QCOMPARE(opaque.prototype().isVariant(), false);
QVERIFY(opaque.property("valueOf").callWithInstance(opaque).equals(opaque));
}
}
@@ -508,7 +600,7 @@ void tst_QJSEngine::newVariant_valueOfToString()
QJSValue value = object.property("valueOf").callWithInstance(object);
QVERIFY(value.isObject());
QVERIFY(value.strictlyEquals(object));
- QCOMPARE(object.toString(), QString::fromLatin1("QVariant(QPoint, QPoint(10,20))"));
+ QCOMPARE(object.toString(), QString::fromLatin1("QPoint(10, 20)"));
}
}
@@ -526,22 +618,27 @@ void tst_QJSEngine::newVariant_valueOfEnum()
void tst_QJSEngine::newRegExp()
{
QJSEngine eng;
- QJSValue rexp = eng.toScriptValue(QRegExp("foo"));
- QVERIFY(!rexp.isUndefined());
- QCOMPARE(rexp.isRegExp(), true);
- QCOMPARE(rexp.isObject(), true);
- QCOMPARE(rexp.isCallable(), false);
- // prototype should be RegExp.prototype
- QVERIFY(!rexp.prototype().isUndefined());
- QCOMPARE(rexp.prototype().isObject(), true);
- // Get [[Class]] internal property of RegExp Prototype Object.
- // See ECMA-262 Section 8.6.2, "Object Internal Properties and Methods".
- // See ECMA-262 Section 15.10.6, "Properties of the RegExp Prototype Object".
- QJSValue r = eng.evaluate("Object.prototype.toString.call(RegExp.prototype)");
- QCOMPARE(r.toString(), QString::fromLatin1("[object Object]"));
- QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true);
+ QJSValue rexps[] = {
+ eng.toScriptValue(QRegularExpression("foo")),
+ eng.toScriptValue(QRegExp("foo"))
+ };
+ for (const auto &rexp : rexps) {
+ QVERIFY(!rexp.isUndefined());
+ QCOMPARE(rexp.isRegExp(), true);
+ QCOMPARE(rexp.isObject(), true);
+ QCOMPARE(rexp.isCallable(), false);
+ // prototype should be RegExp.prototype
+ QVERIFY(!rexp.prototype().isUndefined());
+ QCOMPARE(rexp.prototype().isObject(), true);
+ // Get [[Class]] internal property of RegExp Prototype Object.
+ // See ECMA-262 Section 8.6.2, "Object Internal Properties and Methods".
+ // See ECMA-262 Section 15.10.6, "Properties of the RegExp Prototype Object".
+ QJSValue r = eng.evaluate("Object.prototype.toString.call(RegExp.prototype)");
+ QCOMPARE(r.toString(), QString::fromLatin1("[object Object]"));
+ QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true);
- QCOMPARE(qjsvalue_cast<QRegExp>(rexp).pattern(), QRegExp("foo").pattern());
+ QCOMPARE(qjsvalue_cast<QRegExp>(rexp).pattern(), QRegExp("foo").pattern());
+ }
}
void tst_QJSEngine::jsRegExp()
@@ -1351,12 +1448,10 @@ public:
Q_DECLARE_METATYPE(Foo)
Q_DECLARE_METATYPE(Foo*)
-Q_DECLARE_METATYPE(QLinkedList<QString>)
Q_DECLARE_METATYPE(QList<Foo>)
Q_DECLARE_METATYPE(QVector<QChar>)
Q_DECLARE_METATYPE(QStack<int>)
Q_DECLARE_METATYPE(QQueue<char>)
-Q_DECLARE_METATYPE(QLinkedList<QStack<int> >)
void tst_QJSEngine::valueConversion_basic()
{
@@ -1493,15 +1588,15 @@ void tst_QJSEngine::valueConversion_QVariant()
QCOMPARE(val.toString(), str);
}
{
- QJSValue val = eng.toScriptValue(qVariantFromValue((QObject*)this));
+ QJSValue val = eng.toScriptValue(QVariant::fromValue((QObject*)this));
QVERIFY(!val.isVariant());
QVERIFY(val.isQObject());
QCOMPARE(val.toQObject(), (QObject*)this);
}
{
- QVariant var = qVariantFromValue(QPoint(123, 456));
+ QVariant var = QVariant::fromValue(QPoint(123, 456));
QJSValue val = eng.toScriptValue(var);
- QVERIFY(val.isVariant());
+ QVERIFY(!val.isVariant());
QCOMPARE(val.toVariant(), var);
}
@@ -1607,6 +1702,28 @@ void tst_QJSEngine::valueConversion_regExp()
}
}
+void tst_QJSEngine::valueConversion_RegularExpression()
+{
+ QJSEngine eng;
+ {
+ QRegularExpression in = QRegularExpression("foo");
+ QJSValue val = eng.toScriptValue(in);
+ QVERIFY(val.isRegExp());
+ QRegularExpression out = qjsvalue_cast<QRegularExpression>(val);
+ QCOMPARE(out.pattern(), in.pattern());
+ QCOMPARE(out.patternOptions(), in.patternOptions());
+ }
+ {
+ QRegularExpression in = QRegularExpression("foo",
+ QRegularExpression::CaseInsensitiveOption);
+ QJSValue val = eng.toScriptValue(in);
+ QVERIFY(val.isRegExp());
+ QCOMPARE(qjsvalue_cast<QRegularExpression>(val), in);
+ QRegularExpression out = qjsvalue_cast<QRegularExpression>(val);
+ QCOMPARE(out.patternOptions(), in.patternOptions());
+ }
+}
+
Q_DECLARE_METATYPE(QGradient)
Q_DECLARE_METATYPE(QGradient*)
Q_DECLARE_METATYPE(QLinearGradient)
@@ -2956,6 +3073,8 @@ void tst_QJSEngine::reentrancy_objectCreation()
QJSValue r2 = eng2.evaluate("new RegExp('foo', 'gim')");
QCOMPARE(qjsvalue_cast<QRegExp>(r1), qjsvalue_cast<QRegExp>(r2));
QCOMPARE(qjsvalue_cast<QRegExp>(r2), qjsvalue_cast<QRegExp>(r1));
+ QCOMPARE(qjsvalue_cast<QRegularExpression>(r1), qjsvalue_cast<QRegularExpression>(r2));
+ QCOMPARE(qjsvalue_cast<QRegularExpression>(r2), qjsvalue_cast<QRegularExpression>(r1));
}
{
QJSValue o1 = eng1.newQObject(temp);
@@ -3151,6 +3270,56 @@ void tst_QJSEngine::qRegExpInport()
}
}
+void tst_QJSEngine::qRegularExpressionImport_data()
+{
+ QTest::addColumn<QRegularExpression>("rx");
+ QTest::addColumn<QString>("string");
+ QTest::addColumn<QString>("matched");
+
+ QTest::newRow("normal") << QRegularExpression("(test|foo)") << "test _ foo _ test _ Foo";
+ QTest::newRow("normal2") << QRegularExpression("(Test|Foo)") << "test _ foo _ test _ Foo";
+ QTest::newRow("case insensitive") << QRegularExpression("(test|foo)", QRegularExpression::CaseInsensitiveOption) << "test _ foo _ test _ Foo";
+ QTest::newRow("case insensitive2") << QRegularExpression("(Test|Foo)", QRegularExpression::CaseInsensitiveOption) << "test _ foo _ test _ Foo";
+ QTest::newRow("b(a*)(b*)") << QRegularExpression("b(a*)(b*)", QRegularExpression::CaseInsensitiveOption) << "aaabbBbaAabaAaababaaabbaaab";
+ QTest::newRow("greedy") << QRegularExpression("a*(a*)", QRegularExpression::CaseInsensitiveOption) << "aaaabaaba";
+ QTest::newRow("wildcard") << QRegularExpression(".*\\.txt") << "file.txt";
+ QTest::newRow("wildcard 2") << QRegularExpression("a.b\\.txt") << "ab.txt abb.rtc acb.txt";
+ QTest::newRow("slash") << QRegularExpression("g/.*/s", QRegularExpression::CaseInsensitiveOption) << "string/string/string";
+ QTest::newRow("slash2") << QRegularExpression("g / .* / s", QRegularExpression::CaseInsensitiveOption) << "string / string / string";
+ QTest::newRow("fixed") << QRegularExpression("a\\*aa\\.a\\(ba\\)\\*a\\\\ba", QRegularExpression::CaseInsensitiveOption) << "aa*aa.a(ba)*a\\ba";
+ QTest::newRow("fixed insensitive") << QRegularExpression("A\\*A", QRegularExpression::CaseInsensitiveOption) << "a*A A*a A*A a*a";
+ QTest::newRow("fixed sensitive") << QRegularExpression("A\\*A") << "a*A A*a A*A a*a";
+ QTest::newRow("html") << QRegularExpression("<b>(.*)</b>") << "<b>bold</b><i>italic</i><b>bold</b>";
+ QTest::newRow("html minimal") << QRegularExpression("^<b>(.*)</b>$") << "<b>bold</b><i>italic</i><b>bold</b>";
+ QTest::newRow("aaa") << QRegularExpression("a{2,5}") << "aAaAaaaaaAa";
+ QTest::newRow("aaa minimal") << QRegularExpression("^a{2,5}$") << "aAaAaaaaaAa";
+ QTest::newRow("minimal") << QRegularExpression("^.*\\} [*8]$") << "}?} ?} *";
+ QTest::newRow(".? minimal") << QRegularExpression("^.?$") << ".?";
+ QTest::newRow(".+ minimal") << QRegularExpression("^.+$") << ".+";
+ QTest::newRow("[.?] minimal") << QRegularExpression("^[.?]$") << ".?";
+ QTest::newRow("[.+] minimal") << QRegularExpression("^[.+]$") << ".+";
+}
+
+void tst_QJSEngine::qRegularExpressionImport()
+{
+ QFETCH(QRegularExpression, rx);
+ QFETCH(QString, string);
+
+ QJSEngine eng;
+ QJSValue rexp;
+ rexp = eng.toScriptValue(rx);
+
+ QCOMPARE(rexp.isRegExp(), true);
+ QCOMPARE(rexp.isCallable(), false);
+
+ QJSValue func = eng.evaluate("(function(string, regexp) { return string.match(regexp); })");
+ QJSValue result = func.call(QJSValueList() << string << rexp);
+
+ const QRegularExpressionMatch match = rx.match(string);
+ for (int i = 0; i <= match.lastCapturedIndex(); i++)
+ QCOMPARE(result.property(i).toString(), match.captured(i));
+}
+
// QScriptValue::toDateTime() returns a local time, whereas JS dates
// are always stored as UTC. Qt Script must respect the current time
// zone, and correctly adjust for daylight saving time that may be in
@@ -4156,7 +4325,10 @@ void tst_QJSEngine::engineForObject()
QVERIFY(!qjsEngine(&object));
QJSValue wrapper = engine.newQObject(&object);
QQmlEngine::setObjectOwnership(&object, QQmlEngine::CppOwnership);
+ QVERIFY(qjsEngine(&object));
+#ifdef QT_DEPRECATED
QCOMPARE(qjsEngine(&object), wrapper.engine());
+#endif
}
QVERIFY(!qjsEngine(&object));
}
@@ -4171,6 +4343,7 @@ void tst_QJSEngine::intConversion_QTBUG43309()
QCOMPARE(result.toNumber(), 25.0);
}
+#ifdef QT_DEPRECATED
// QTBUG-44039 and QTBUG-43885:
void tst_QJSEngine::toFixed()
{
@@ -4182,6 +4355,7 @@ void tst_QJSEngine::toFixed()
QVERIFY(result.isString());
QCOMPARE(result.toString(), QStringLiteral("12.1"));
}
+#endif
void tst_QJSEngine::argumentEvaluationOrder()
{
@@ -4693,6 +4867,87 @@ void tst_QJSEngine::noAccumulatorInTemplateLiteral()
qputenv("QV4_MM_AGGRESSIVE_GC", origAggressiveGc);
}
+void tst_QJSEngine::interrupt_data()
+{
+ QTest::addColumn<int>("jitThreshold");
+ QTest::addColumn<QString>("code");
+
+ const int big = (1 << 24);
+ for (int i = 0; i <= big; i += big) {
+ const char *mode = i ? "interpret" : "jit";
+ QTest::addRow("for with content / %s", mode) << i << "var a = 0; for (;;) { a += 2; }";
+ QTest::addRow("for empty / %s", mode) << i << "for (;;) {}";
+ QTest::addRow("for continue / %s", mode) << i << "for (;;) { continue; }";
+ QTest::addRow("while with content / %s", mode) << i << "var a = 0; while (true) { a += 2; }";
+ QTest::addRow("while empty / %s", mode) << i << "while (true) {}";
+ QTest::addRow("while continue / %s", mode) << i << "while (true) { continue; }";
+ QTest::addRow("do with content / %s", mode) << i << "var a = 0; do { a += 2; } while (true);";
+ QTest::addRow("do empty / %s", mode) << i << "do {} while (true);";
+ QTest::addRow("do continue / %s", mode) << i << "do { continue; } while (true);";
+ QTest::addRow("nested loops / %s", mode) << i << "while (true) { for (;;) {} }";
+ QTest::addRow("labeled continue / %s", mode) << i << "a: while (true) { for (;;) { continue a; } }";
+ QTest::addRow("labeled break / %s", mode) << i << "while (true) { a: for (;;) { break a; } }";
+ QTest::addRow("tail call / %s", mode) << i << "'use strict';\nfunction x() { return x(); }; x();";
+ QTest::addRow("huge array join / %s", mode) << i << "Array(1E9)|1";
+ }
+}
+
+class TemporaryJitThreshold
+{
+ Q_DISABLE_COPY_MOVE(TemporaryJitThreshold)
+public:
+ TemporaryJitThreshold(int threshold) {
+ m_wasSet = qEnvironmentVariableIsSet(m_envVar);
+ m_value = qgetenv(m_envVar);
+ qputenv(m_envVar, QByteArray::number(threshold));
+ }
+
+ ~TemporaryJitThreshold()
+ {
+ if (m_wasSet)
+ qputenv(m_envVar, m_value);
+ else
+ qunsetenv(m_envVar);
+ }
+
+private:
+ const char *m_envVar = "QV4_JIT_CALL_THRESHOLD";
+ bool m_wasSet = false;
+ QByteArray m_value;
+};
+
+void tst_QJSEngine::interrupt()
+{
+#if QT_CONFIG(cxx11_future)
+ QFETCH(int, jitThreshold);
+ QFETCH(QString, code);
+
+ TemporaryJitThreshold threshold(jitThreshold);
+ Q_UNUSED(threshold);
+
+ QJSEngine *engineInThread = nullptr;
+ QScopedPointer<QThread> worker(QThread::create([&engineInThread, &code, jitThreshold](){
+ QJSEngine jsEngine;
+ engineInThread = &jsEngine;
+ QJSValue result = jsEngine.evaluate(code);
+ QVERIFY(jsEngine.isInterrupted());
+ QVERIFY(result.isError());
+ QCOMPARE(result.toString(), QString::fromLatin1("Error: Interrupted"));
+ engineInThread = nullptr;
+ }));
+ worker->start();
+
+ QTRY_VERIFY(engineInThread);
+
+ engineInThread->setInterrupted(true);
+
+ QVERIFY(worker->wait());
+ QVERIFY(!engineInThread);
+#else
+ QSKIP("This test requires C++11 futures");
+#endif
+}
+
void tst_QJSEngine::triggerBackwardJumpWithDestructuring()
{
QJSEngine engine;
diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
index a34a9e5188..37d0ea4dea 100644
--- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
+++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
@@ -52,7 +52,9 @@ void tst_QJSValue::ctor_invalid()
{
QJSValue v;
QVERIFY(v.isUndefined());
+#ifdef QT_DEPRECATED
QCOMPARE(v.engine(), (QJSEngine *)nullptr);
+#endif
}
}
@@ -63,7 +65,9 @@ void tst_QJSValue::ctor_undefinedWithEngine()
QJSValue v = eng.toScriptValue(QVariant());
QVERIFY(v.isUndefined());
QCOMPARE(v.isObject(), false);
+#ifdef QT_DEPRECATED
QCOMPARE(v.engine(), &eng);
+#endif
}
}
@@ -75,7 +79,9 @@ void tst_QJSValue::ctor_nullWithEngine()
QVERIFY(!v.isUndefined());
QCOMPARE(v.isNull(), true);
QCOMPARE(v.isObject(), false);
+#ifdef QT_DEPRECATED
QCOMPARE(v.engine(), &eng);
+#endif
}
}
@@ -88,7 +94,9 @@ void tst_QJSValue::ctor_boolWithEngine()
QCOMPARE(v.isBool(), true);
QCOMPARE(v.isObject(), false);
QCOMPARE(v.toBool(), false);
+#ifdef QT_DEPRECATED
QCOMPARE(v.engine(), &eng);
+#endif
}
}
@@ -101,7 +109,9 @@ void tst_QJSValue::ctor_intWithEngine()
QCOMPARE(v.isNumber(), true);
QCOMPARE(v.isObject(), false);
QCOMPARE(v.toNumber(), 1.0);
+#ifdef QT_DEPRECATED
QCOMPARE(v.engine(), &eng);
+#endif
}
}
@@ -118,7 +128,9 @@ void tst_QJSValue::ctor_int()
QCOMPARE(v.isNumber(), true);
QCOMPARE(v.isObject(), false);
QCOMPARE(v.toNumber(), 1.0);
+#ifdef QT_DEPRECATED
QCOMPARE(v.engine(), (QJSEngine *)nullptr);
+#endif
}
}
@@ -131,7 +143,9 @@ void tst_QJSValue::ctor_uintWithEngine()
QCOMPARE(v.isNumber(), true);
QCOMPARE(v.isObject(), false);
QCOMPARE(v.toNumber(), 1.0);
+#ifdef QT_DEPRECATED
QCOMPARE(v.engine(), &eng);
+#endif
}
}
@@ -148,7 +162,9 @@ void tst_QJSValue::ctor_uint()
QCOMPARE(v.isNumber(), true);
QCOMPARE(v.isObject(), false);
QCOMPARE(v.toNumber(), 1.0);
+#ifdef QT_DEPRECATED
QCOMPARE(v.engine(), (QJSEngine *)nullptr);
+#endif
}
}
@@ -161,7 +177,9 @@ void tst_QJSValue::ctor_floatWithEngine()
QCOMPARE(v.isNumber(), true);
QCOMPARE(v.isObject(), false);
QCOMPARE(v.toNumber(), 1.0);
+#ifdef QT_DEPRECATED
QCOMPARE(v.engine(), &eng);
+#endif
}
}
@@ -178,7 +196,9 @@ void tst_QJSValue::ctor_float()
QCOMPARE(v.isNumber(), true);
QCOMPARE(v.isObject(), false);
QCOMPARE(v.toNumber(), 1.0);
+#ifdef QT_DEPRECATED
QCOMPARE(v.engine(), (QJSEngine *)nullptr);
+#endif
}
}
@@ -191,7 +211,9 @@ void tst_QJSValue::ctor_stringWithEngine()
QCOMPARE(v.isString(), true);
QCOMPARE(v.isObject(), false);
QCOMPARE(v.toString(), QLatin1String("ciao"));
+#ifdef QT_DEPRECATED
QCOMPARE(v.engine(), &eng);
+#endif
}
}
@@ -203,7 +225,9 @@ void tst_QJSValue::ctor_string()
QCOMPARE(v.isString(), true);
QCOMPARE(v.isObject(), false);
QCOMPARE(v.toString(), QLatin1String("ciao"));
+#ifdef QT_DEPRECATED
QCOMPARE(v.engine(), (QJSEngine *)nullptr);
+#endif
}
{
QJSValue v("ciao");
@@ -211,7 +235,9 @@ void tst_QJSValue::ctor_string()
QCOMPARE(v.isString(), true);
QCOMPARE(v.isObject(), false);
QCOMPARE(v.toString(), QLatin1String("ciao"));
+#ifdef QT_DEPRECATED
QCOMPARE(v.engine(), (QJSEngine *)nullptr);
+#endif
}
}
@@ -223,12 +249,16 @@ void tst_QJSValue::ctor_copyAndAssignWithEngine()
QJSValue v = eng.toScriptValue(1.0);
QJSValue v2(v);
QCOMPARE(v2.strictlyEquals(v), true);
+#ifdef QT_DEPRECATED
QCOMPARE(v2.engine(), &eng);
+#endif
QJSValue v3(v);
QCOMPARE(v3.strictlyEquals(v), true);
QCOMPARE(v3.strictlyEquals(v2), true);
+#ifdef QT_DEPRECATED
QCOMPARE(v3.engine(), &eng);
+#endif
QJSValue v4 = eng.toScriptValue(2.0);
QCOMPARE(v4.strictlyEquals(v), false);
@@ -253,7 +283,9 @@ void tst_QJSValue::ctor_undefined()
QJSValue v(QJSValue::UndefinedValue);
QVERIFY(v.isUndefined());
QCOMPARE(v.isObject(), false);
+#ifdef QT_DEPRECATED
QCOMPARE(v.engine(), (QJSEngine *)nullptr);
+#endif
}
void tst_QJSValue::ctor_null()
@@ -262,7 +294,9 @@ void tst_QJSValue::ctor_null()
QVERIFY(!v.isUndefined());
QCOMPARE(v.isNull(), true);
QCOMPARE(v.isObject(), false);
+#ifdef QT_DEPRECATED
QCOMPARE(v.engine(), (QJSEngine *)nullptr);
+#endif
}
void tst_QJSValue::ctor_bool()
@@ -273,7 +307,9 @@ void tst_QJSValue::ctor_bool()
QCOMPARE(v.isBool(), true);
QCOMPARE(v.isObject(), false);
QCOMPARE(v.toBool(), false);
+#ifdef QT_DEPRECATED
QCOMPARE(v.engine(), (QJSEngine *)nullptr);
+#endif
}
void tst_QJSValue::ctor_copyAndAssign()
@@ -281,12 +317,16 @@ void tst_QJSValue::ctor_copyAndAssign()
QJSValue v(1.0);
QJSValue v2(v);
QCOMPARE(v2.strictlyEquals(v), true);
+#ifdef QT_DEPRECATED
QCOMPARE(v2.engine(), (QJSEngine *)nullptr);
+#endif
QJSValue v3(v);
QCOMPARE(v3.strictlyEquals(v), true);
QCOMPARE(v3.strictlyEquals(v2), true);
+#ifdef QT_DEPRECATED
QCOMPARE(v3.engine(), (QJSEngine *)nullptr);
+#endif
QJSValue v4(2.0);
QCOMPARE(v4.strictlyEquals(v), false);
@@ -411,8 +451,8 @@ void tst_QJSValue::toString()
// variant should use internal valueOf(), then fall back to QVariant::toString(),
// then fall back to "QVariant(typename)"
QJSValue variant = eng.toScriptValue(QPoint(10, 20));
- QVERIFY(variant.isVariant());
- QCOMPARE(variant.toString(), QString::fromLatin1("QVariant(QPoint, QPoint(10,20))"));
+ QVERIFY(!variant.isVariant());
+ QCOMPARE(variant.toString(), QString::fromLatin1("QPoint(10, 20)"));
variant = eng.toScriptValue(QUrl());
QVERIFY(variant.isVariant());
QVERIFY(variant.toString().isEmpty());
@@ -423,7 +463,9 @@ void tst_QJSValue::toString()
QCOMPARE(o.toString(), QStringLiteral("[object Object]"));
o = createUnboundValue(o);
+#ifdef QT_DEPRECATED
QVERIFY(!o.engine());
+#endif
QCOMPARE(o.toString(), QStringLiteral("[object Object]"));
}
@@ -435,7 +477,9 @@ void tst_QJSValue::toString()
QCOMPARE(o.toString(), QStringLiteral("1,2,3"));
o = createUnboundValue(o);
+#ifdef QT_DEPRECATED
QVERIFY(!o.engine());
+#endif
QCOMPARE(o.toString(), QStringLiteral("1,2,3"));
}
@@ -1034,6 +1078,20 @@ void tst_QJSValue::toVariant()
QJSValue rxObject = eng.toScriptValue(rx);
QVERIFY(rxObject.isRegExp());
QVariant var = rxObject.toVariant();
+
+ // We can't roundtrip a QRegExp this way, as toVariant() has no information on whether we
+ // want QRegExp or QRegularExpression. It will always create a QRegularExpression.
+ QCOMPARE(var.type(), QMetaType::QRegularExpression);
+ QRegularExpression result = var.toRegularExpression();
+ QCOMPARE(result.pattern(), rx.pattern());
+ QCOMPARE(result.patternOptions() & QRegularExpression::CaseInsensitiveOption, 0);
+ }
+
+ {
+ QRegularExpression rx = QRegularExpression("[0-9a-z]+");
+ QJSValue rxObject = eng.toScriptValue(rx);
+ QVERIFY(rxObject.isRegExp());
+ QVariant var = rxObject.toVariant();
QCOMPARE(var, QVariant(rx));
}
@@ -1114,7 +1172,7 @@ void tst_QJSValue::toQObject_nonQObject_data()
QTest::newRow("array") << engine->newArray();
QTest::newRow("date") << engine->evaluate("new Date(124)");
QTest::newRow("variant(12345)") << engine->toScriptValue(QVariant(12345));
- QTest::newRow("variant((QObject*)0)") << engine->toScriptValue(qVariantFromValue((QObject*)nullptr));
+ QTest::newRow("variant((QObject*)0)") << engine->toScriptValue(QVariant::fromValue((QObject*)nullptr));
QTest::newRow("newQObject(0)") << engine->newQObject(nullptr);
}
@@ -1201,6 +1259,32 @@ void tst_QJSValue::toRegExp()
QVERIFY(qjsvalue_cast<QRegExp>(eng.toScriptValue(QVariant())).isEmpty());
}
+void tst_QJSValue::toRegularExpression()
+{
+ QJSEngine eng;
+ {
+ QRegularExpression rx = qjsvalue_cast<QRegularExpression>(eng.evaluate("/foo/"));
+ QVERIFY(rx.isValid());
+ QCOMPARE(rx.pattern(), QString::fromLatin1("foo"));
+ QVERIFY(!(rx.patternOptions() & QRegularExpression::CaseInsensitiveOption));
+ }
+ {
+ QRegularExpression rx = qjsvalue_cast<QRegularExpression>(eng.evaluate("/bar/gi"));
+ QVERIFY(rx.isValid());
+ QCOMPARE(rx.pattern(), QString::fromLatin1("bar"));
+ QVERIFY(rx.patternOptions() & QRegularExpression::CaseInsensitiveOption);
+ }
+
+ QVERIFY(qjsvalue_cast<QRegularExpression>(eng.evaluate("[]")).pattern().isEmpty());
+ QVERIFY(qjsvalue_cast<QRegularExpression>(eng.evaluate("{}")).pattern().isEmpty());
+ QVERIFY(qjsvalue_cast<QRegularExpression>(eng.globalObject()).pattern().isEmpty());
+ QVERIFY(qjsvalue_cast<QRegularExpression>(QJSValue()).pattern().isEmpty());
+ QVERIFY(qjsvalue_cast<QRegularExpression>(QJSValue(123)).pattern().isEmpty());
+ QVERIFY(qjsvalue_cast<QRegularExpression>(QJSValue(false)).pattern().isEmpty());
+ QVERIFY(qjsvalue_cast<QRegularExpression>(eng.evaluate("null")).pattern().isEmpty());
+ QVERIFY(qjsvalue_cast<QRegularExpression>(eng.toScriptValue(QVariant())).pattern().isEmpty());
+}
+
void tst_QJSValue::isArray_data()
{
newEngine();
@@ -1601,10 +1685,14 @@ void tst_QJSValue::getSetProperty()
QCOMPARE(object.property("baz").toNumber(), num.toNumber());
QJSValue strstr = QJSValue("bar");
+#ifdef QT_DEPRECATED
QCOMPARE(strstr.engine(), (QJSEngine *)nullptr);
+#endif
object.setProperty("foo", strstr);
QCOMPARE(object.property("foo").toString(), strstr.toString());
+#ifdef QT_DEPRECATED
QCOMPARE(strstr.engine(), &eng); // the value has been bound to the engine
+#endif
QJSValue numnum = QJSValue(123.0);
object.setProperty("baz", numnum);
@@ -2255,8 +2343,8 @@ void tst_QJSValue::strictlyEquals()
{
QJSValue var1 = eng.toScriptValue(QVariant(QStringList() << "a"));
QJSValue var2 = eng.toScriptValue(QVariant(QStringList() << "a"));
- QVERIFY(var1.isArray());
- QVERIFY(var2.isArray());
+ QVERIFY(!var1.isArray());
+ QVERIFY(!var2.isArray());
QVERIFY(!var1.strictlyEquals(var2));
}
{
@@ -2294,7 +2382,7 @@ void tst_QJSValue::castToPointer()
QBrush *bp = qjsvalue_cast<QBrush*>(v);
QVERIFY(!bp);
- QJSValue v2 = eng.toScriptValue(qVariantFromValue(cp));
+ QJSValue v2 = eng.toScriptValue(QVariant::fromValue(cp));
QCOMPARE(qjsvalue_cast<QColor*>(v2), cp);
}
}
@@ -2496,15 +2584,18 @@ void tst_QJSValue::engineDeleted()
delete eng;
QVERIFY(v1.isUndefined());
- QVERIFY(!v1.engine());
QVERIFY(v2.isUndefined());
- QVERIFY(!v2.engine());
QVERIFY(v3.isUndefined());
- QVERIFY(!v3.engine());
QVERIFY(v4.isUndefined());
- QVERIFY(!v4.engine());
QVERIFY(v5.isString()); // was not bound to engine
+
+#ifdef QT_DEPRECATED
+ QVERIFY(!v1.engine());
+ QVERIFY(!v2.engine());
+ QVERIFY(!v3.engine());
+ QVERIFY(!v4.engine());
QVERIFY(!v5.engine());
+#endif
QVERIFY(v3.property("foo").isUndefined());
}
diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.h b/tests/auto/qml/qjsvalue/tst_qjsvalue.h
index ccbacb3acc..f704169d43 100644
--- a/tests/auto/qml/qjsvalue/tst_qjsvalue.h
+++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.h
@@ -74,6 +74,7 @@ private slots:
void toQObject();
void toDateTime();
void toRegExp();
+ void toRegularExpression();
void isArray_data();
void isArray();
void isDate();
diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro
index 5448088ee5..db9bb52010 100644
--- a/tests/auto/qml/qml.pro
+++ b/tests/auto/qml/qml.pro
@@ -70,6 +70,7 @@ PRIVATETESTS += \
qqmltranslation \
qqmlimport \
qqmlobjectmodel \
+ qqmltablemodel \
qv4assembler \
qv4mm \
qv4identifiertable \
@@ -104,7 +105,3 @@ qtConfig(private_tests): \
qtNomakeTools( \
qmlplugindump \
)
-
-QtConfig(qml_tracing) {
- PRIVATETESTS += v4traced
-}
diff --git a/tests/auto/qml/qmlcachegen/data/parameterAdjustment.qml b/tests/auto/qml/qmlcachegen/data/parameterAdjustment.qml
new file mode 100644
index 0000000000..2128a54d81
--- /dev/null
+++ b/tests/auto/qml/qmlcachegen/data/parameterAdjustment.qml
@@ -0,0 +1,7 @@
+import QtQml 2.12
+
+QtObject {
+ signal testSignal(string a, int b, string c, bool d, bool e, real f, real g, bool h, int i, int j, string k, int l, string m, string n)
+ onTestSignal: {}
+ Component.onCompleted: testSignal("a", 1, "b", true, true, 0.1, 0.1, true, 1, 1, "a", 1, "a", "a")
+}
diff --git a/tests/auto/qml/qmlcachegen/qmlcachegen.pro b/tests/auto/qml/qmlcachegen/qmlcachegen.pro
index 53b26ccfae..4daf1d35c3 100644
--- a/tests/auto/qml/qmlcachegen/qmlcachegen.pro
+++ b/tests/auto/qml/qmlcachegen/qmlcachegen.pro
@@ -17,7 +17,8 @@ RESOURCES += \
data/jsmoduleimport.qml \
data/script.mjs \
data/module.mjs \
- data/utils.mjs
+ data/utils.mjs \
+ data/parameterAdjustment.qml
workerscripts_test.files = \
data/worker.js \
diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
index 741989e732..4a1f5378a6 100644
--- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
+++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
@@ -36,6 +36,7 @@
#include <QSysInfo>
#include <QLoggingCategory>
#include <private/qqmlcomponent_p.h>
+#include <private/qqmlscriptdata_p.h>
#include <qtranslator.h>
#include "../../shared/util.h"
@@ -72,6 +73,8 @@ private slots:
void reproducibleCache_data();
void reproducibleCache();
+
+ void parameterAdjustment();
};
// A wrapper around QQmlComponent to ensure the temporary reference counts
@@ -669,6 +672,14 @@ void tst_qmlcachegen::reproducibleCache()
QCOMPARE(contents1, contents2);
}
+void tst_qmlcachegen::parameterAdjustment()
+{
+ QQmlEngine engine;
+ CleanlyLoadingComponent component(&engine, QUrl("qrc:///data/parameterAdjustment.qml"));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull()); // Doesn't crash
+}
+
QTEST_GUILESS_MAIN(tst_qmlcachegen)
#include "tst_qmlcachegen.moc"
diff --git a/tests/auto/qml/qmldiskcache/dummy_imports.qml b/tests/auto/qml/qmldiskcache/dummy_imports.qml
new file mode 100644
index 0000000000..b9a196e188
--- /dev/null
+++ b/tests/auto/qml/qmldiskcache/dummy_imports.qml
@@ -0,0 +1,8 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in C++
+// code in tst_parserstress.cpp
+
+import QtQuick 2.0
+
+QtObject { } // This is needed in order to keep importscanner happy
diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
index 70a5a73e0f..1f0115b926 100644
--- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
+++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
@@ -30,10 +30,11 @@
#include <private/qv4compileddata_p.h>
#include <private/qv4compiler_p.h>
-#include <private/qv8engine_p.h>
#include <private/qv4engine_p.h>
#include <private/qv4codegen_p.h>
#include <private/qqmlcomponent_p.h>
+#include <private/qv4executablecompilationunit_p.h>
+#include <private/qqmlscriptdata_p.h>
#include <QQmlComponent>
#include <QQmlEngine>
#include <QQmlFileSelector>
@@ -119,7 +120,8 @@ struct TestCompiler
{
closeMapping();
testFilePath = baseDirectory + QStringLiteral("/test.qml");
- cacheFilePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath));
+ cacheFilePath = QV4::ExecutableCompilationUnit::localCacheFilePath(
+ QUrl::fromLocalFile(testFilePath));
mappedFile.setFileName(cacheFilePath);
}
@@ -186,8 +188,10 @@ struct TestCompiler
bool verify()
{
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading();
- return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath), QFileInfo(testFilePath).lastModified(), &lastErrorString);
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> unit
+ = QV4::ExecutableCompilationUnit::create();
+ return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath),
+ QFileInfo(testFilePath).lastModified(), &lastErrorString);
}
void closeMapping()
@@ -264,8 +268,9 @@ void tst_qmldiskcache::loadLocalAsFallback()
f.write(reinterpret_cast<const char *>(&unit), sizeof(unit));
}
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading();
- bool loaded = unit->loadFromDisk(QUrl::fromLocalFile(testCompiler.testFilePath), QFileInfo(testCompiler.testFilePath).lastModified(),
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> unit = QV4::ExecutableCompilationUnit::create();
+ bool loaded = unit->loadFromDisk(QUrl::fromLocalFile(testCompiler.testFilePath),
+ QFileInfo(testCompiler.testFilePath).lastModified(),
&testCompiler.lastErrorString);
QVERIFY2(loaded, qPrintable(testCompiler.lastErrorString));
QCOMPARE(unit->objectCount(), 1);
@@ -324,7 +329,10 @@ void tst_qmldiskcache::regenerateAfterChange()
const QV4::CompiledData::Object *obj = qmlUnit->objectAt(0);
QCOMPARE(quint32(obj->nBindings), quint32(2));
QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Number));
- QCOMPARE(obj->bindingTable()->valueAsNumber(reinterpret_cast<const QV4::Value *>(testUnit->constants())), double(42));
+
+ QCOMPARE(reinterpret_cast<const QV4::Value *>(testUnit->constants())
+ [obj->bindingTable()->value.constantValueIndex].doubleValue(),
+ double(42));
QCOMPARE(quint32(testUnit->functionTableSize), quint32(1));
@@ -573,7 +581,8 @@ void tst_qmldiskcache::fileSelectors()
QVERIFY(!obj.isNull());
QCOMPARE(obj->property("value").toInt(), 42);
- QFile cacheFile(QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath)));
+ QFile cacheFile(QV4::ExecutableCompilationUnit::localCacheFilePath(
+ QUrl::fromLocalFile(testFilePath)));
QVERIFY2(cacheFile.exists(), qPrintable(cacheFile.fileName()));
}
@@ -588,7 +597,8 @@ void tst_qmldiskcache::fileSelectors()
QVERIFY(!obj.isNull());
QCOMPARE(obj->property("value").toInt(), 100);
- QFile cacheFile(QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(selectedTestFilePath)));
+ QFile cacheFile(QV4::ExecutableCompilationUnit::localCacheFilePath(
+ QUrl::fromLocalFile(selectedTestFilePath)));
QVERIFY2(cacheFile.exists(), qPrintable(cacheFile.fileName()));
}
}
@@ -636,10 +646,21 @@ void tst_qmldiskcache::localAliases()
}
}
+static QSet<QString> entrySet(const QDir &dir)
+{
+ const auto &list = dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
+ return QSet<QString>(list.cbegin(), list.cend());
+}
+
+static QSet<QString> entrySet(const QDir &dir, const QStringList &filters)
+{
+ const auto &list = dir.entryList(filters);
+ return QSet<QString>(list.cbegin(), list.cend());
+}
+
void tst_qmldiskcache::cacheResources()
{
- const QSet<QString> existingFiles =
- m_qmlCacheDirectory.entryList(QDir::Files | QDir::NoDotAndDotDot).toSet();
+ const QSet<QString> existingFiles = entrySet(m_qmlCacheDirectory);
QQmlEngine engine;
@@ -650,8 +671,7 @@ void tst_qmldiskcache::cacheResources()
QCOMPARE(obj->property("value").toInt(), 20);
}
- const QSet<QString> entries =
- m_qmlCacheDirectory.entryList(QDir::NoDotAndDotDot | QDir::Files).toSet().subtract(existingFiles);
+ const QSet<QString> entries = entrySet(m_qmlCacheDirectory).subtract(existingFiles);
QCOMPARE(entries.count(), 1);
QDateTime cacheFileTimeStamp;
@@ -680,8 +700,7 @@ void tst_qmldiskcache::cacheResources()
}
{
- const QSet<QString> entries =
- m_qmlCacheDirectory.entryList(QDir::NoDotAndDotDot | QDir::Files).toSet().subtract(existingFiles);
+ const QSet<QString> entries = entrySet(m_qmlCacheDirectory).subtract(existingFiles);
QCOMPARE(entries.count(), 1);
QCOMPARE(QFileInfo(m_qmlCacheDirectory.absoluteFilePath(*entries.cbegin())).lastModified().toMSecsSinceEpoch(),
@@ -738,7 +757,8 @@ void tst_qmldiskcache::stableOrderOfDependentCompositeTypes()
QVERIFY2(firstDependentTypeClassName.contains("QMLTYPE"), firstDependentTypeClassName.constData());
QVERIFY2(secondDependentTypeClassName.contains("QMLTYPE"), secondDependentTypeClassName.constData());
- const QString testFileCachePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath));
+ const QString testFileCachePath = QV4::ExecutableCompilationUnit::localCacheFilePath(
+ QUrl::fromLocalFile(testFilePath));
QVERIFY(QFile::exists(testFileCachePath));
QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified();
@@ -816,7 +836,8 @@ void tst_qmldiskcache::singletonDependency()
QCOMPARE(obj->property("value").toInt(), 42);
}
- const QString testFileCachePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath));
+ const QString testFileCachePath = QV4::ExecutableCompilationUnit::localCacheFilePath(
+ QUrl::fromLocalFile(testFilePath));
QVERIFY(QFile::exists(testFileCachePath));
QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified();
@@ -873,7 +894,8 @@ void tst_qmldiskcache::cppRegisteredSingletonDependency()
QCOMPARE(value.toInt(), 42);
}
- const QString testFileCachePath = QV4::CompiledData::CompilationUnit::localCacheFilePath(QUrl::fromLocalFile(testFilePath));
+ const QString testFileCachePath = QV4::ExecutableCompilationUnit::localCacheFilePath(
+ QUrl::fromLocalFile(testFilePath));
QVERIFY(QFile::exists(testFileCachePath));
QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified();
@@ -902,8 +924,7 @@ void tst_qmldiskcache::cppRegisteredSingletonDependency()
void tst_qmldiskcache::cacheModuleScripts()
{
- const QSet<QString> existingFiles =
- m_qmlCacheDirectory.entryList(QDir::Files | QDir::NoDotAndDotDot).toSet();
+ const QSet<QString> existingFiles = entrySet(m_qmlCacheDirectory);
QQmlEngine engine;
@@ -924,8 +945,7 @@ void tst_qmldiskcache::cacheModuleScripts()
QVERIFY(!compilationUnit->backingFile.isNull());
}
- const QSet<QString> entries =
- m_qmlCacheDirectory.entryList(QStringList("*.mjsc")).toSet().subtract(existingFiles);
+ const QSet<QString> entries = entrySet(m_qmlCacheDirectory, QStringList("*.mjsc"));
QCOMPARE(entries.count(), 1);
diff --git a/tests/auto/qml/qmllint/data/CatchStatement.qml b/tests/auto/qml/qmllint/data/CatchStatement.qml
new file mode 100644
index 0000000000..e0f70fce7e
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/CatchStatement.qml
@@ -0,0 +1,8 @@
+import QtQml 2.12
+
+QtObject {
+ function f() {
+ try {} catch(err) {}
+ console.log(err);
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/FromRoot.qml b/tests/auto/qml/qmllint/data/FromRoot.qml
new file mode 100644
index 0000000000..021c09285e
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/FromRoot.qml
@@ -0,0 +1,17 @@
+import QtQuick 2.0
+
+Item {
+ id: root
+ property int unqualified: 42
+
+ Item {
+ Item {
+ x: unqualified // user defined property from root
+ }
+
+ QtObject {
+ property int check: x // existing property from root
+ }
+ }
+
+}
diff --git a/tests/auto/qml/qmllint/data/IdFromOuterSpace.qml b/tests/auto/qml/qmllint/data/IdFromOuterSpace.qml
new file mode 100644
index 0000000000..774a1cfc7c
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/IdFromOuterSpace.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.0
+
+Item {
+ x: alien.x
+
+ Component.onCompleted: {
+ console.log(alien);
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/SignalHandler.qml b/tests/auto/qml/qmllint/data/SignalHandler.qml
new file mode 100644
index 0000000000..865277cedb
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/SignalHandler.qml
@@ -0,0 +1,13 @@
+import QtQuick 2.0
+
+MouseArea {
+ onDoubleClicked: {
+ console.log(mouse);
+ // do further things
+ }
+ onClicked: console.info(mouse)
+ onPositionChanged: {
+ console.log(mouse)
+ }
+ onPressAndHold: console.warn(mouse)
+}
diff --git a/tests/auto/qml/qmllint/data/WithStatement.qml b/tests/auto/qml/qmllint/data/WithStatement.qml
new file mode 100644
index 0000000000..5641f21eeb
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/WithStatement.qml
@@ -0,0 +1,13 @@
+import QtQuick 2.0
+
+Item {
+ Item {
+ id: target
+ property int test: 42
+ }
+ Component.onCompleted: {
+ with(target) {
+ console.log(test);
+ }
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/catchIdentifierNoWarning.qml b/tests/auto/qml/qmllint/data/catchIdentifierNoWarning.qml
new file mode 100644
index 0000000000..097baa6fcf
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/catchIdentifierNoWarning.qml
@@ -0,0 +1,7 @@
+import QtQml 2.12
+
+QtObject {
+ function f() {
+ try {} catch(err) {console.log(err);}
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml b/tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml
new file mode 100644
index 0000000000..a59b736929
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.12
+import QtQml 2.12
+
+Item {
+ QtObject {
+ property int x: parent.x
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/spuriousParentWarning.qml b/tests/auto/qml/qmllint/data/spuriousParentWarning.qml
new file mode 100644
index 0000000000..1323593031
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/spuriousParentWarning.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.12
+
+Item {
+ Unknown {
+ property int x: parent.x
+ }
+}
diff --git a/tests/auto/qml/qmllint/main.cpp b/tests/auto/qml/qmllint/main.cpp
deleted file mode 100644
index eedbd7c2f2..0000000000
--- a/tests/auto/qml/qmllint/main.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sergio Martins <sergio.martins@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins 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 <QtTest/QtTest>
-#include <QProcess>
-#include <QString>
-
-class TestQmllint: public QObject
-{
- Q_OBJECT
-
-private Q_SLOTS:
- void initTestCase();
- void test();
- void test_data();
-private:
- QString m_qmllintPath;
-};
-
-void TestQmllint::initTestCase()
-{
- m_qmllintPath = QLibraryInfo::location(QLibraryInfo::BinariesPath) + QLatin1String("/qmllint");
-#ifdef Q_OS_WIN
- m_qmllintPath += QLatin1String(".exe");
-#endif
- if (!QFileInfo(m_qmllintPath).exists()) {
- QString message = QStringLiteral("qmllint executable not found (looked for %0)").arg(m_qmllintPath);
- QFAIL(qPrintable(message));
- }
-}
-
-void TestQmllint::test_data()
-{
- QTest::addColumn<QString>("filename");
- QTest::addColumn<bool>("isValid");
-
- // Valid files:
- QTest::newRow("Simple_QML") << QStringLiteral("Simple.qml") << true;
- QTest::newRow("QML_importing_JS") << QStringLiteral("importing_js.qml") << true;
- QTest::newRow("QTBUG-45916_JS_with_pragma_and_import") << QStringLiteral("QTBUG-45916.js") << true;
-
- // Invalid files:
- QTest::newRow("Invalid_syntax_QML") << QStringLiteral("failure1.qml") << false;
- QTest::newRow("Invalid_syntax_JS") << QStringLiteral("failure1.js") << false;
-}
-
-void TestQmllint::test()
-{
- QFETCH(QString, filename);
- QFETCH(bool, isValid);
- filename = QStringLiteral("data/") + filename;
- QStringList args;
- args << QStringLiteral("--silent") << filename;
-
- bool success = QProcess::execute(m_qmllintPath, args) == 0;
- QCOMPARE(success, isValid);
-}
-
-QTEST_MAIN(TestQmllint)
-#include "main.moc"
diff --git a/tests/auto/qml/qmllint/qmllint.pro b/tests/auto/qml/qmllint/qmllint.pro
index b53a6f6877..95470b4085 100644
--- a/tests/auto/qml/qmllint/qmllint.pro
+++ b/tests/auto/qml/qmllint/qmllint.pro
@@ -1,6 +1,11 @@
-TEMPLATE = app
-TARGET = testqmllint
-INCLUDEPATH += .
+CONFIG += testcase
+TARGET = tst_qmllint
+macos:CONFIG -= app_bundle
+
+SOURCES += tst_qmllint.cpp
+
+include (../../shared/util.pri)
+
+TESTDATA = data/*
-SOURCES += main.cpp
QT += testlib
diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp
new file mode 100644
index 0000000000..582f146dca
--- /dev/null
+++ b/tests/auto/qml/qmllint/tst_qmllint.cpp
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sergio Martins <sergio.martins@kdab.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins 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 <QtTest/QtTest>
+#include <QProcess>
+#include <QString>
+
+#include <util.h>
+
+class TestQmllint: public QQmlDataTest
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void initTestCase() override;
+ void test();
+ void test_data();
+ void testUnqualified();
+ void testUnqualified_data();
+ void testUnqualifiedNoSpuriousParentWarning();
+ void catchIdentifierNoFalsePositive();
+private:
+ QString m_qmllintPath;
+};
+
+void TestQmllint::initTestCase()
+{
+ QQmlDataTest::initTestCase();
+ m_qmllintPath = QLibraryInfo::location(QLibraryInfo::BinariesPath) + QLatin1String("/qmllint");
+#ifdef Q_OS_WIN
+ m_qmllintPath += QLatin1String(".exe");
+#endif
+ if (!QFileInfo(m_qmllintPath).exists()) {
+ QString message = QStringLiteral("qmllint executable not found (looked for %0)").arg(m_qmllintPath);
+ QFAIL(qPrintable(message));
+ }
+}
+
+void TestQmllint::test_data()
+{
+ QTest::addColumn<QString>("filename");
+ QTest::addColumn<bool>("isValid");
+
+ // Valid files:
+ QTest::newRow("Simple_QML") << QStringLiteral("Simple.qml") << true;
+ QTest::newRow("QML_importing_JS") << QStringLiteral("importing_js.qml") << true;
+ QTest::newRow("QTBUG-45916_JS_with_pragma_and_import") << QStringLiteral("QTBUG-45916.js") << true;
+
+ // Invalid files:
+ QTest::newRow("Invalid_syntax_QML") << QStringLiteral("failure1.qml") << false;
+ QTest::newRow("Invalid_syntax_JS") << QStringLiteral("failure1.js") << false;
+}
+
+void TestQmllint::testUnqualified()
+{
+ auto qmlImportDir = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath);
+ QFETCH(QString, filename);
+ QFETCH(QString, warningMessage);
+ QFETCH(int, warningLine);
+ QFETCH(int, warningColumn);
+ QStringList args;
+ args << QStringLiteral("-U") << testFile(filename) << QStringLiteral("-I") << qmlImportDir;
+
+ QProcess process;
+ process.start(m_qmllintPath, args);
+ QVERIFY(process.waitForFinished());
+ QVERIFY(process.exitStatus() == QProcess::NormalExit);
+ QVERIFY(process.exitCode());
+ QString output = process.readAllStandardError();
+ QVERIFY(output.contains(QString::asprintf("Warning: unqualified access at %d:%d", warningLine, warningColumn)));
+ QVERIFY(output.contains(warningMessage));
+}
+
+void TestQmllint::testUnqualified_data()
+{
+ QTest::addColumn<QString>("filename");
+ QTest::addColumn<QString>("warningMessage");
+ QTest::addColumn<int>("warningLine");
+ QTest::addColumn<int>("warningColumn");
+
+ // check for false positive due to and warning about with statement
+ QTest::newRow("WithStatement") << QStringLiteral("WithStatement.qml") << QStringLiteral("with statements are strongly discouraged") << 10 << 25;
+ // id from nowhere (as with setContextProperty)
+ QTest::newRow("IdFromOuterSpaceDirect") << QStringLiteral("IdFromOuterSpace.qml") << "alien.x" << 4 << 8;
+ QTest::newRow("IdFromOuterSpaceAccess") << QStringLiteral("IdFromOuterSpace.qml") << "console.log(alien)" << 7 << 21;
+ // access property of root object
+ QTest::newRow("FromRootDirect") << QStringLiteral("FromRoot.qml") << QStringLiteral("x: root.unqualified") << 9 << 16; // new property
+ QTest::newRow("FromRootAccess") << QStringLiteral("FromRoot.qml") << QStringLiteral("property int check: root.x") << 13 << 33; // builtin property
+ // access injected name from signal
+ QTest::newRow("SignalHandler1") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onDoubleClicked: function(mouse) {...") << 5 << 21;
+ QTest::newRow("SignalHandler2") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onPositionChanged: function(mouse) {...") << 10 << 21;
+ QTest::newRow("SignalHandlerShort1") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onClicked: (mouse) => {...") << 8 << 29;
+ QTest::newRow("SignalHandlerShort2") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onPressAndHold: (mouse) => {...") << 12 << 34;
+ // access catch identifier outside catch block
+ QTest::newRow("CatchStatement") << QStringLiteral("CatchStatement.qml") << QStringLiteral("err") << 6 << 21;
+}
+
+void TestQmllint::testUnqualifiedNoSpuriousParentWarning()
+{
+ auto qmlImportDir = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath);
+ {
+ QString filename = testFile("spuriousParentWarning.qml");
+ QStringList args;
+ args << QStringLiteral("-U") << filename << QStringLiteral("-I") << qmlImportDir;
+ QProcess process;
+ process.start(m_qmllintPath, args);
+ QVERIFY(process.waitForFinished());
+ QVERIFY(process.exitStatus() == QProcess::NormalExit);
+ QVERIFY(process.exitCode() == 0);
+ }
+ {
+ QString filename = testFile("nonSpuriousParentWarning.qml");
+ QStringList args;
+ args << QStringLiteral("-U") << filename << QStringLiteral("-I") << qmlImportDir;
+ QProcess process;
+ process.start(m_qmllintPath, args);
+ QVERIFY(process.waitForFinished());
+ QVERIFY(process.exitStatus() == QProcess::NormalExit);
+ QVERIFY(process.exitCode());
+ }
+}
+
+void TestQmllint::catchIdentifierNoFalsePositive()
+{
+ auto qmlImportDir = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath);
+ QString filename = QLatin1String("catchIdentifierNoWarning.qml");
+ filename.prepend(QStringLiteral("data/"));
+ QStringList args;
+ args << QStringLiteral("-U") << filename << QStringLiteral("-I") << qmlImportDir;
+ QProcess process;
+ process.start(m_qmllintPath, args);
+ QVERIFY(process.waitForFinished());
+ QVERIFY(process.exitStatus() == QProcess::NormalExit);
+ QVERIFY(process.exitCode() == 0);
+}
+
+void TestQmllint::test()
+{
+ QFETCH(QString, filename);
+ QFETCH(bool, isValid);
+ QStringList args;
+ args << QStringLiteral("--silent") << testFile(filename);
+
+ bool success = QProcess::execute(m_qmllintPath, args) == 0;
+ QCOMPARE(success, isValid);
+}
+
+QTEST_MAIN(TestQmllint)
+#include "tst_qmllint.moc"
diff --git a/tests/auto/qml/qmlmin/tst_qmlmin.cpp b/tests/auto/qml/qmlmin/tst_qmlmin.cpp
index d1e74aecef..e244369581 100644
--- a/tests/auto/qml/qmlmin/tst_qmlmin.cpp
+++ b/tests/auto/qml/qmlmin/tst_qmlmin.cpp
@@ -64,6 +64,7 @@ tst_qmlmin::tst_qmlmin()
void tst_qmlmin::initTestCase()
{
+#if QT_CONFIG(process) && !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled
qmlminPath = QLibraryInfo::location(QLibraryInfo::BinariesPath) + QLatin1String("/qmlmin");
#ifdef Q_OS_WIN
qmlminPath += QLatin1String(".exe");
@@ -129,6 +130,10 @@ void tst_qmlmin::initTestCase()
invalidFiles << "tests/auto/qml/qjsengine/script/com/trolltech/syntaxerror/__init__.js";
invalidFiles << "tests/auto/qml/debugger/qqmlpreview/data/broken.qml";
invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.2.qml";
+ invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.3.qml";
+ // generatorFunction.qml is not invalid per se, but the minifier cannot handle yield statements
+ invalidFiles << "tests/auto/qml/qqmlecmascript/data/generatorFunction.qml";
+#endif
}
QStringList tst_qmlmin::findFiles(const QDir &d)
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/Derived.qml b/tests/auto/qml/qmlplugindump/data/dumper/Imports/Derived.qml
new file mode 100644
index 0000000000..2f0ac401d7
--- /dev/null
+++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/Derived.qml
@@ -0,0 +1,6 @@
+pragma Singleton
+import dumper.Imports 1.0
+
+Imports {
+ property int something: 2
+}
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.pro b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.pro
index d20ea967ea..b4bd9baf5b 100644
--- a/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.pro
+++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/imports.pro
@@ -17,7 +17,7 @@ HEADERS += \
imports.h
!equals(_PRO_FILE_PWD_, $$OUT_PWD) {
- cp.files = qmldir plugins.qmltypes CompositeImports.qml
+ cp.files = qmldir plugins.qmltypes CompositeImports.qml Derived.qml
cp.path = $$OUT_PWD
COPIES += cp
}
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/plugins.qmltypes b/tests/auto/qml/qmlplugindump/data/dumper/Imports/plugins.qmltypes
index 937dd60a9e..fb13928ba0 100644
--- a/tests/auto/qml/qmlplugindump/data/dumper/Imports/plugins.qmltypes
+++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/plugins.qmltypes
@@ -14,4 +14,14 @@ Module {
exports: ["dumper.Imports/Imports 1.0"]
exportMetaObjectRevisions: [0]
}
+ Component {
+ prototype: "Imports"
+ name: "dumper.Imports/Derived 1.0"
+ exports: ["dumper.Imports/Derived 1.0"]
+ exportMetaObjectRevisions: [0]
+ isComposite: true
+ isCreatable: false
+ isSingleton: true
+ Property { name: "something"; type: "int" }
+ }
}
diff --git a/tests/auto/qml/qmlplugindump/data/dumper/Imports/qmldir b/tests/auto/qml/qmlplugindump/data/dumper/Imports/qmldir
index c9058a7f95..f84fca1d75 100644
--- a/tests/auto/qml/qmlplugindump/data/dumper/Imports/qmldir
+++ b/tests/auto/qml/qmlplugindump/data/dumper/Imports/qmldir
@@ -1,3 +1,4 @@
module dumper.Imports
plugin Imports
CompositeImports 1.0 CompositeImports.qml
+singleton Derived 1.0 Derived.qml
diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp
index a9c28a0911..0f5eea8b95 100644
--- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp
+++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp
@@ -52,6 +52,7 @@ private slots:
void removeObjectsWhenDestroyed();
void loadTranslation_data();
void loadTranslation();
+ void setInitialProperties();
private:
QString buildDir;
@@ -96,6 +97,9 @@ void tst_qqmlapplicationengine::basicLoading()
// will break.
void tst_qqmlapplicationengine::testNonResolvedPath()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("Android stores QML files in resources, and the path to a resource cannot be relative in this case");
+#endif
{
// NOTE NOTE NOTE! Missing testFileUrl is *WANTED* here! We want a
// non-resolved URL.
@@ -117,6 +121,9 @@ void tst_qqmlapplicationengine::testNonResolvedPath()
void tst_qqmlapplicationengine::application_data()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("Cannot launch external process on Android");
+#endif
QTest::addColumn<QByteArray>("qmlFile");
QTest::addColumn<QByteArray>("expectedStdErr");
@@ -269,6 +276,23 @@ void tst_qqmlapplicationengine::loadTranslation()
QCOMPARE(rootObject->property("translation").toString(), translation);
}
+void tst_qqmlapplicationengine::setInitialProperties()
+{
+ QQmlApplicationEngine test {};
+ {
+ test.setInitialProperties(QVariantMap{{"success", false}});
+ test.load(testFileUrl("basicTest.qml"));
+ QVERIFY(!test.rootObjects().empty());
+ QCOMPARE(test.rootObjects().first()->property("success").toBool(), false);
+ }
+ {
+ test.setInitialProperties({{"success", true}});
+ test.load(testFileUrl("basicTest.qml"));
+ QCOMPARE(test.rootObjects().size(), 2);
+ QCOMPARE(test.rootObjects().at(1)->property("success").toBool(), true);
+ }
+}
+
QTEST_MAIN(tst_qqmlapplicationengine)
#include "tst_qqmlapplicationengine.moc"
diff --git a/tests/auto/qml/qqmlbinding/data/MyComponent.qml b/tests/auto/qml/qqmlbinding/data/MyComponent.qml
new file mode 100644
index 0000000000..5892539a5d
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/data/MyComponent.qml
@@ -0,0 +1,2 @@
+import QtQuick 2.3
+QtObject { property real p: 0 }
diff --git a/tests/auto/qml/qqmlbinding/data/bindToQMLComponent.qml b/tests/auto/qml/qqmlbinding/data/bindToQMLComponent.qml
new file mode 100644
index 0000000000..471db9023b
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/data/bindToQMLComponent.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.0
+
+Item {
+ id: root
+ property MyComponent myProperty
+ Binding {
+ target: root
+ property: "myProperty"
+ value: myObject
+ }
+ MyComponent { id: myObject }
+}
diff --git a/tests/auto/qml/qqmlbinding/data/nanPropertyToInt.qml b/tests/auto/qml/qqmlbinding/data/nanPropertyToInt.qml
new file mode 100644
index 0000000000..366dbf0464
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/data/nanPropertyToInt.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.11
+
+Item {
+ visible: true
+ width: 320
+ height: 200
+ property int val: other.val
+
+ Rectangle {
+ id: other
+ anchors.fill: parent;
+ property int val: undefined / 2
+ }
+}
diff --git a/tests/auto/qml/qqmlbinding/data/noUnexpectedStringConversion.qml b/tests/auto/qml/qqmlbinding/data/noUnexpectedStringConversion.qml
new file mode 100644
index 0000000000..d0f30c5da5
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/data/noUnexpectedStringConversion.qml
@@ -0,0 +1,30 @@
+import QtQuick 2.12
+import QtQuick.Window 2.12
+
+Window {
+visible: true
+width: 640
+height: 480
+title: qsTr("Hello World")
+
+ Rectangle {
+ id: colorRect
+ objectName: "colorRect"
+ anchors.fill: parent
+ Text {
+ objectName: "colorLabel"
+ id: colorLabel
+ }
+ }
+
+ Binding {
+ target: colorLabel
+ property: "text"
+ value: "red"
+ }
+ Binding {
+ target: colorRect
+ property: "color"
+ value: "red"
+ }
+}
diff --git a/tests/auto/qml/qqmlbinding/data/restoreBinding2.qml b/tests/auto/qml/qqmlbinding/data/restoreBinding2.qml
new file mode 100644
index 0000000000..b42f975fe0
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/data/restoreBinding2.qml
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtQml 2.14
+
+Rectangle {
+ height: 400
+ width: 400
+
+ Rectangle {
+ property bool when: true
+
+ id: myItem
+ objectName: "myItem"
+ height: 300
+ width: 200
+ }
+
+ property var boundValue: 100
+
+ Binding {
+ restoreMode: Binding.RestoreValue
+ objectName: "theBinding"
+ target: myItem
+ property: "height"
+ when: myItem.when
+ value: boundValue
+ }
+}
diff --git a/tests/auto/qml/qqmlbinding/data/restoreBinding3.qml b/tests/auto/qml/qqmlbinding/data/restoreBinding3.qml
new file mode 100644
index 0000000000..1f63457ab6
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/data/restoreBinding3.qml
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtQml 2.14
+
+Rectangle {
+ height: 400
+ width: 400
+
+ Rectangle {
+ property bool when: true
+
+ id: myItem
+ objectName: "myItem"
+ height: 300
+ width: 200
+ property var foo: 42
+ }
+
+ property var boundValue: 13
+
+ Binding {
+ restoreMode: Binding.RestoreBindingOrValue
+ objectName: "theBinding"
+ target: myItem
+ property: "foo"
+ when: myItem.when
+ value: boundValue
+ }
+}
diff --git a/tests/auto/qml/qqmlbinding/data/restoreBinding4.qml b/tests/auto/qml/qqmlbinding/data/restoreBinding4.qml
new file mode 100644
index 0000000000..f34c3b40cf
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/data/restoreBinding4.qml
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtQml 2.14
+
+Rectangle {
+ height: 400
+ width: 400
+
+ Rectangle {
+ property bool when: true
+
+ id: myItem
+ objectName: "myItem"
+ height: 300
+ width: 200
+ property var foo: original
+ property bool fooCheck: foo === original && foo.bar() === 42
+ }
+
+ property var original: ({ bar: function() { return 42 } })
+
+ property var boundValue: 13
+
+ Binding {
+ restoreMode: Binding.RestoreBinding
+ objectName: "theBinding"
+ target: myItem
+ property: "foo"
+ when: myItem.when
+ value: boundValue
+ }
+}
diff --git a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
index 34cf21024d..2610402455 100644
--- a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
+++ b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
@@ -42,6 +42,9 @@ private slots:
void binding();
void whenAfterValue();
void restoreBinding();
+ void restoreBindingValue();
+ void restoreBindingVarValue();
+ void restoreBindingJSValue();
void restoreBindingWithLoop();
void restoreBindingWithoutCrash();
void deletedObject();
@@ -51,6 +54,9 @@ private slots:
void disabledOnReadonlyProperty();
void delayed();
void bindingOverwriting();
+ void bindToQmlComponent();
+ void bindingDoesNoWeirdConversion();
+ void bindNaNToInt();
private:
QQmlEngine engine;
@@ -64,7 +70,7 @@ void tst_qqmlbinding::binding()
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("test-binding.qml"));
- QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create());
+ QScopedPointer<QQuickRectangle> rect { qobject_cast<QQuickRectangle*>(c.create()) };
QVERIFY(rect != nullptr);
QQmlBind *binding3 = qobject_cast<QQmlBind*>(rect->findChild<QQmlBind*>("binding3"));
@@ -81,18 +87,16 @@ void tst_qqmlbinding::binding()
QQmlBind *binding = qobject_cast<QQmlBind*>(rect->findChild<QQmlBind*>("binding1"));
QVERIFY(binding != nullptr);
- QCOMPARE(binding->object(), qobject_cast<QObject*>(rect));
+ QCOMPARE(binding->object(), qobject_cast<QObject*>(rect.get()));
QCOMPARE(binding->property(), QLatin1String("text"));
QCOMPARE(binding->value().toString(), QLatin1String("Hello"));
-
- delete rect;
}
void tst_qqmlbinding::whenAfterValue()
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("test-binding2.qml"));
- QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create());
+ QScopedPointer<QQuickRectangle> rect {qobject_cast<QQuickRectangle*>(c.create())};
QVERIFY(rect != nullptr);
QCOMPARE(rect->color(), QColor("yellow"));
@@ -100,15 +104,13 @@ void tst_qqmlbinding::whenAfterValue()
rect->setProperty("changeColor", true);
QCOMPARE(rect->color(), QColor("red"));
-
- delete rect;
}
void tst_qqmlbinding::restoreBinding()
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("restoreBinding.qml"));
- QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create());
+ QScopedPointer<QQuickRectangle> rect { qobject_cast<QQuickRectangle*>(c.create()) };
QVERIFY(rect != nullptr);
QQuickRectangle *myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem"));
@@ -130,15 +132,85 @@ void tst_qqmlbinding::restoreBinding()
//original binding restored
myItem->setY(49);
QCOMPARE(myItem->x(), qreal(100-49));
+}
+
+void tst_qqmlbinding::restoreBindingValue()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("restoreBinding2.qml"));
+ QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(c.create()));
+ QVERIFY(!rect.isNull());
+
+ auto myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem"));
+ QVERIFY(myItem != nullptr);
+
+ QCOMPARE(myItem->height(), 100);
+ myItem->setProperty("when", QVariant(false));
+ QCOMPARE(myItem->height(), 300); // make sure the original value was restored
+
+ myItem->setProperty("when", QVariant(true));
+ QCOMPARE(myItem->height(), 100); // make sure the value specified in Binding is set
+ rect->setProperty("boundValue", 200);
+ QCOMPARE(myItem->height(), 200); // make sure the changed binding value is set
+ myItem->setProperty("when", QVariant(false));
+ // make sure that the original value is back, not e.g. the value from before the
+ // change (i.e. 100)
+ QCOMPARE(myItem->height(), 300);
+}
+
+void tst_qqmlbinding::restoreBindingVarValue()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("restoreBinding3.qml"));
+ QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(c.create()));
+ QVERIFY(!rect.isNull());
+
+ auto myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem"));
+ QVERIFY(myItem != nullptr);
+
+ QCOMPARE(myItem->property("foo"), 13);
+ myItem->setProperty("when", QVariant(false));
+ QCOMPARE(myItem->property("foo"), 42); // make sure the original value was restored
+
+ myItem->setProperty("when", QVariant(true));
+ QCOMPARE(myItem->property("foo"), 13); // make sure the value specified in Binding is set
+ rect->setProperty("boundValue", 31337);
+ QCOMPARE(myItem->property("foo"), 31337); // make sure the changed binding value is set
+ myItem->setProperty("when", QVariant(false));
+ // make sure that the original value is back, not e.g. the value from before the
+ // change (i.e. 100)
+ QCOMPARE(myItem->property("foo"), 42);
+}
+
+void tst_qqmlbinding::restoreBindingJSValue()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("restoreBinding4.qml"));
+ QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(c.create()));
+ QVERIFY(!rect.isNull());
+
+ auto myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem"));
+ QVERIFY(myItem != nullptr);
+
+ QCOMPARE(myItem->property("fooCheck"), false);
+ myItem->setProperty("when", QVariant(false));
+ QCOMPARE(myItem->property("fooCheck"), true); // make sure the original value was restored
+
+ myItem->setProperty("when", QVariant(true));
+ QCOMPARE(myItem->property("fooCheck"), false); // make sure the value specified in Binding is set
+ rect->setProperty("boundValue", 31337);
+ QCOMPARE(myItem->property("fooCheck"), false); // make sure the changed binding value is set
+ myItem->setProperty("when", QVariant(false));
+ // make sure that the original value is back, not e.g. the value from before the change
+ QCOMPARE(myItem->property("fooCheck"), true);
- delete rect;
}
void tst_qqmlbinding::restoreBindingWithLoop()
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("restoreBindingWithLoop.qml"));
- QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create());
+ QScopedPointer<QQuickRectangle> rect {qobject_cast<QQuickRectangle*>(c.create())};
QVERIFY(rect != nullptr);
QQuickRectangle *myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem"));
@@ -166,15 +238,13 @@ void tst_qqmlbinding::restoreBindingWithLoop()
myItem->setY(49);
QCOMPARE(myItem->x(), qreal(49 + 100));
-
- delete rect;
}
void tst_qqmlbinding::restoreBindingWithoutCrash()
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("restoreBindingWithoutCrash.qml"));
- QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create());
+ QScopedPointer<QQuickRectangle> rect {qobject_cast<QQuickRectangle*>(c.create())};
QVERIFY(rect != nullptr);
QQuickRectangle *myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem"));
@@ -205,8 +275,6 @@ void tst_qqmlbinding::restoreBindingWithoutCrash()
//original binding restored
myItem->setY(49);
QCOMPARE(myItem->x(), qreal(100-49));
-
- delete rect;
}
//QTBUG-20692
@@ -214,15 +282,13 @@ void tst_qqmlbinding::deletedObject()
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("deletedObject.qml"));
- QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create());
+ QScopedPointer<QQuickRectangle> rect {qobject_cast<QQuickRectangle*>(c.create())};
QVERIFY(rect != nullptr);
QGuiApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
//don't crash
rect->setProperty("activateBinding", true);
-
- delete rect;
}
void tst_qqmlbinding::warningOnUnknownProperty()
@@ -231,9 +297,8 @@ void tst_qqmlbinding::warningOnUnknownProperty()
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("unknownProperty.qml"));
- QQuickItem *item = qobject_cast<QQuickItem*>(c.create());
+ QScopedPointer<QQuickItem> item { qobject_cast<QQuickItem *>(c.create()) };
QVERIFY(item);
- delete item;
QCOMPARE(messageHandler.messages().count(), 1);
@@ -247,9 +312,8 @@ void tst_qqmlbinding::warningOnReadOnlyProperty()
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("readonlyProperty.qml"));
- QQuickItem *item = qobject_cast<QQuickItem*>(c.create());
+ QScopedPointer<QQuickItem> item { qobject_cast<QQuickItem *>(c.create()) };
QVERIFY(item);
- delete item;
QCOMPARE(messageHandler.messages().count(), 1);
@@ -263,9 +327,8 @@ void tst_qqmlbinding::disabledOnUnknownProperty()
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("disabledUnknown.qml"));
- QQuickItem *item = qobject_cast<QQuickItem*>(c.create());
+ QScopedPointer<QQuickItem> item { qobject_cast<QQuickItem *>(c.create()) };
QVERIFY(item);
- delete item;
QCOMPARE(messageHandler.messages().count(), 0);
}
@@ -276,10 +339,8 @@ void tst_qqmlbinding::disabledOnReadonlyProperty()
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("disabledReadonly.qml"));
- QQuickItem *item = qobject_cast<QQuickItem*>(c.create());
+ QScopedPointer<QQuickItem> item { qobject_cast<QQuickItem *>(c.create()) };
QVERIFY(item);
- delete item;
-
QCOMPARE(messageHandler.messages().count(), 0);
}
@@ -287,21 +348,19 @@ void tst_qqmlbinding::delayed()
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("delayed.qml"));
- QQuickItem *item = qobject_cast<QQuickItem*>(c.create());
+ QScopedPointer<QQuickItem> item {qobject_cast<QQuickItem*>(c.create())};
QVERIFY(item != nullptr);
// update on creation
QCOMPARE(item->property("changeCount").toInt(), 1);
- QMetaObject::invokeMethod(item, "updateText");
+ QMetaObject::invokeMethod(item.get(), "updateText");
// doesn't update immediately
QCOMPARE(item->property("changeCount").toInt(), 1);
QCoreApplication::processEvents();
// only updates once (non-delayed would update twice)
QCOMPARE(item->property("changeCount").toInt(), 2);
-
- delete item;
}
void tst_qqmlbinding::bindingOverwriting()
@@ -311,14 +370,45 @@ void tst_qqmlbinding::bindingOverwriting()
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("bindingOverwriting.qml"));
- QQuickItem *item = qobject_cast<QQuickItem*>(c.create());
+ QScopedPointer<QQuickItem> item {qobject_cast<QQuickItem*>(c.create())};
QVERIFY(item);
- delete item;
QLoggingCategory::setFilterRules(QString());
QCOMPARE(messageHandler.messages().count(), 2);
}
+void tst_qqmlbinding::bindToQmlComponent()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("bindToQMLComponent.qml"));
+ QVERIFY(c.create());
+}
+
+// QTBUG-78943
+void tst_qqmlbinding::bindingDoesNoWeirdConversion()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("noUnexpectedStringConversion.qml"));
+ QScopedPointer<QObject> o {c.create()};
+ QVERIFY(o);
+ QObject *colorRect = o->findChild<QObject*>("colorRect");
+ QVERIFY(colorRect);
+ QCOMPARE(qvariant_cast<QColor>(colorRect->property("color")), QColorConstants::Red);
+ QObject *colorLabel = o->findChild<QObject*>("colorLabel");
+ QCOMPARE(colorLabel->property("text").toString(), QLatin1String("red"));
+ QVERIFY(colorLabel);
+}
+
+//QTBUG-72442
+void tst_qqmlbinding::bindNaNToInt()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("nanPropertyToInt.qml"));
+ QScopedPointer<QQuickItem> item(qobject_cast<QQuickItem*>(c.create()));
+
+ QVERIFY(item != nullptr);
+ QCOMPARE(item->property("val").toInt(), 0);
+}
QTEST_MAIN(tst_qqmlbinding)
#include "tst_qqmlbinding.moc"
diff --git a/tests/auto/qml/qqmlchangeset/qqmlchangeset.pro b/tests/auto/qml/qqmlchangeset/qqmlchangeset.pro
index 9f854f1fa2..cdac5c0ff9 100644
--- a/tests/auto/qml/qqmlchangeset/qqmlchangeset.pro
+++ b/tests/auto/qml/qqmlchangeset/qqmlchangeset.pro
@@ -4,4 +4,4 @@ macx:CONFIG -= app_bundle
SOURCES += tst_qqmlchangeset.cpp
-QT += core-private gui-private qml-private testlib
+QT += core-private gui-private qml-private testlib qmlmodels-private
diff --git a/tests/auto/qml/qqmlcomponent/data/allJSONTypes.qml b/tests/auto/qml/qqmlcomponent/data/allJSONTypes.qml
new file mode 100644
index 0000000000..0541c9b104
--- /dev/null
+++ b/tests/auto/qml/qqmlcomponent/data/allJSONTypes.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.14
+
+Item {
+ property int i
+ property bool b
+ property double d
+ property string s
+ property var nothing
+}
diff --git a/tests/auto/qml/qqmlcomponent/data/variantBasedInitialization.qml b/tests/auto/qml/qqmlcomponent/data/variantBasedInitialization.qml
new file mode 100644
index 0000000000..acf08e94d2
--- /dev/null
+++ b/tests/auto/qml/qqmlcomponent/data/variantBasedInitialization.qml
@@ -0,0 +1,21 @@
+import QtQuick 2.14
+
+Item {
+ property int i
+ property bool b
+ property double d
+ property string s
+ property var nothing
+ property url myurl
+ property color c
+ property font myfont
+ property date mydate
+ property point mypoint
+ property size mysize
+ property rect myrect
+ property matrix4x4 matrix
+ property quaternion quat
+ property vector2d vec2
+ property vector3d vec3
+ property vector4d vec4
+}
diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
index a872cece96..1d2fa42b75 100644
--- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
+++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
@@ -35,7 +35,6 @@
#include <QtQuick>
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuick/private/qquickmousearea_p.h>
-#include <private/qv8engine_p.h>
#include <private/qqmlcontext_p.h>
#include <private/qv4qmlcontext_p.h>
#include <private/qv4scopedvalue_p.h>
@@ -122,6 +121,7 @@ private slots:
void relativeUrl_data();
void relativeUrl();
void setDataNoEngineNoSegfault();
+ void testSetInitialProperties();
private:
QQmlEngine engine;
@@ -222,12 +222,12 @@ void tst_qqmlcomponent::qmlCreateObjectAutoParent()
QVERIFY(window_item);
QVERIFY(window_window);
- QCOMPARE(qtobject_item->metaObject()->className(), "QQuickItem");
- QCOMPARE(qtobject_window->metaObject()->className(), "QQuickWindow");
- QCOMPARE(item_item->metaObject()->className(), "QQuickItem");
- QCOMPARE(item_window->metaObject()->className(), "QQuickWindow");
- QCOMPARE(window_item->metaObject()->className(), "QQuickItem");
- QCOMPARE(window_window->metaObject()->className(), "QQuickWindow");
+ QVERIFY(QByteArray(qtobject_item->metaObject()->className()).startsWith("QQuickItem"));
+ QVERIFY(QByteArray(qtobject_window->metaObject()->className()).startsWith("QQuickWindow"));
+ QVERIFY(QByteArray(item_item->metaObject()->className()).startsWith("QQuickItem"));
+ QVERIFY(QByteArray(item_window->metaObject()->className()).startsWith("QQuickWindow"));
+ QVERIFY(QByteArray(window_item->metaObject()->className()).startsWith("QQuickItem"));
+ QVERIFY(QByteArray(window_window->metaObject()->className()).startsWith("QQuickWindow"));
QCOMPARE(qtobject_qtobject->parent(), qtobjectParent);
QCOMPARE(qtobject_item->parent(), qtobjectParent);
@@ -637,9 +637,11 @@ void tst_qqmlcomponent::relativeUrl_data()
{
QTest::addColumn<QUrl>("url");
+#if !defined(Q_OS_ANDROID)
QTest::addRow("fromLocalFile") << QUrl::fromLocalFile("data/QtObjectComponent.qml");
QTest::addRow("fromLocalFileHash") << QUrl::fromLocalFile("data/QtObjectComponent#2.qml");
QTest::addRow("constructor") << QUrl("data/QtObjectComponent.qml");
+#endif
QTest::addRow("absolute") << QUrl::fromLocalFile(QFINDTESTDATA("data/QtObjectComponent.qml"));
QTest::addRow("qrc") << QUrl("qrc:/data/QtObjectComponent.qml");
}
@@ -666,6 +668,101 @@ void tst_qqmlcomponent::setDataNoEngineNoSegfault()
QVERIFY(!c);
}
+void tst_qqmlcomponent::testSetInitialProperties()
+{
+ QQmlEngine eng;
+ {
+ // JSON based initialization
+ QQmlComponent comp(&eng);
+ comp.loadUrl(testFileUrl("allJSONTypes.qml"));
+ QScopedPointer<QObject> obj { comp.beginCreate(eng.rootContext()) };
+ QVERIFY(obj);
+ comp.setInitialProperties(obj.get(), QVariantMap {
+ {QLatin1String("i"), 42},
+ {QLatin1String("b"), true},
+ {QLatin1String("d"), 3.1416},
+ {QLatin1String("s"), QLatin1String("hello world")},
+ {QLatin1String("nothing"), QVariant::fromValue(nullptr)}
+ });
+ comp.completeCreate();
+ if (!comp.errors().empty())
+ qDebug() << comp.errorString() << comp.errors();
+ QVERIFY(comp.errors().empty());
+ QCOMPARE(obj->property("i"), 42);
+ QCOMPARE(obj->property("b"), true);
+ QCOMPARE(obj->property("d"), 3.1416);
+ QCOMPARE(obj->property("s"), QLatin1String("hello world"));
+ QCOMPARE(obj->property("nothing"), QVariant::fromValue(nullptr));
+ }
+ {
+ // QVariant
+ QQmlComponent comp(&eng);
+ comp.loadUrl(testFileUrl("variantBasedInitialization.qml"));
+ QScopedPointer<QObject> obj { comp.beginCreate(eng.rootContext()) };
+ QVERIFY(obj);
+ QUrl myurl = comp.url();
+ QFont myfont;
+ QDateTime mydate = QDateTime::currentDateTime();
+ QPoint mypoint {1,2};
+ QSizeF mysize {0.5, 0.3};
+ QMatrix4x4 matrix {};
+ QQuaternion quat {5.0f, 0.3f, 0.2f, 0.1f};
+ QVector2D vec2 {2.0f, 3.1f};
+ QVector3D vec3 {1.0f, 2.0, 3.0f};
+ QVector4D vec4 {1.0f, 2.0f, 3.0f, 4.0f};
+#define ASJSON(NAME) {QLatin1String(#NAME), NAME}
+ comp.setInitialProperties(obj.get(), QVariantMap {
+ {QLatin1String("i"), 42},
+ {QLatin1String("b"), true},
+ {QLatin1String("d"), 3.1416},
+ {QLatin1String("s"), QLatin1String("hello world")},
+ {QLatin1String("nothing"), QVariant::fromValue( nullptr)},
+ ASJSON(myurl),
+ ASJSON(myfont),
+ ASJSON(mydate),
+ ASJSON(mypoint),
+ ASJSON(mysize),
+ ASJSON(matrix),
+ ASJSON(quat),
+ ASJSON(vec2), ASJSON(vec3), ASJSON(vec4)
+ });
+#undef ASJSON
+ comp.completeCreate();
+ if (!comp.errors().empty())
+ qDebug() << comp.errorString() << comp.errors();
+ QVERIFY(comp.errors().empty());
+ QCOMPARE(obj->property("i"), 42);
+ QCOMPARE(obj->property("b"), true);
+ QCOMPARE(obj->property("d"), 3.1416);
+ QCOMPARE(obj->property("s"), QLatin1String("hello world"));
+ QCOMPARE(obj->property("nothing"), QVariant::fromValue(nullptr));
+#define COMPARE(NAME) QCOMPARE(obj->property(#NAME), NAME)
+ COMPARE(myurl);
+ COMPARE(myfont);
+ COMPARE(mydate);
+ COMPARE(mypoint);
+ COMPARE(mysize);
+ COMPARE(matrix);
+ COMPARE(quat);
+ COMPARE(vec2);
+ COMPARE(vec3);
+ COMPARE(vec4);
+#undef COMPARE
+
+ }
+ {
+ // createWithInitialProperties: setting a nonexistent property
+ QQmlComponent comp(&eng);
+ comp.loadUrl(testFileUrl("allJSONTypes.qml"));
+ QScopedPointer<QObject> obj {
+ comp.createWithInitialProperties(QVariantMap { {"notThePropertiesYoureLookingFor", 42} })
+ };
+ qDebug() << comp.errorString();
+ QVERIFY(obj);
+ QVERIFY(comp.errorString().contains("Could not set property notThePropertiesYoureLookingFor"));
+ }
+}
+
QTEST_MAIN(tst_qqmlcomponent)
#include "tst_qqmlcomponent.moc"
diff --git a/tests/auto/qml/qqmlconnections/data/connection-no-signal-name.qml b/tests/auto/qml/qqmlconnections/data/bindings/connection-no-signal-name.qml
index 462a9577ff..462a9577ff 100644
--- a/tests/auto/qml/qqmlconnections/data/connection-no-signal-name.qml
+++ b/tests/auto/qml/qqmlconnections/data/bindings/connection-no-signal-name.qml
diff --git a/tests/auto/qml/qqmlconnections/data/connection-targetchange.qml b/tests/auto/qml/qqmlconnections/data/bindings/connection-targetchange.qml
index 154c309c9c..154c309c9c 100644
--- a/tests/auto/qml/qqmlconnections/data/connection-targetchange.qml
+++ b/tests/auto/qml/qqmlconnections/data/bindings/connection-targetchange.qml
diff --git a/tests/auto/qml/qqmlconnections/data/connection-unknownsignals-ignored.qml b/tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals-ignored.qml
index 0780dd1509..0780dd1509 100644
--- a/tests/auto/qml/qqmlconnections/data/connection-unknownsignals-ignored.qml
+++ b/tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals-ignored.qml
diff --git a/tests/auto/qml/qqmlconnections/data/connection-unknownsignals-notarget.qml b/tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals-notarget.qml
index 3da3e0f5d1..3da3e0f5d1 100644
--- a/tests/auto/qml/qqmlconnections/data/connection-unknownsignals-notarget.qml
+++ b/tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals-notarget.qml
diff --git a/tests/auto/qml/qqmlconnections/data/connection-unknownsignals-parent.qml b/tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals-parent.qml
index 2c55215579..2c55215579 100644
--- a/tests/auto/qml/qqmlconnections/data/connection-unknownsignals-parent.qml
+++ b/tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals-parent.qml
diff --git a/tests/auto/qml/qqmlconnections/data/connection-unknownsignals.qml b/tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals.qml
index a351016b4a..a351016b4a 100644
--- a/tests/auto/qml/qqmlconnections/data/connection-unknownsignals.qml
+++ b/tests/auto/qml/qqmlconnections/data/bindings/connection-unknownsignals.qml
diff --git a/tests/auto/qml/qqmlconnections/data/disabled-at-start.qml b/tests/auto/qml/qqmlconnections/data/bindings/disabled-at-start.qml
index 1a823f87f6..1a823f87f6 100644
--- a/tests/auto/qml/qqmlconnections/data/disabled-at-start.qml
+++ b/tests/auto/qml/qqmlconnections/data/bindings/disabled-at-start.qml
diff --git a/tests/auto/qml/qqmlconnections/data/override-proxy-type.qml b/tests/auto/qml/qqmlconnections/data/bindings/override-proxy-type.qml
index 80e459966b..80e459966b 100644
--- a/tests/auto/qml/qqmlconnections/data/override-proxy-type.qml
+++ b/tests/auto/qml/qqmlconnections/data/bindings/override-proxy-type.qml
diff --git a/tests/auto/qml/qqmlconnections/data/rewriteError-global.qml b/tests/auto/qml/qqmlconnections/data/bindings/rewriteError-global.qml
index 1d0b557069..1d0b557069 100644
--- a/tests/auto/qml/qqmlconnections/data/rewriteError-global.qml
+++ b/tests/auto/qml/qqmlconnections/data/bindings/rewriteError-global.qml
diff --git a/tests/auto/qml/qqmlconnections/data/rewriteError-unnamed.qml b/tests/auto/qml/qqmlconnections/data/bindings/rewriteError-unnamed.qml
index a4849e994b..a4849e994b 100644
--- a/tests/auto/qml/qqmlconnections/data/rewriteError-unnamed.qml
+++ b/tests/auto/qml/qqmlconnections/data/bindings/rewriteError-unnamed.qml
diff --git a/tests/auto/qml/qqmlconnections/data/singletontype-target.qml b/tests/auto/qml/qqmlconnections/data/bindings/singletontype-target.qml
index 7de488c2dd..7de488c2dd 100644
--- a/tests/auto/qml/qqmlconnections/data/singletontype-target.qml
+++ b/tests/auto/qml/qqmlconnections/data/bindings/singletontype-target.qml
diff --git a/tests/auto/qml/qqmlconnections/data/test-connection-implicit.qml b/tests/auto/qml/qqmlconnections/data/bindings/test-connection-implicit.qml
index d5aa0f102a..d5aa0f102a 100644
--- a/tests/auto/qml/qqmlconnections/data/test-connection-implicit.qml
+++ b/tests/auto/qml/qqmlconnections/data/bindings/test-connection-implicit.qml
diff --git a/tests/auto/qml/qqmlconnections/data/test-connection.qml b/tests/auto/qml/qqmlconnections/data/bindings/test-connection.qml
index f44cbc047f..f44cbc047f 100644
--- a/tests/auto/qml/qqmlconnections/data/test-connection.qml
+++ b/tests/auto/qml/qqmlconnections/data/bindings/test-connection.qml
diff --git a/tests/auto/qml/qqmlconnections/data/trimming.qml b/tests/auto/qml/qqmlconnections/data/bindings/trimming.qml
index 4c37eb22af..4c37eb22af 100644
--- a/tests/auto/qml/qqmlconnections/data/trimming.qml
+++ b/tests/auto/qml/qqmlconnections/data/bindings/trimming.qml
diff --git a/tests/auto/qml/qqmlconnections/data/functions/connection-no-signal-name.qml b/tests/auto/qml/qqmlconnections/data/functions/connection-no-signal-name.qml
new file mode 100644
index 0000000000..04cc36b3c5
--- /dev/null
+++ b/tests/auto/qml/qqmlconnections/data/functions/connection-no-signal-name.qml
@@ -0,0 +1,15 @@
+import QtQuick 2.4
+
+Item {
+ id: blaBlaBla
+ function hint() {
+ }
+
+ Connections {
+ //target: blaBlaBla
+ // function onHint() { hint() };
+ on: true
+ }
+}
+
+
diff --git a/tests/auto/qml/qqmlconnections/data/functions/connection-targetchange.qml b/tests/auto/qml/qqmlconnections/data/functions/connection-targetchange.qml
new file mode 100644
index 0000000000..692194e837
--- /dev/null
+++ b/tests/auto/qml/qqmlconnections/data/functions/connection-targetchange.qml
@@ -0,0 +1,25 @@
+import QtQuick 2.0
+
+Item {
+ Component {
+ id: item1
+ Item {
+ objectName: "item1"
+ }
+ }
+ Component {
+ id: item2
+ Item {
+ objectName: "item2"
+ }
+ }
+ Loader {
+ id: loader
+ sourceComponent: item1
+ }
+ Connections {
+ objectName: "connections"
+ target: loader.item
+ function onWidthChanged() { loader.sourceComponent = item2 }
+ }
+}
diff --git a/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-ignored.qml b/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-ignored.qml
new file mode 100644
index 0000000000..f70d8cdb15
--- /dev/null
+++ b/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-ignored.qml
@@ -0,0 +1,17 @@
+import QtQml 2.0
+
+QtObject {
+ id: root
+
+ property Connections c1: Connections {
+ target: root;
+ function onNotFooBar1() {}
+ ignoreUnknownSignals: true
+ }
+
+ property Connections c2: Connections {
+ objectName: "connections"
+ function onNotFooBar2() {}
+ ignoreUnknownSignals: true
+ }
+}
diff --git a/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-notarget.qml b/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-notarget.qml
new file mode 100644
index 0000000000..7658728dd9
--- /dev/null
+++ b/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-notarget.qml
@@ -0,0 +1,9 @@
+import QtQml 2.0
+
+QtObject {
+ property Connections c1: Connections {
+ objectName: "connections"
+ target: null
+ function onNotFooBar() {}
+ }
+}
diff --git a/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-parent.qml b/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-parent.qml
new file mode 100644
index 0000000000..ece76b0cf7
--- /dev/null
+++ b/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals-parent.qml
@@ -0,0 +1,8 @@
+import QtQml 2.0
+
+QtObject {
+ property Connections c1: Connections {
+ objectName: "connections"
+ function onFooBar() {}
+ }
+}
diff --git a/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals.qml b/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals.qml
new file mode 100644
index 0000000000..a198a724d0
--- /dev/null
+++ b/tests/auto/qml/qqmlconnections/data/functions/connection-unknownsignals.qml
@@ -0,0 +1,11 @@
+import QtQml 2.0
+
+QtObject {
+ id: screen
+
+ property Connections c1: Connections {
+ objectName: "connections"
+ target: screen
+ function onFooBar() {}
+ }
+}
diff --git a/tests/auto/qml/qqmlconnections/data/functions/disabled-at-start.qml b/tests/auto/qml/qqmlconnections/data/functions/disabled-at-start.qml
new file mode 100644
index 0000000000..981437fe8c
--- /dev/null
+++ b/tests/auto/qml/qqmlconnections/data/functions/disabled-at-start.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.9
+
+Item {
+ id: root
+
+ property bool tested: false
+ signal testMe()
+
+ Connections {
+ target: root
+ enabled: false
+ function onTestMe() { root.tested = true; }
+ }
+}
diff --git a/tests/auto/qml/qqmlconnections/data/functions/override-proxy-type.qml b/tests/auto/qml/qqmlconnections/data/functions/override-proxy-type.qml
new file mode 100644
index 0000000000..b83f0baa11
--- /dev/null
+++ b/tests/auto/qml/qqmlconnections/data/functions/override-proxy-type.qml
@@ -0,0 +1,13 @@
+import QtQml 2.12
+import test.proxy 1.0
+
+Proxy {
+ property int testEnum: 0;
+ id: proxy
+ property Connections connections: Connections {
+ target: proxy
+ function onSomeSignal() { testEnum = Proxy.EnumValue }
+ }
+
+ Component.onCompleted: someSignal()
+}
diff --git a/tests/auto/qml/qqmlconnections/data/functions/rewriteError-global.qml b/tests/auto/qml/qqmlconnections/data/functions/rewriteError-global.qml
new file mode 100644
index 0000000000..de3154c431
--- /dev/null
+++ b/tests/auto/qml/qqmlconnections/data/functions/rewriteError-global.qml
@@ -0,0 +1,8 @@
+import QtQml 2.0
+import Test 1.0
+
+TestObject {
+ property QtObject connection: Connections {
+ function onSignalWithGlobalName() { ran = true }
+ }
+}
diff --git a/tests/auto/qml/qqmlconnections/data/functions/rewriteError-unnamed.qml b/tests/auto/qml/qqmlconnections/data/functions/rewriteError-unnamed.qml
new file mode 100644
index 0000000000..fa1d1b17d7
--- /dev/null
+++ b/tests/auto/qml/qqmlconnections/data/functions/rewriteError-unnamed.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+import Test 1.0
+
+TestObject {
+ property QtObject connection: Connections {
+ function onUnnamedArgumentSignal() { ran = true }
+ }
+}
diff --git a/tests/auto/qml/qqmlconnections/data/functions/singletontype-target.qml b/tests/auto/qml/qqmlconnections/data/functions/singletontype-target.qml
new file mode 100644
index 0000000000..935b610351
--- /dev/null
+++ b/tests/auto/qml/qqmlconnections/data/functions/singletontype-target.qml
@@ -0,0 +1,22 @@
+import QtQml 2.0
+import MyTestSingletonType 1.0 as MyTestSingletonType
+
+QtObject {
+ id: rootObject
+ objectName: "rootObject"
+ property int newIntPropValue: 12
+
+ property int moduleIntPropChangedCount: 0
+ property int moduleOtherSignalCount: 0
+
+ function setModuleIntProp() {
+ MyTestSingletonType.Api.intProp = newIntPropValue;
+ newIntPropValue = newIntPropValue + 1;
+ }
+
+ property Connections c: Connections {
+ target: MyTestSingletonType.Api
+ function onIntPropChanged() { moduleIntPropChangedCount = moduleIntPropChangedCount + 1 }
+ function onOtherSignal() { moduleOtherSignalCount = moduleOtherSignalCount + 1 }
+ }
+}
diff --git a/tests/auto/qml/qqmlconnections/data/functions/test-connection-implicit.qml b/tests/auto/qml/qqmlconnections/data/functions/test-connection-implicit.qml
new file mode 100644
index 0000000000..2ed5278636
--- /dev/null
+++ b/tests/auto/qml/qqmlconnections/data/functions/test-connection-implicit.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.0
+
+Item {
+ width: 50
+
+ property bool tested: false
+
+ Connections { function onWidthChanged() { tested = true } }
+}
diff --git a/tests/auto/qml/qqmlconnections/data/functions/test-connection.qml b/tests/auto/qml/qqmlconnections/data/functions/test-connection.qml
new file mode 100644
index 0000000000..c706797ea4
--- /dev/null
+++ b/tests/auto/qml/qqmlconnections/data/functions/test-connection.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.0
+
+Item {
+ id: screen; width: 50
+
+ property bool tested: false
+ signal testMe
+
+ Connections {
+ objectName: "connections"
+ target: screen;
+ function onWidthChanged() { screen.tested = true }
+ }
+}
diff --git a/tests/auto/qml/qqmlconnections/data/functions/trimming.qml b/tests/auto/qml/qqmlconnections/data/functions/trimming.qml
new file mode 100644
index 0000000000..7dfd673539
--- /dev/null
+++ b/tests/auto/qml/qqmlconnections/data/functions/trimming.qml
@@ -0,0 +1,13 @@
+import QtQml 2.0
+
+QtObject {
+ id: root
+
+ property string tested
+ signal testMe(int param1, string param2)
+
+ property Connections c: Connections {
+ target: root
+ function onTestMe(param1, param2) { root.tested = param2 + param1 }
+ }
+}
diff --git a/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp b/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp
index dc29363fcf..07af519a3d 100644
--- a/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp
+++ b/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp
@@ -42,29 +42,57 @@ public:
private slots:
void defaultValues();
void properties();
+
+ void connection_data() { prefixes(); }
void connection();
+
+ void trimming_data() { prefixes(); }
void trimming();
+
+ void targetChanged_data() { prefixes(); };
void targetChanged();
+
void unknownSignals_data();
void unknownSignals();
+
void errors_data();
void errors();
+
+ void rewriteErrors_data() { prefixes(); }
void rewriteErrors();
+
+ void singletonTypeTarget_data() { prefixes(); }
void singletonTypeTarget();
+
+ void enableDisable_QTBUG_36350_data() { prefixes(); }
void enableDisable_QTBUG_36350();
+
+ void disabledAtStart_data() { prefixes(); }
void disabledAtStart();
+
+ void clearImplicitTarget_data() { prefixes(); }
void clearImplicitTarget();
void onWithoutASignal();
+
+ void noAcceleratedGlobalLookup_data() { prefixes(); }
void noAcceleratedGlobalLookup();
private:
QQmlEngine engine;
+ void prefixes();
};
tst_qqmlconnections::tst_qqmlconnections()
{
}
+void tst_qqmlconnections::prefixes()
+{
+ QTest::addColumn<QString>("prefix");
+ QTest::newRow("functions") << QString("functions");
+ QTest::newRow("bindings") << QString("bindings");
+}
+
void tst_qqmlconnections::defaultValues()
{
QQmlEngine engine;
@@ -93,8 +121,9 @@ void tst_qqmlconnections::properties()
void tst_qqmlconnections::connection()
{
+ QFETCH(QString, prefix);
QQmlEngine engine;
- QQmlComponent c(&engine, testFileUrl("test-connection.qml"));
+ QQmlComponent c(&engine, testFileUrl(prefix + "/test-connection.qml"));
QQuickItem *item = qobject_cast<QQuickItem*>(c.create());
QVERIFY(item != nullptr);
@@ -110,8 +139,9 @@ void tst_qqmlconnections::connection()
void tst_qqmlconnections::trimming()
{
+ QFETCH(QString, prefix);
QQmlEngine engine;
- QQmlComponent c(&engine, testFileUrl("trimming.qml"));
+ QQmlComponent c(&engine, testFileUrl(prefix + "/trimming.qml"));
QObject *object = c.create();
QVERIFY(object != nullptr);
@@ -131,8 +161,9 @@ void tst_qqmlconnections::trimming()
// Confirm that target can be changed by one of our signal handlers
void tst_qqmlconnections::targetChanged()
{
+ QFETCH(QString, prefix);
QQmlEngine engine;
- QQmlComponent c(&engine, testFileUrl("connection-targetchange.qml"));
+ QQmlComponent c(&engine, testFileUrl(prefix + "/connection-targetchange.qml"));
QQuickItem *item = qobject_cast<QQuickItem*>(c.create());
QVERIFY(item != nullptr);
@@ -158,10 +189,15 @@ void tst_qqmlconnections::unknownSignals_data()
QTest::addColumn<QString>("file");
QTest::addColumn<QString>("error");
- QTest::newRow("basic") << "connection-unknownsignals.qml" << ":6:30: QML Connections: Cannot assign to non-existent property \"onFooBar\"";
- QTest::newRow("parent") << "connection-unknownsignals-parent.qml" << ":4:30: QML Connections: Cannot assign to non-existent property \"onFooBar\"";
- QTest::newRow("ignored") << "connection-unknownsignals-ignored.qml" << ""; // should be NO error
- QTest::newRow("notarget") << "connection-unknownsignals-notarget.qml" << ""; // should be NO error
+ QTest::newRow("functions/basic") << "functions/connection-unknownsignals.qml" << ":6:30: QML Connections: Detected function \"onFooBar\" in Connections element. This is probably intended to be a signal handler but no signal of the target matches the name.";
+ QTest::newRow("functions/parent") << "functions/connection-unknownsignals-parent.qml" << ":4:30: QML Connections: Detected function \"onFooBar\" in Connections element. This is probably intended to be a signal handler but no signal of the target matches the name.";
+ QTest::newRow("functions/ignored") << "functions/connection-unknownsignals-ignored.qml" << ""; // should be NO error
+ QTest::newRow("functions/notarget") << "functions/connection-unknownsignals-notarget.qml" << ""; // should be NO error
+
+ QTest::newRow("bindings/basic") << "bindings/connection-unknownsignals.qml" << ":6:30: QML Connections: Cannot assign to non-existent property \"onFooBar\"";
+ QTest::newRow("bindings/parent") << "bindings/connection-unknownsignals-parent.qml" << ":4:30: QML Connections: Cannot assign to non-existent property \"onFooBar\"";
+ QTest::newRow("bindings/ignored") << "bindings/connection-unknownsignals-ignored.qml" << ""; // should be NO error
+ QTest::newRow("bindings/notarget") << "bindings/connection-unknownsignals-notarget.qml" << ""; // should be NO error
}
void tst_qqmlconnections::unknownSignals()
@@ -239,10 +275,11 @@ private:
void tst_qqmlconnections::rewriteErrors()
{
+ QFETCH(QString, prefix);
qmlRegisterType<TestObject>("Test", 1, 0, "TestObject");
{
QQmlEngine engine;
- QQmlComponent c(&engine, testFileUrl("rewriteError-unnamed.qml"));
+ QQmlComponent c(&engine, testFileUrl(prefix + "/rewriteError-unnamed.qml"));
QTest::ignoreMessage(QtWarningMsg, (c.url().toString() + ":5:35: QML Connections: Signal uses unnamed parameter followed by named parameter.").toLatin1());
TestObject *obj = qobject_cast<TestObject*>(c.create());
QVERIFY(obj != nullptr);
@@ -254,7 +291,7 @@ void tst_qqmlconnections::rewriteErrors()
{
QQmlEngine engine;
- QQmlComponent c(&engine, testFileUrl("rewriteError-global.qml"));
+ QQmlComponent c(&engine, testFileUrl(prefix + "/rewriteError-global.qml"));
QTest::ignoreMessage(QtWarningMsg, (c.url().toString() + ":5:35: QML Connections: Signal parameter \"parseInt\" hides global variable.").toLatin1());
TestObject *obj = qobject_cast<TestObject*>(c.create());
QVERIFY(obj != nullptr);
@@ -305,8 +342,9 @@ static QObject *module_api_factory(QQmlEngine *engine, QJSEngine *scriptEngine)
// QTBUG-20937
void tst_qqmlconnections::singletonTypeTarget()
{
+ QFETCH(QString, prefix);
qmlRegisterSingletonType<MyTestSingletonType>("MyTestSingletonType", 1, 0, "Api", module_api_factory);
- QQmlComponent component(&engine, testFileUrl("singletontype-target.qml"));
+ QQmlComponent component(&engine, testFileUrl(prefix + "/singletontype-target.qml"));
QObject *object = component.create();
QVERIFY(object != nullptr);
@@ -331,8 +369,9 @@ void tst_qqmlconnections::singletonTypeTarget()
void tst_qqmlconnections::enableDisable_QTBUG_36350()
{
+ QFETCH(QString, prefix);
QQmlEngine engine;
- QQmlComponent c(&engine, testFileUrl("test-connection.qml"));
+ QQmlComponent c(&engine, testFileUrl(prefix + "/test-connection.qml"));
QQuickItem *item = qobject_cast<QQuickItem*>(c.create());
QVERIFY(item != nullptr);
@@ -358,8 +397,9 @@ void tst_qqmlconnections::enableDisable_QTBUG_36350()
void tst_qqmlconnections::disabledAtStart()
{
+ QFETCH(QString, prefix);
QQmlEngine engine;
- QQmlComponent c(&engine, testFileUrl("disabled-at-start.qml"));
+ QQmlComponent c(&engine, testFileUrl(prefix + "/disabled-at-start.qml"));
QObject * const object = c.create();
QVERIFY(object != nullptr);
@@ -376,8 +416,9 @@ void tst_qqmlconnections::disabledAtStart()
//QTBUG-56499
void tst_qqmlconnections::clearImplicitTarget()
{
+ QFETCH(QString, prefix);
QQmlEngine engine;
- QQmlComponent c(&engine, testFileUrl("test-connection-implicit.qml"));
+ QQmlComponent c(&engine, testFileUrl(prefix + "/test-connection-implicit.qml"));
QQuickItem *item = qobject_cast<QQuickItem*>(c.create());
QVERIFY(item != nullptr);
@@ -421,14 +462,15 @@ signals:
void tst_qqmlconnections::noAcceleratedGlobalLookup()
{
+ QFETCH(QString, prefix);
qRegisterMetaType<Proxy::MyEnum>();
qmlRegisterType<Proxy>("test.proxy", 1, 0, "Proxy");
QQmlEngine engine;
- QQmlComponent c(&engine, testFileUrl("override-proxy-type.qml"));
+ QQmlComponent c(&engine, testFileUrl(prefix + "/override-proxy-type.qml"));
QVERIFY(c.isReady());
QScopedPointer<QObject> object(c.create());
const QVariant val = object->property("testEnum");
- QCOMPARE(val.type(), QMetaType::Int);
+ QCOMPARE(val.type(), QVariant::Int);
QCOMPARE(val.toInt(), int(Proxy::EnumValue));
}
diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
index cb4bee0d3a..6754f22049 100644
--- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
+++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
@@ -71,6 +71,7 @@ private slots:
void outerContextObject();
void contextObjectHierarchy();
+ void destroyContextProperty();
private:
QQmlEngine engine;
@@ -892,6 +893,31 @@ void tst_qqmlcontext::contextObjectHierarchy()
});
}
+void tst_qqmlcontext::destroyContextProperty()
+{
+ QScopedPointer<QQmlContext> context;
+ QScopedPointer<QObject> objectThatOutlivesEngine(new QObject);
+ {
+ QQmlEngine engine;
+ context.reset(new QQmlContext(&engine));
+
+ {
+ QObject object;
+ context->setContextProperty(QLatin1String("a"), &object);
+ QCOMPARE(qvariant_cast<QObject *>(context->contextProperty(QLatin1String("a"))), &object);
+ }
+
+ QCOMPARE(qvariant_cast<QObject *>(context->contextProperty(QLatin1String("a"))), nullptr);
+ context->setContextProperty(QLatin1String("b"), objectThatOutlivesEngine.data());
+ }
+
+ // dropDestroyedObject() should not crash, even if the engine is gone.
+ objectThatOutlivesEngine.reset();
+
+ // We're not allowed to call context->contextProperty("b") anymore.
+ // TODO: Or are we?
+}
+
QTEST_MAIN(tst_qqmlcontext)
#include "tst_qqmlcontext.moc"
diff --git a/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp b/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp
index 99cabb4b09..13e4d4c53b 100644
--- a/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp
+++ b/tests/auto/qml/qqmlcpputils/tst_qqmlcpputils.cpp
@@ -71,7 +71,7 @@ void tst_qqmlcpputils::fastConnect()
{
MyObject obj;
- qmlobject_connect(&obj, MyObject, SIGNAL(signal1()), &obj, MyObject, SLOT(slot1()))
+ qmlobject_connect(&obj, MyObject, SIGNAL(signal1()), &obj, MyObject, SLOT(slot1()));
obj.signal1();
QCOMPARE(obj.slotCount, 1);
diff --git a/tests/auto/qml/qqmldirparser/qqmldirparser.pro b/tests/auto/qml/qqmldirparser/qqmldirparser.pro
index dda74b1ef9..b5373a6e8f 100644
--- a/tests/auto/qml/qqmldirparser/qqmldirparser.pro
+++ b/tests/auto/qml/qqmldirparser/qqmldirparser.pro
@@ -6,3 +6,5 @@ macx:CONFIG -= app_bundle
SOURCES += tst_qqmldirparser.cpp
include (../../shared/util.pri)
+
+TESTDATA = data/*
diff --git a/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp b/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp
index 3643ca65c6..1e690e38dd 100644
--- a/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp
+++ b/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp
@@ -32,6 +32,7 @@
#include <QObject>
#include <QQmlEngine>
#include <QQmlComponent>
+#include <private/qqmljsdiagnosticmessage_p.h>
#include <private/qqmldirparser_p.h>
#include <QDebug>
@@ -56,12 +57,21 @@ tst_qqmldirparser::tst_qqmldirparser()
namespace {
- QStringList toStringList(const QList<QQmlError> &errors)
+ QStringList toStringList(const QList<QQmlJS::DiagnosticMessage> &errors)
{
QStringList rv;
- foreach (const QQmlError &e, errors)
- rv.append(e.toString());
+ for (const QQmlJS::DiagnosticMessage &e : errors) {
+ QString errorString = QLatin1String("qmldir");
+ if (e.line > 0) {
+ errorString += QLatin1Char(':') + QString::number(e.line);
+ if (e.column > 0)
+ errorString += QLatin1Char(':') + QString::number(e.column);
+ }
+
+ errorString += QLatin1String(": ") + e.message;
+ rv.append(errorString);
+ }
return rv;
}
diff --git a/tests/auto/qml/qqmlecmascript/data/generatorFunction.qml b/tests/auto/qml/qqmlecmascript/data/generatorFunction.qml
new file mode 100644
index 0000000000..66bb642f34
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/generatorFunction.qml
@@ -0,0 +1,22 @@
+import QtQml 2.12
+
+QtObject {
+ id: root
+ property bool test1: false;
+ property bool test2: false;
+ property bool test3: false;
+ property bool done: false;
+ function *gen() {
+ yield 1
+ yield 2
+ yield 3
+ }
+
+ Component.onCompleted: {
+ let it = root.gen();
+ root.test1 = (it.next().value == 1);
+ root.test2 = (it.next().value == 2);
+ root.test3 = (it.next().value == 3);
+ root.done = it.next().done;
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/regularExpression.2.qml b/tests/auto/qml/qqmlecmascript/data/regularExpression.2.qml
new file mode 100644
index 0000000000..b22f8ab71e
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/regularExpression.2.qml
@@ -0,0 +1,7 @@
+import Qt.test 1.0
+
+MyQmlObject{
+ id: obj
+ objectName: "obj"
+ regularExpression: "[a-zA-z]"
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/regularExpression.qml b/tests/auto/qml/qqmlecmascript/data/regularExpression.qml
new file mode 100644
index 0000000000..6f31ffd305
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/regularExpression.qml
@@ -0,0 +1,7 @@
+import Qt.test 1.0
+
+MyQmlObject{
+ id: obj
+ objectName: "obj"
+ regularExpression: /[a-zA-z]/
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/semicolonAfterProperty.qml b/tests/auto/qml/qqmlecmascript/data/semicolonAfterProperty.qml
new file mode 100644
index 0000000000..0a75ea6f36
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/semicolonAfterProperty.qml
@@ -0,0 +1,10 @@
+import QtQml 2.0
+
+QtObject {
+ property var field: { "key": "value"};
+ property list<QtObject> mylist: [
+ QtObject {id: a},
+ QtObject {id: b}
+ ];
+ property var object: QtObject {};
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/signalParameterTypes.qml b/tests/auto/qml/qqmlecmascript/data/signalParameterTypes.qml
index 54d29dfc94..9fdb7f92f3 100644
--- a/tests/auto/qml/qqmlecmascript/data/signalParameterTypes.qml
+++ b/tests/auto/qml/qqmlecmascript/data/signalParameterTypes.qml
@@ -1,4 +1,5 @@
import Qt.test 1.0
+import QtQuick 2.0 // We need the the QtQuick color provider for colorProperty
MyQmlObject
{
diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h
index 4547a74470..3233e7f105 100644
--- a/tests/auto/qml/qqmlecmascript/testtypes.h
+++ b/tests/auto/qml/qqmlecmascript/testtypes.h
@@ -33,6 +33,7 @@
#include <QtQml/qqmlexpression.h>
#include <QtCore/qpoint.h>
#include <QtCore/qsize.h>
+#include <QtCore/qregularexpression.h>
#include <QtQml/qqmllist.h>
#include <QtCore/qrect.h>
#include <QtGui/qmatrix.h>
@@ -49,7 +50,6 @@
#include <QtQml/qqmlcomponent.h>
#include <private/qqmlengine_p.h>
-#include <private/qv8engine_p.h>
#include <private/qv4qobjectwrapper_p.h>
class MyQmlAttachedObject : public QObject
@@ -101,6 +101,7 @@ class MyQmlObject : public QObject
Q_PROPERTY(QQmlListProperty<QObject> objectListProperty READ objectListProperty CONSTANT)
Q_PROPERTY(int resettableProperty READ resettableProperty WRITE setResettableProperty RESET resetProperty)
Q_PROPERTY(QRegExp regExp READ regExp WRITE setRegExp)
+ Q_PROPERTY(QRegularExpression regularExpression READ regularExpression WRITE setRegularExpression)
Q_PROPERTY(int nonscriptable READ nonscriptable WRITE setNonscriptable SCRIPTABLE false)
Q_PROPERTY(int intProperty READ intProperty WRITE setIntProperty NOTIFY intChanged)
Q_PROPERTY(QJSValue qjsvalue READ qjsvalue WRITE setQJSValue NOTIFY qjsvalueChanged)
@@ -170,6 +171,12 @@ public:
QRegExp regExp() { return m_regExp; }
void setRegExp(const QRegExp &regExp) { m_regExp = regExp; }
+ QRegularExpression regularExpression() { return m_regularExpression; }
+ void setRegularExpression(const QRegularExpression &regularExpression)
+ {
+ m_regularExpression = regularExpression;
+ }
+
int console() const { return 11; }
int nonscriptable() const { return 0; }
@@ -270,6 +277,7 @@ private:
int m_value;
int m_resetProperty;
QRegExp m_regExp;
+ QRegularExpression m_regularExpression;
QVariant m_variant;
QJSValue m_qjsvalue;
int m_intProperty;
@@ -788,11 +796,11 @@ public:
Q_INVOKABLE void method_real(qreal a) { invoke(10); m_actuals << a; }
Q_INVOKABLE void method_QString(QString a) { invoke(11); m_actuals << a; }
Q_INVOKABLE void method_QPointF(QPointF a) { invoke(12); m_actuals << a; }
- Q_INVOKABLE void method_QObject(QObject *a) { invoke(13); m_actuals << qVariantFromValue(a); }
- Q_INVOKABLE void method_QScriptValue(QJSValue a) { invoke(14); m_actuals << qVariantFromValue(a); }
- Q_INVOKABLE void method_intQScriptValue(int a, QJSValue b) { invoke(15); m_actuals << a << qVariantFromValue(b); }
+ Q_INVOKABLE void method_QObject(QObject *a) { invoke(13); m_actuals << QVariant::fromValue(a); }
+ Q_INVOKABLE void method_QScriptValue(QJSValue a) { invoke(14); m_actuals << QVariant::fromValue(a); }
+ Q_INVOKABLE void method_intQScriptValue(int a, QJSValue b) { invoke(15); m_actuals << a << QVariant::fromValue(b); }
Q_INVOKABLE void method_QByteArray(QByteArray value) { invoke(29); m_actuals << value; }
- Q_INVOKABLE QJSValue method_intQJSValue(int a, QJSValue b) { invoke(30); m_actuals << a << qVariantFromValue(b); return b.call(); }
+ Q_INVOKABLE QJSValue method_intQJSValue(int a, QJSValue b) { invoke(30); m_actuals << a << QVariant::fromValue(b); return b.call(); }
Q_INVOKABLE QJSValue method_intQJSValue(int a, int b) { m_actuals << a << b; return QJSValue();} // Should never be called.
Q_INVOKABLE void method_overload(int a) { invoke(16); m_actuals << a; }
@@ -1090,13 +1098,11 @@ class MyItemUsingRevisionedObject : public QObject
Q_PROPERTY(MyRevisionedClass *revisioned READ revisioned)
public:
- MyItemUsingRevisionedObject() {
- m_revisioned = new MyRevisionedClass;
- }
+ MyItemUsingRevisionedObject() : m_revisioned (new MyRevisionedClass) {}
- MyRevisionedClass *revisioned() const { return m_revisioned; }
+ MyRevisionedClass *revisioned() const { return m_revisioned.get(); }
private:
- MyRevisionedClass *m_revisioned;
+ QScopedPointer<MyRevisionedClass> m_revisioned;
};
QML_DECLARE_TYPE(MyRevisionedBaseClassRegistered)
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 7a9c611269..8824dfd019 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -47,6 +47,7 @@
#include <private/qv4alloca_p.h>
#include <private/qv4runtime_p.h>
#include <private/qv4object_p.h>
+#include <private/qv4script_p.h>
#include <private/qqmlcomponentattached_p.h>
#include <private/qv4objectiterator_p.h>
#include <private/qqmlabstractbinding_p.h>
@@ -236,6 +237,7 @@ private slots:
void functionAssignment_afterBinding();
void eval();
void function();
+ void topLevelGeneratorFunction();
void qtbug_10696();
void qtbug_11606();
void qtbug_11600();
@@ -376,6 +378,7 @@ private slots:
void hugeRegexpQuantifiers();
void singletonTypeWrapperLookup();
void getThisObject();
+ void semicolonAfterProperty();
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
@@ -1770,10 +1773,10 @@ void tst_qqmlecmascript::componentCreation()
}
QQmlComponent component(&engine, testUrl);
- MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create());
+ QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(component.create()));
QVERIFY(object != nullptr);
- QMetaObject::invokeMethod(object, method.toUtf8());
+ QMetaObject::invokeMethod(object.get(), method.toUtf8());
QQmlComponent *created = object->componentProperty();
if (creationError.isEmpty()) {
@@ -1781,7 +1784,7 @@ void tst_qqmlecmascript::componentCreation()
QObject *expectedParent = reinterpret_cast<QObject *>(quintptr(-1));
if (createdParent == QLatin1String("obj")) {
- expectedParent = object;
+ expectedParent = object.get();
} else if ((createdParent == QLatin1String("null")) || createdParent.isEmpty()) {
expectedParent = nullptr;
}
@@ -2506,6 +2509,13 @@ void tst_qqmlecmascript::regExpBug()
delete object;
}
+ {
+ QQmlComponent component(&engine, testFileUrl("regularExpression.qml"));
+ QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create()));
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->regularExpression().pattern(), QLatin1String("[a-zA-z]"));
+ }
+
//QTBUG-23068
{
QString err = QString(QLatin1String("%1:6 Invalid property assignment: regular expression expected; use /pattern/ syntax\n")).arg(testFileUrl("regExp.2.qml").toString());
@@ -2515,6 +2525,18 @@ void tst_qqmlecmascript::regExpBug()
QVERIFY(!object);
QCOMPARE(component.errorString(), err);
}
+
+ {
+ const QString err = QString::fromLatin1("%1:6 Invalid property assignment: "
+ "regular expression expected; "
+ "use /pattern/ syntax\n")
+ .arg(testFileUrl("regularExpression.2.qml").toString());
+ QQmlComponent component(&engine, testFileUrl("regularExpression.2.qml"));
+ QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
+ MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
+ QVERIFY(!object);
+ QCOMPARE(component.errorString(), err);
+ }
}
static inline bool evaluate_error(QV4::ExecutionEngine *v4, const QV4::Value &o, const char *source)
@@ -2569,7 +2591,7 @@ static inline bool evaluate_value(QV4::ExecutionEngine *v4, const QV4::Value &o,
scope.engine->catchException();
return false;
}
- return QV4::Runtime::method_strictEqual(value, result);
+ return QV4::Runtime::StrictEqual::call(value, result);
}
static inline QV4::ReturnedValue evaluate(QV4::ExecutionEngine *v4, const QV4::Value &o,
@@ -2899,35 +2921,35 @@ void tst_qqmlecmascript::callQtInvokables()
QCOMPARE(o->error(), false);
QCOMPARE(o->invoked(), 13);
QCOMPARE(o->actuals().count(), 1);
- QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)nullptr));
+ QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr));
o->reset();
QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", QV4::Primitive::undefinedValue()));
QCOMPARE(o->error(), false);
QCOMPARE(o->invoked(), 13);
QCOMPARE(o->actuals().count(), 1);
- QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)nullptr));
+ QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr));
o->reset();
QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", QV4::Primitive::undefinedValue()));
QCOMPARE(o->error(), false);
QCOMPARE(o->invoked(), 13);
QCOMPARE(o->actuals().count(), 1);
- QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)nullptr));
+ QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr));
o->reset();
QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", QV4::Primitive::undefinedValue()));
QCOMPARE(o->error(), false);
QCOMPARE(o->invoked(), 13);
QCOMPARE(o->actuals().count(), 1);
- QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)nullptr));
+ QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr));
o->reset();
QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", QV4::Primitive::undefinedValue()));
QCOMPARE(o->error(), false);
QCOMPARE(o->invoked(), 13);
QCOMPARE(o->actuals().count(), 1);
- QCOMPARE(o->actuals().at(0), qVariantFromValue((QObject *)o));
+ QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)o));
o->reset();
QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", QV4::Primitive::undefinedValue()));
@@ -3182,13 +3204,13 @@ void tst_qqmlecmascript::callQtInvokables()
void tst_qqmlecmascript::resolveClashingProperties()
{
- ClashingNames *o = new ClashingNames();
+ QScopedPointer<ClashingNames> o(new ClashingNames());
QQmlEngine qmlengine;
QV4::ExecutionEngine *engine = qmlengine.handle();
QV4::Scope scope(engine);
- QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine, o));
+ QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine, o.get()));
QV4::ObjectIterator it(scope, object->as<QV4::Object>(), QV4::ObjectIterator::EnumerableOnly);
QV4::ScopedValue name(scope);
QV4::ScopedValue value(scope);
@@ -3203,7 +3225,7 @@ void tst_qqmlecmascript::resolveClashingProperties()
QString key = name->toQStringNoThrow();
if (key == QLatin1String("clashes")) {
value = v;
- QV4::ScopedValue typeString(scope, QV4::Runtime::method_typeofValue(engine, value));
+ QV4::ScopedValue typeString(scope, QV4::Runtime::TypeofValue::call(engine, value));
QString type = typeString->toQStringNoThrow();
if (type == QLatin1String("boolean")) {
QVERIFY(!seenProperty);
@@ -6151,7 +6173,7 @@ void tst_qqmlecmascript::variants()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("variants.qml"));
- QObject *object = component.create();
+ QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
QCOMPARE(object->property("undefinedVariant").type(), QVariant::Invalid);
@@ -6160,13 +6182,13 @@ void tst_qqmlecmascript::variants()
QCOMPARE(object->property("doubleVariant").type(), QVariant::Double);
QVariant result;
- QMetaObject::invokeMethod(object, "checkNull", Q_RETURN_ARG(QVariant, result));
+ QMetaObject::invokeMethod(object.get(), "checkNull", Q_RETURN_ARG(QVariant, result));
QCOMPARE(result.toBool(), true);
- QMetaObject::invokeMethod(object, "checkUndefined", Q_RETURN_ARG(QVariant, result));
+ QMetaObject::invokeMethod(object.get(), "checkUndefined", Q_RETURN_ARG(QVariant, result));
QCOMPARE(result.toBool(), true);
- QMetaObject::invokeMethod(object, "checkNumber", Q_RETURN_ARG(QVariant, result));
+ QMetaObject::invokeMethod(object.get(), "checkNumber", Q_RETURN_ARG(QVariant, result));
QCOMPARE(result.toBool(), true);
}
@@ -6408,6 +6430,28 @@ void tst_qqmlecmascript::function()
delete o;
}
+// QTBUG-77096
+void tst_qqmlecmascript::topLevelGeneratorFunction()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("generatorFunction.qml"));
+
+ QScopedPointer<QObject> o {component.create()};
+ QVERIFY(o != nullptr);
+
+ // check that generator works correctly in QML
+ QCOMPARE(o->property("test1").toBool(), true);
+ QCOMPARE(o->property("test2").toBool(), true);
+ QCOMPARE(o->property("test3").toBool(), true);
+ QCOMPARE(o->property("done").toBool(), true);
+
+ // check that generator is accessible from C++
+ QVariant returnedValue;
+ QMetaObject::invokeMethod(o.get(), "gen", Q_RETURN_ARG(QVariant, returnedValue));
+ auto it = returnedValue.value<QJSValue>();
+ QCOMPARE(it.property("next").callWithInstance(it).property("value").toInt(), 1);
+}
+
// Test the "Qt.include" method
void tst_qqmlecmascript::include()
{
@@ -7034,12 +7078,12 @@ void tst_qqmlecmascript::realToInt()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("realToInt.qml"));
- MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
+ QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create()));
QVERIFY(object != nullptr);
- QMetaObject::invokeMethod(object, "test1");
+ QMetaObject::invokeMethod(object.get(), "test1");
QCOMPARE(object->value(), int(4));
- QMetaObject::invokeMethod(object, "test2");
+ QMetaObject::invokeMethod(object.get(), "test2");
QCOMPARE(object->value(), int(7));
}
@@ -7048,7 +7092,7 @@ void tst_qqmlecmascript::urlProperty()
QQmlEngine engine;
{
QQmlComponent component(&engine, testFileUrl("urlProperty.1.qml"));
- MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
+ QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create()));
QVERIFY(object != nullptr);
object->setStringProperty("http://qt-project.org");
QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html"));
@@ -7063,7 +7107,7 @@ void tst_qqmlecmascript::urlPropertyWithEncoding()
QQmlEngine engine;
{
QQmlComponent component(&engine, testFileUrl("urlProperty.2.qml"));
- MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
+ QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create()));
QVERIFY(object != nullptr);
object->setStringProperty("http://qt-project.org");
const QUrl encoded = QUrl::fromEncoded("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode);
@@ -7098,7 +7142,7 @@ void tst_qqmlecmascript::dynamicString()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("dynamicString.qml"));
- QObject *object = component.create();
+ QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
QCOMPARE(object->property("stringProperty").toString(),
QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!"));
@@ -7108,7 +7152,7 @@ void tst_qqmlecmascript::deleteLaterObjectMethodCall()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("deleteLaterObjectMethodCall.qml"));
- QObject *object = component.create();
+ QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
}
@@ -7116,7 +7160,7 @@ void tst_qqmlecmascript::automaticSemicolon()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("automaticSemicolon.qml"));
- QObject *object = component.create();
+ QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
}
@@ -7124,7 +7168,7 @@ void tst_qqmlecmascript::compatibilitySemicolon()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("compatibilitySemicolon.qml"));
- QObject *object = component.create();
+ QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
}
@@ -7132,7 +7176,7 @@ void tst_qqmlecmascript::incrDecrSemicolon1()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("incrDecrSemicolon1.qml"));
- QObject *object = component.create();
+ QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
}
@@ -7140,7 +7184,7 @@ void tst_qqmlecmascript::incrDecrSemicolon2()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("incrDecrSemicolon2.qml"));
- QObject *object = component.create();
+ QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
}
@@ -7156,7 +7200,7 @@ void tst_qqmlecmascript::unaryExpression()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("unaryExpression.qml"));
- QObject *object = component.create();
+ QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
}
@@ -7296,7 +7340,7 @@ void tst_qqmlecmascript::switchStatement()
QQmlEngine engine;
{
QQmlComponent component(&engine, testFileUrl("switchStatement.1.qml"));
- MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+ QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create()));
QVERIFY(object != nullptr);
// `object->value()' is the number of executed statements
@@ -7319,7 +7363,7 @@ void tst_qqmlecmascript::switchStatement()
{
QQmlComponent component(&engine, testFileUrl("switchStatement.2.qml"));
- MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+ QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create()));
QVERIFY(object != nullptr);
// `object->value()' is the number of executed statements
@@ -7342,7 +7386,7 @@ void tst_qqmlecmascript::switchStatement()
{
QQmlComponent component(&engine, testFileUrl("switchStatement.3.qml"));
- MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+ QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create()));
QVERIFY(object != nullptr);
// `object->value()' is the number of executed statements
@@ -7369,7 +7413,7 @@ void tst_qqmlecmascript::switchStatement()
QString warning = component.url().toString() + ":4:5: Unable to assign [undefined] to int";
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
- MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+ QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create()));
QVERIFY(object != nullptr);
// `object->value()' is the number of executed statements
@@ -7393,7 +7437,7 @@ void tst_qqmlecmascript::switchStatement()
{
QQmlComponent component(&engine, testFileUrl("switchStatement.5.qml"));
- MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+ QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create()));
QVERIFY(object != nullptr);
// `object->value()' is the number of executed statements
@@ -7416,7 +7460,7 @@ void tst_qqmlecmascript::switchStatement()
{
QQmlComponent component(&engine, testFileUrl("switchStatement.6.qml"));
- MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+ QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create()));
QVERIFY(object != nullptr);
// `object->value()' is the number of executed statements
@@ -7444,7 +7488,7 @@ void tst_qqmlecmascript::withStatement()
{
QUrl url = testFileUrl("withStatement.1.qml");
QQmlComponent component(&engine, url);
- MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+ QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create()));
QVERIFY(object != nullptr);
QCOMPARE(object->value(), 123);
@@ -7456,7 +7500,7 @@ void tst_qqmlecmascript::tryStatement()
QQmlEngine engine;
{
QQmlComponent component(&engine, testFileUrl("tryStatement.1.qml"));
- MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+ QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create()));
QVERIFY(object != nullptr);
QCOMPARE(object->value(), 123);
@@ -7464,7 +7508,7 @@ void tst_qqmlecmascript::tryStatement()
{
QQmlComponent component(&engine, testFileUrl("tryStatement.2.qml"));
- MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+ QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create()));
QVERIFY(object != nullptr);
QCOMPARE(object->value(), 321);
@@ -7472,7 +7516,7 @@ void tst_qqmlecmascript::tryStatement()
{
QQmlComponent component(&engine, testFileUrl("tryStatement.3.qml"));
- MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+ QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create()));
QVERIFY(object != nullptr);
QVERIFY(object->qjsvalue().isUndefined());
@@ -7480,7 +7524,7 @@ void tst_qqmlecmascript::tryStatement()
{
QQmlComponent component(&engine, testFileUrl("tryStatement.4.qml"));
- MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
+ QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create()));
QVERIFY(object != nullptr);
QVERIFY(object->qjsvalue().isUndefined());
@@ -7621,7 +7665,7 @@ void tst_qqmlecmascript::onDestruction()
// component instance. This shouldn't crash.
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
- QObject *obj = c.create();
+ QScopedPointer<QObject> obj(c.create());
QVERIFY(obj != nullptr);
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
}
@@ -7962,19 +8006,19 @@ void tst_qqmlecmascript::dateParse()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("date.qml"));
- QObject *object = component.create();
+ QScopedPointer<QObject> object(component.create());
if (object == nullptr)
qDebug() << component.errorString();
QVERIFY(object != nullptr);
QVariant q;
- QMetaObject::invokeMethod(object, "test_is_invalid_jsDateTime", Q_RETURN_ARG(QVariant, q));
+ QMetaObject::invokeMethod(object.get(), "test_is_invalid_jsDateTime", Q_RETURN_ARG(QVariant, q));
QVERIFY(q.toBool());
- QMetaObject::invokeMethod(object, "test_is_invalid_qtDateTime", Q_RETURN_ARG(QVariant, q));
+ QMetaObject::invokeMethod(object.get(), "test_is_invalid_qtDateTime", Q_RETURN_ARG(QVariant, q));
QVERIFY(q.toBool());
- QMetaObject::invokeMethod(object, "test_rfc2822_date", Q_RETURN_ARG(QVariant, q));
+ QMetaObject::invokeMethod(object.get(), "test_rfc2822_date", Q_RETURN_ARG(QVariant, q));
QCOMPARE(q.toLongLong(), 1379512851000LL);
}
@@ -7983,14 +8027,14 @@ void tst_qqmlecmascript::utcDate()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("utcdate.qml"));
- QObject *object = component.create();
+ QScopedPointer<QObject> object(component.create());
if (object == nullptr)
qDebug() << component.errorString();
QVERIFY(object != nullptr);
QVariant q;
QVariant val = QString::fromLatin1("2014-07-16T23:30:31");
- QMetaObject::invokeMethod(object, "check_utc", Q_RETURN_ARG(QVariant, q), Q_ARG(QVariant, val));
+ QMetaObject::invokeMethod(object.get(), "check_utc", Q_RETURN_ARG(QVariant, q), Q_ARG(QVariant, val));
QVERIFY(q.toBool());
}
@@ -7999,20 +8043,20 @@ void tst_qqmlecmascript::negativeYear()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("negativeyear.qml"));
- QObject *object = component.create();
+ QScopedPointer<QObject> object(component.create());
if (object == nullptr)
qDebug() << component.errorString();
QVERIFY(object != nullptr);
QVariant q;
- QMetaObject::invokeMethod(object, "check_negative_tostring", Q_RETURN_ARG(QVariant, q));
+ QMetaObject::invokeMethod(object.get(), "check_negative_tostring", Q_RETURN_ARG(QVariant, q));
// Only check for the year. We hope that every language writes the year in arabic numerals and
// in relation to a specific dude's date of birth. We also hope that no language adds a "-2001"
// junk string somewhere in the middle.
QVERIFY(q.toString().indexOf(QStringLiteral("-2001")) != -1);
- QMetaObject::invokeMethod(object, "check_negative_toisostring", Q_RETURN_ARG(QVariant, q));
+ QMetaObject::invokeMethod(object.get(), "check_negative_toisostring", Q_RETURN_ARG(QVariant, q));
QCOMPARE(q.toString().left(16), QStringLiteral("result: -002000-"));
}
@@ -8055,6 +8099,7 @@ void tst_qqmlecmascript::jsOwnedObjectsDeletedOnEngineDestroy()
QCOMPARE(spy1.count(), 1);
QCOMPARE(spy2.count(), 1);
+ deleteObject.deleteNestedObject();
delete object;
}
@@ -8066,7 +8111,7 @@ void tst_qqmlecmascript::updateCall()
QString file("updateCall.qml");
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl(file));
- QObject *object = component.create();
+ QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
}
@@ -8077,7 +8122,7 @@ void tst_qqmlecmascript::numberParsing()
QString file("numberParsing.%1.qml");
file = file.arg(i);
QQmlComponent component(&engine, testFileUrl(file));
- QObject *object = component.create();
+ QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
}
for (int i = 1; i < 3; ++i) {
@@ -8260,8 +8305,8 @@ void tst_qqmlecmascript::idsAsLValues()
QString err = QString(QLatin1String("%1:5: Error: left-hand side of assignment operator is not an lvalue")).arg(testFileUrl("idAsLValue.qml").toString());
QQmlComponent component(&engine, testFileUrl("idAsLValue.qml"));
QTest::ignoreMessage(QtWarningMsg, qPrintable(err));
- MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
- QVERIFY(!object);
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!qobject_cast<MyQmlObject*>(object.get()));
}
void tst_qqmlecmascript::qtbug_34792()
@@ -8334,7 +8379,9 @@ void tst_qqmlecmascript::varPropertyAccessOnObjectWithInvalidContext()
void tst_qqmlecmascript::importedScriptsAccessOnObjectWithInvalidContext()
{
QQmlEngine engine;
- QQmlComponent component(&engine, testFileUrl("importedScriptsAccessOnObjectWithInvalidContext.qml"));
+ const QUrl url = testFileUrl("importedScriptsAccessOnObjectWithInvalidContext.qml");
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(url.toString() + ":29: TypeError: Cannot read property 'Foo' of null"));
+ QQmlComponent component(&engine, url);
QScopedPointer<QObject> obj(component.create());
if (obj.isNull())
qDebug() << component.errors().first().toString();
@@ -8474,10 +8521,10 @@ void tst_qqmlecmascript::readUnregisteredQObjectProperty()
qmlRegisterType<ObjectContainer>("Test", 1, 0, "ObjectContainer");
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("accessUnregisteredQObjectProperty.qml"));
- QObject *root = component.create();
+ QScopedPointer<QObject> root(component.create());
QVERIFY(root);
- QMetaObject::invokeMethod(root, "readProperty");
+ QMetaObject::invokeMethod(root.get(), "readProperty");
QCOMPARE(root->property("container").value<ObjectContainer*>()->mGetterCalled, true);
}
@@ -8486,10 +8533,10 @@ void tst_qqmlecmascript::writeUnregisteredQObjectProperty()
qmlRegisterType<ObjectContainer>("Test", 1, 0, "ObjectContainer");
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("accessUnregisteredQObjectProperty.qml"));
- QObject *root = component.create();
+ QScopedPointer<QObject> root(component.create());
QVERIFY(root);
- QMetaObject::invokeMethod(root, "writeProperty");
+ QMetaObject::invokeMethod(root.get(), "writeProperty");
QCOMPARE(root->property("container").value<ObjectContainer*>()->mSetterCalled, true);
}
@@ -9112,8 +9159,8 @@ void tst_qqmlecmascript::singletonTypeWrapperLookup()
});
auto cleanup = qScopeGuard([&]() {
- qmlUnregisterType(singletonTypeId1);
- qmlUnregisterType(singletonTypeId2);
+ QQmlMetaType::unregisterType(singletonTypeId1);
+ QQmlMetaType::unregisterType(singletonTypeId2);
});
QQmlComponent component(&engine, testFileUrl("SingletonLookupTest.qml"));
@@ -9141,6 +9188,16 @@ void tst_qqmlecmascript::getThisObject()
QTRY_COMPARE(qvariant_cast<QObject *>(test->property("self")), test.data());
}
+// QTBUG-77954
+void tst_qqmlecmascript::semicolonAfterProperty()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("semicolonAfterProperty.qml"));
+ QVERIFY(component.isReady());
+ QScopedPointer<QObject> test(component.create());
+ QVERIFY(!test.isNull());
+}
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"
diff --git a/tests/auto/qml/qqmlengine/qqmlengine.pro b/tests/auto/qml/qqmlengine/qqmlengine.pro
index d2eb92bfd5..8946201ea0 100644
--- a/tests/auto/qml/qqmlengine/qqmlengine.pro
+++ b/tests/auto/qml/qqmlengine/qqmlengine.pro
@@ -4,6 +4,8 @@ macx:CONFIG -= app_bundle
include (../../shared/util.pri)
+TESTDATA = data/*
+
SOURCES += tst_qqmlengine.cpp
QT += core-private gui-private qml-private network testlib
diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
index 0cb6753020..64f167b47e 100644
--- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
+++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
@@ -43,6 +43,7 @@
#include <QQmlIncubationController>
#include <QTemporaryDir>
#include <private/qqmlengine_p.h>
+#include <private/qqmltypedata_p.h>
#include <QQmlAbstractUrlInterceptor>
class tst_qqmlengine : public QQmlDataTest
@@ -427,7 +428,7 @@ void tst_qqmlengine::trimComponentCache()
engine.setIncubationController(&componentCache);
QQmlComponent component(&engine, testFileUrl(file));
- QVERIFY(component.isReady());
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
QCOMPARE(object->property("success").toBool(), true);
@@ -741,13 +742,17 @@ public:
CustomSelector(const QUrl &base):m_base(base){}
virtual QUrl intercept(const QUrl &url, QQmlAbstractUrlInterceptor::DataType d)
{
- if (url.scheme() != QStringLiteral("file"))
+ if ((url.scheme() != QStringLiteral("file") && url.scheme() != QStringLiteral("qrc"))
+ || url.path().contains("QtQml"))
return url;
if (!m_interceptionPoints.contains(d))
return url;
- if (url.path().endsWith("Test.2/qmldir"))//Special case
- return QUrl::fromLocalFile(m_base.path() + "interception/module/intercepted/qmldir");
+ if (url.path().endsWith("Test.2/qmldir")) {//Special case
+ QUrl url = m_base;
+ url.setPath(m_base.path() + "interception/module/intercepted/qmldir");
+ return url;
+ }
// Special case: with 5.10 we always add the implicit import, so we need to explicitly handle this case now
if (url.path().endsWith("intercepted/qmldir"))
return url;
@@ -835,7 +840,7 @@ void tst_qqmlengine::urlInterceptor()
QFETCH(QString, expectedAbsoluteUrl);
QQmlEngine e;
- e.setImportPathList(QStringList() << testFileUrl("interception/imports").toLocalFile());
+ e.addImportPath(testFileUrl("interception/imports").url());
CustomSelector cs(testFileUrl(""));
cs.m_interceptionPoints = interceptionPoint;
e.setUrlInterceptor(&cs);
@@ -935,7 +940,7 @@ void tst_qqmlengine::cppSignalAndEval()
{
ObjectCaller objectCaller;
QQmlEngine engine;
- engine.rootContext()->setContextProperty(QLatin1Literal("CallerCpp"), &objectCaller);
+ engine.rootContext()->setContextProperty(QLatin1String("CallerCpp"), &objectCaller);
QQmlComponent c(&engine);
c.setData("import QtQuick 2.9\n"
"Item {\n"
@@ -1015,6 +1020,65 @@ void tst_qqmlengine::singletonInstance()
}
{
+ int data = 30;
+ auto id = qmlRegisterSingletonType<CppSingleton>("Qt.test",1,0,"CapturingLambda",[data](QQmlEngine*, QJSEngine*){ // register qobject singleton with capturing lambda
+ auto o = new CppSingleton;
+ o->setProperty("data", data);
+ return o;
+ });
+ QJSValue value = engine.singletonInstance<QJSValue>(id);
+ QVERIFY(!value.isUndefined());
+ QVERIFY(value.isQObject());
+ QObject *instance = value.toQObject();
+ QVERIFY(instance);
+ QCOMPARE(instance->metaObject()->className(), "CppSingleton");
+ QCOMPARE(instance->property("data"), data);
+ }
+ {
+ qmlRegisterSingletonType<CppSingleton>("Qt.test",1,0,"NotAmbiguous", [](QQmlEngine* qeng, QJSEngine* jeng) -> QObject* {return CppSingleton::create(qeng, jeng);}); // test that overloads for qmlRegisterSingleton are not ambiguous
+ }
+ {
+ // Register QObject* directly
+ CppSingleton single;
+ int id = qmlRegisterSingletonInstance("Qt.test", 1, 0, "CppOwned",
+ &single);
+ QQmlEngine engine2;
+ CppSingleton *singlePtr = engine2.singletonInstance<CppSingleton *>(id);
+ QVERIFY(singlePtr);
+ QCOMPARE(&single, singlePtr);
+ QVERIFY(engine2.objectOwnership(singlePtr) == QQmlEngine::CppOwnership);
+ }
+
+ {
+ CppSingleton single;
+ QQmlEngine engineA;
+ QQmlEngine engineB;
+ int id = qmlRegisterSingletonInstance("Qt.test", 1, 0, "CppOwned", &single);
+ auto singlePtr = engineA.singletonInstance<CppSingleton *>(id);
+ QVERIFY(singlePtr);
+ singlePtr = engineA.singletonInstance<CppSingleton *>(id); // accessing the singleton multiple times from the same engine is fine
+ QVERIFY(singlePtr);
+ QTest::ignoreMessage(QtMsgType::QtCriticalMsg, "<Unknown File>: qmlRegisterSingletonType(): \"CppOwned\" is not available because the callback function returns a null pointer.");
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, "<Unknown File>: Singleton registered by registerSingletonInstance must only be accessed from one engine");
+ QCOMPARE(&single, singlePtr);
+ auto noSinglePtr = engineB.singletonInstance<CppSingleton *>(id);
+ QVERIFY(!noSinglePtr);
+ }
+
+ {
+ CppSingleton single;
+ QThread newThread {};
+ single.moveToThread(&newThread);
+ QCOMPARE(single.thread(), &newThread);
+ QQmlEngine engineB;
+ int id = qmlRegisterSingletonInstance("Qt.test", 1, 0, "CppOwned", &single);
+ QTest::ignoreMessage(QtMsgType::QtCriticalMsg, "<Unknown File>: qmlRegisterSingletonType(): \"CppOwned\" is not available because the callback function returns a null pointer.");
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, "<Unknown File>: Registered object must live in the same thread as the engine it was registered with");
+ auto noSinglePtr = engineB.singletonInstance<CppSingleton *>(id);
+ QVERIFY(!noSinglePtr);
+ }
+
+ {
// Invalid types
QJSValue value;
value = engine.singletonInstance<QJSValue>(-4711);
@@ -1043,6 +1107,16 @@ void tst_qqmlengine::singletonInstance()
SomeQObjectClass * instance = engine.singletonInstance<SomeQObjectClass*>(cppSingletonTypeId);
QVERIFY(!instance);
}
+
+ {
+ // deleted object
+ auto dayfly = new QObject{};
+ auto id = qmlRegisterSingletonInstance("Vanity", 1, 0, "Dayfly", dayfly);
+ delete dayfly;
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, "<Unknown File>: The registered singleton has already been deleted. Ensure that it outlives the engine.");
+ QObject *instance = engine.singletonInstance<QObject*>(id);
+ QVERIFY(!instance);
+ }
}
void tst_qqmlengine::aggressiveGc()
diff --git a/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp b/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp
index 7a7185e909..f282e60417 100644
--- a/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp
+++ b/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp
@@ -195,7 +195,7 @@ void tst_qqmlerror::debug()
}
{
- QUrl url(dataDirectoryUrl().resolved(QUrl("test.txt")));
+ QUrl url = testFileUrl("test.txt");
QQmlError error;
error.setUrl(url);
error.setDescription("An Error");
diff --git a/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp b/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp
index 1decc04ad2..0ba29d6b6a 100644
--- a/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp
+++ b/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp
@@ -28,6 +28,7 @@
#include <qtest.h>
#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlfile.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlexpression.h>
#include <QtQml/qqmlscriptstring.h>
@@ -125,10 +126,12 @@ void tst_qqmlexpression::expressionFromDataComponent()
QQmlEngine engine;
QQmlComponent c(&engine);
- QUrl url = testFileUrl("expressionFromDataComponent.qml");
+ const QString fn(QLatin1String("expressionFromDataComponent.qml"));
+ QUrl url = testFileUrl(fn);
+ QString path = testFile(fn);
{
- QFile f(url.toLocalFile());
+ QFile f(path);
QVERIFY(f.open(QIODevice::ReadOnly));
c.setData(f.readAll(), url);
}
diff --git a/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp b/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp
index 341a49bf09..16b8fe578d 100644
--- a/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp
+++ b/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp
@@ -47,18 +47,21 @@ class tst_qqmlextensionplugin : public QObject
{
Q_OBJECT
- bool isDuplicate(QString file, const QList<QString> & files) {
-#ifndef DEBUG_SUFFIX
- Q_UNUSED(file)
- Q_UNUSED(files)
- return false;
-#else
+ static QStringList removeDuplicates(QStringList files) {
+#ifdef DEBUG_SUFFIX
+ const auto isDuplicate = [files] (QString file) {
# ifdef QT_DEBUG
- return !file.endsWith(DEBUG_SUFFIX) && files.contains(file.replace(SUFFIX, DEBUG_SUFFIX));
+ return !file.endsWith(DEBUG_SUFFIX) && files.contains(file.replace(SUFFIX, DEBUG_SUFFIX));
# else
- return file.endsWith(DEBUG_SUFFIX) && files.contains(file.replace(DEBUG_SUFFIX, SUFFIX));
+ return file.endsWith(DEBUG_SUFFIX) && files.contains(file.replace(DEBUG_SUFFIX, SUFFIX));
# endif
+ };
+
+ files.erase(std::remove_if(files.begin(), files.end(), isDuplicate),
+ files.end());
+
#endif
+ return files;
}
public:
@@ -84,12 +87,7 @@ void tst_qqmlextensionplugin::iidCheck_data()
}
}
- for (QMutableListIterator<QString> it(files); it.hasNext(); ) {
- QString file = it.next();
- if (isDuplicate(file, files)) {
- it.remove();
- }
- }
+ files = removeDuplicates(std::move(files));
QTest::addColumn<QString>("filePath");
foreach (const QString &file, files) {
diff --git a/tests/auto/qml/qqmlimport/qqmlimport.pro b/tests/auto/qml/qqmlimport/qqmlimport.pro
index c8b0f68d9f..ba80547f4e 100644
--- a/tests/auto/qml/qqmlimport/qqmlimport.pro
+++ b/tests/auto/qml/qqmlimport/qqmlimport.pro
@@ -6,3 +6,8 @@ osx:CONFIG -= app_bundle
SOURCES += tst_qqmlimport.cpp
include (../../shared/util.pri)
+
+TESTDATA = data/* \
+ MyPluginSupported/* \
+ MyPluginUnsupported/* \
+ FormFromQmlDir/*
diff --git a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
index a9657eba1d..ca1e52ad2c 100644
--- a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
+++ b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
@@ -78,7 +78,7 @@ void tst_QQmlImport::testDesignerSupported()
QVERIFY(window->errors().isEmpty());
QString warningString("%1:30:1: module does not support the designer \"MyPluginUnsupported\" \n import MyPluginUnsupported 1.0\r \n ^ ");
-#ifndef Q_OS_WIN
+#if !defined(Q_OS_WIN) && !defined(Q_OS_ANDROID)
warningString.remove('\r');
#endif
warningString = warningString.arg(testFileUrl("testfile_unsupported.qml").toString());
@@ -130,6 +130,9 @@ void tst_QQmlImport::uiFormatLoading()
void tst_QQmlImport::importPathOrder()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath) returns bogus path on Android, but its nevertheless unusable.");
+#endif
QStringList expectedImportPaths;
QString appDirPath = QCoreApplication::applicationDirPath();
QString qml2Imports = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath);
diff --git a/tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro b/tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro
index 542ec44736..e8ed8e91b1 100644
--- a/tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro
+++ b/tests/auto/qml/qqmlinstantiator/qqmlinstantiator.pro
@@ -9,4 +9,4 @@ include (../../shared/util.pri)
TESTDATA = data/*
-QT += core-private gui-private qml-private testlib
+QT += core-private gui-private qml-private testlib qmlmodels-private
diff --git a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
index a66f13e6bb..9c5e09c77c 100644
--- a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
+++ b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
@@ -31,7 +31,7 @@
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
-#include <QtQml/private/qqmlinstantiator_p.h>
+#include <QtQmlModels/private/qqmlinstantiator_p.h>
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlincubator.h>
#include "../../shared/util.h"
diff --git a/tests/auto/qml/qqmllanguage/data/alias.17.qml b/tests/auto/qml/qqmllanguage/data/alias.17.qml
new file mode 100644
index 0000000000..a76dd120b6
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/alias.17.qml
@@ -0,0 +1,31 @@
+import QtQuick 2.12
+
+Item {
+ id: root
+ anchors.fill: parent
+ width: 100
+ height: 100
+ property bool success: checkValue === aliasUser.topMargin
+ property int checkValue: 42
+ Rectangle {
+ id: myItem
+ objectName: "myItem"
+ color: "blue"
+ anchors.topMargin: root.checkValue
+ width: 50
+ height: 50
+ Text {text: "source:\n" + myItem.anchors.topMargin}
+ }
+
+ Rectangle {
+ property alias topMargin: myItem.anchors.topMargin
+ id: aliasUser
+ objectName: "aliasUser"
+ color: "red"
+ anchors.left: myItem.right
+ width: 50
+ height: 50
+ Text {objectName: "myText"; text: "alias:\n" + aliasUser.topMargin}
+ }
+}
+
diff --git a/tests/auto/qml/qqmllanguage/data/bareQmlImport.errors.txt b/tests/auto/qml/qqmllanguage/data/bareQmlImport.errors.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/bareQmlImport.errors.txt
diff --git a/tests/auto/qml/qqmllanguage/data/bareQmlImport.qml b/tests/auto/qml/qqmllanguage/data/bareQmlImport.qml
new file mode 100644
index 0000000000..3567cdb5a3
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/bareQmlImport.qml
@@ -0,0 +1,6 @@
+import QML 1.0
+QtObject {
+ property Component component: Component {
+ QtObject {}
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/fakeDotProperty.errors.txt b/tests/auto/qml/qqmllanguage/data/fakeDotProperty.errors.txt
index 30748234bc..5a144f2db5 100644
--- a/tests/auto/qml/qqmllanguage/data/fakeDotProperty.errors.txt
+++ b/tests/auto/qml/qqmllanguage/data/fakeDotProperty.errors.txt
@@ -1 +1 @@
-3:5:Invalid grouped property access
+3:5:Invalid grouped property access: Property "value" with primitive type "int".
diff --git a/tests/auto/qml/qqmllanguage/data/functionParameterTypes.qml b/tests/auto/qml/qqmllanguage/data/functionParameterTypes.qml
new file mode 100644
index 0000000000..26931a4f10
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/functionParameterTypes.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0 as QQ
+import QtQml 2.0 as Core
+QQ.Item {
+ id: root
+ function returnItem() : Core.QtObject { return root; }
+ function takeString(arg: string) { return arg; }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/fuzzed.2.errors.txt b/tests/auto/qml/qqmllanguage/data/fuzzed.2.errors.txt
index 92ce4c649f..8dca84b34e 100644
--- a/tests/auto/qml/qqmllanguage/data/fuzzed.2.errors.txt
+++ b/tests/auto/qml/qqmllanguage/data/fuzzed.2.errors.txt
@@ -1,2 +1 @@
5:1:TetZ$ is not a type
--1:-1:Invalid QML type name "TetZ$"
diff --git a/tests/auto/qml/qqmllanguage/data/fuzzed.2.qml b/tests/auto/qml/qqmllanguage/data/fuzzed.2.qml
index e726f6783c..f164ec98ea 100644
--- a/tests/auto/qml/qqmllanguage/data/fuzzed.2.qml
+++ b/tests/auto/qml/qqmllanguage/data/fuzzed.2.qml
Binary files differ
diff --git a/tests/auto/qml/qqmllanguage/data/fuzzed.3.errors.txt b/tests/auto/qml/qqmllanguage/data/fuzzed.3.errors.txt
new file mode 100644
index 0000000000..da17dc5599
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/fuzzed.3.errors.txt
@@ -0,0 +1,2 @@
+3:2:Unexpected token `version number'
+1:1:Expected a qualified name id or a string literal
diff --git a/tests/auto/qml/qqmllanguage/data/fuzzed.3.qml b/tests/auto/qml/qqmllanguage/data/fuzzed.3.qml
new file mode 100644
index 0000000000..6861ebf8a9
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/fuzzed.3.qml
Binary files differ
diff --git a/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.2.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.2.errors.txt
index 810fd31b41..5deec4ccf9 100644
--- a/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.2.errors.txt
+++ b/tests/auto/qml/qqmllanguage/data/invalidGroupedProperty.2.errors.txt
@@ -1 +1 @@
-5:5:Invalid grouped property access
+5:5:Invalid grouped property access: Property "o" with primitive type "int".
diff --git a/tests/auto/qml/qqmllanguage/data/objectValueTypeProperty.errors.txt b/tests/auto/qml/qqmllanguage/data/objectValueTypeProperty.errors.txt
index 945dacf8ab..887d87b9fb 100644
--- a/tests/auto/qml/qqmllanguage/data/objectValueTypeProperty.errors.txt
+++ b/tests/auto/qml/qqmllanguage/data/objectValueTypeProperty.errors.txt
@@ -1 +1 @@
-4:18:Can not assign value of type "int" to property "x", expecting an object
+4:18:Cannot assign value of type "MyTypeObject" to property "x", expecting "int"
diff --git a/tests/auto/qml/qqmllanguage/data/propertyUnknownType.errors.txt b/tests/auto/qml/qqmllanguage/data/propertyUnknownType.errors.txt
new file mode 100644
index 0000000000..1655f9264a
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/propertyUnknownType.errors.txt
@@ -0,0 +1 @@
+3:23:Cannot assign to property of unknown type "SomethingUnknown*".
diff --git a/tests/auto/qml/qqmllanguage/data/propertyUnknownType.qml b/tests/auto/qml/qqmllanguage/data/propertyUnknownType.qml
new file mode 100644
index 0000000000..c22fd65350
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/propertyUnknownType.qml
@@ -0,0 +1,4 @@
+import Test 1.0
+MyQmlObject {
+ somethingUnknown: SomethingKnown {}
+}
diff --git a/tests/auto/qml/qqmllanguage/data/signalParameterTypes.3.qml b/tests/auto/qml/qqmllanguage/data/signalParameterTypes.3.qml
new file mode 100644
index 0000000000..6dee30de95
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/signalParameterTypes.3.qml
@@ -0,0 +1,19 @@
+import QtQml 2.0
+
+QtObject {
+ id: root
+
+ property bool success: false
+
+ signal testSignal(param: bool)
+
+ function handleTestSignal(param) {
+ success = param
+ }
+
+ Component.onCompleted: {
+ success = false;
+ root.testSignal.connect(handleTestSignal)
+ root.testSignal(true);
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/typeAnnotations.2.errors.txt b/tests/auto/qml/qqmllanguage/data/typeAnnotations.2.errors.txt
new file mode 100644
index 0000000000..b316acae30
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/typeAnnotations.2.errors.txt
@@ -0,0 +1 @@
+5:14:Type annotations are not permitted in variable declarations
diff --git a/tests/auto/qml/qqmllanguage/data/typeAnnotations.2.qml b/tests/auto/qml/qqmllanguage/data/typeAnnotations.2.qml
new file mode 100644
index 0000000000..655fe4c226
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/typeAnnotations.2.qml
@@ -0,0 +1,8 @@
+import QtQml 2.0
+
+QtObject {
+ function notYet() {
+ var x: string = "ko"
+ return x
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/qqmllanguage.pro b/tests/auto/qml/qqmllanguage/qqmllanguage.pro
index 3e88f3f0db..724a27320c 100644
--- a/tests/auto/qml/qqmllanguage/qqmllanguage.pro
+++ b/tests/auto/qml/qqmllanguage/qqmllanguage.pro
@@ -17,3 +17,5 @@ include (../../shared/util.pri)
OTHER_FILES += \
data/readonlyObjectProperty.qml
+
+android: RESOURCES += qqmllanguage.qrc
diff --git a/tests/auto/qml/qqmllanguage/qqmllanguage.qrc b/tests/auto/qml/qqmllanguage/qqmllanguage.qrc
new file mode 100644
index 0000000000..f5212ac75c
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/qqmllanguage.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/">
+<file alias="data/I18nTypeÁâãäå.qml">data/I18nType30.qml</file>
+</qresource>
+</RCC>
diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp
index f9a6ee8e5a..6956533196 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.cpp
+++ b/tests/auto/qml/qqmllanguage/testtypes.cpp
@@ -27,6 +27,8 @@
****************************************************************************/
#include "testtypes.h"
+#include <private/qv4qmlcontext_p.h>
+
static QObject *myTypeObjectSingleton(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
@@ -60,6 +62,7 @@ void registerTypes()
qmlRegisterType<MyRevisionedClass,1>("Test",1,1,"MyRevisionedClass");
qmlRegisterType<MyRevisionedIllegalOverload>("Test",1,0,"MyRevisionedIllegalOverload");
qmlRegisterType<MyRevisionedLegalOverload>("Test",1,0,"MyRevisionedLegalOverload");
+ qmlRegisterType<SomethingKnown>("Test",1,0,"SomethingKnown");
// Register the uncreatable base class
qmlRegisterRevision<MyRevisionedBaseClassRegistered,1>("Test",1,1);
@@ -125,7 +128,7 @@ QVariant myCustomVariantTypeConverter(const QString &data)
}
-void CustomBindingParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
+void CustomBindingParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
{
CustomBinding *customBinding = qobject_cast<CustomBinding*>(object);
Q_ASSERT(customBinding);
@@ -154,7 +157,7 @@ void CustomBinding::componentComplete()
}
}
-void EnumSupportingCustomParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
+void EnumSupportingCustomParser::verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
{
if (bindings.count() != 1) {
error(bindings.first(), QStringLiteral("Custom parser invoked incorrectly for unit test"));
@@ -184,7 +187,7 @@ void EnumSupportingCustomParser::verifyBindings(const QQmlRefPointer<QV4::Compil
}
}
-void SimpleObjectCustomParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &bindings)
+void SimpleObjectCustomParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &bindings)
{
SimpleObjectWithCustomParser *o = qobject_cast<SimpleObjectWithCustomParser*>(object);
Q_ASSERT(o);
diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h
index bb6e9582c2..1aab24841a 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.h
+++ b/tests/auto/qml/qqmllanguage/testtypes.h
@@ -89,6 +89,14 @@ private:
int m_value2;
};
+class SomethingUnknown : public QObject {
+ Q_OBJECT
+};
+
+class SomethingKnown : public SomethingUnknown {
+ Q_OBJECT
+};
+
class MyQmlObject : public QObject, public MyInterface
{
Q_OBJECT
@@ -104,6 +112,7 @@ class MyQmlObject : public QObject, public MyInterface
Q_PROPERTY(int propertyWithNotify READ propertyWithNotify WRITE setPropertyWithNotify NOTIFY oddlyNamedNotifySignal)
Q_PROPERTY(int nonScriptable READ nonScriptable WRITE setNonScriptable SCRIPTABLE false)
Q_PROPERTY(QJSValue qjsvalue READ qjsvalue WRITE setQJSValue NOTIFY qjsvalueChanged)
+ Q_PROPERTY(SomethingUnknown* somethingUnknown READ somethingUnknown WRITE setSomethingUnknown NOTIFY somethingUnknownChanged)
Q_INTERFACES(MyInterface)
public:
@@ -151,6 +160,9 @@ public:
int childAddedEventCount() const { return m_childAddedEventCount; }
+ SomethingUnknown* somethingUnknown() const { return nullptr; }
+ void setSomethingUnknown(SomethingUnknown* something) { Q_UNUSED(something); }
+
public slots:
void basicSlot() { qWarning("MyQmlObject::basicSlot"); }
void basicSlotWithArgs(int v) { qWarning("MyQmlObject::basicSlotWithArgs(%d)", v); }
@@ -162,6 +174,7 @@ signals:
void oddlyNamedNotifySignal();
void signalWithDefaultArg(int parameter = 5);
void qjsvalueChanged();
+ void somethingUnknownChanged();
protected:
virtual bool event(QEvent *event);
@@ -781,15 +794,15 @@ class MyCustomParserType : public QObject
class MyCustomParserTypeParser : public QQmlCustomParser
{
public:
- virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {}
- virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {}
+ virtual void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {}
+ virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {}
};
class EnumSupportingCustomParser : public QQmlCustomParser
{
public:
- virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &);
- virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {}
+ virtual void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &);
+ virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {}
};
class MyParserStatus : public QObject, public QQmlParserStatus
@@ -1275,15 +1288,15 @@ public:
void setTarget(QObject *newTarget) { m_target = newTarget; }
QPointer<QObject> m_target;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
QList<const QV4::CompiledData::Binding*> bindings;
QByteArray m_bindingData;
};
class CustomBindingParser : public QQmlCustomParser
{
- virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {}
- virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &);
+ virtual void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {}
+ virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &);
};
class SimpleObjectWithCustomParser : public QObject
@@ -1328,8 +1341,8 @@ private:
class SimpleObjectCustomParser : public QQmlCustomParser
{
- virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {}
- virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &);
+ virtual void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) {}
+ virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &);
};
class RootObjectInCreationTester : public QObject
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 63fc55ad0f..fae74f1f25 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -208,6 +208,7 @@ private slots:
void remoteLoadCrash();
void signalWithDefaultArg();
void signalParameterTypes();
+ void functionParameterTypes();
// regression tests for crashes
void crash1();
@@ -242,12 +243,11 @@ private slots:
void compositeSingletonModuleQualified();
void compositeSingletonInstantiateError();
void compositeSingletonDynamicPropertyError();
- void compositeSingletonDynamicSignal();
+ void compositeSingletonDynamicSignalAndJavaScriptPragma();
void compositeSingletonQmlRegisterTypeError();
void compositeSingletonQmldirNoPragmaError();
void compositeSingletonQmlDirError();
void compositeSingletonRemote();
- void compositeSingletonJavaScriptPragma();
void compositeSingletonSelectors();
void compositeSingletonRegistered();
void compositeSingletonCircular();
@@ -369,7 +369,8 @@ private:
void tst_qqmllanguage::cleanupTestCase()
{
- QVERIFY(QFile::remove(testFile(QString::fromUtf8("I18nType\303\201\303\242\303\243\303\244\303\245.qml"))));
+ if (dataDirectoryUrl().scheme() != QLatin1String("qrc"))
+ QVERIFY(QFile::remove(testFile(QString::fromUtf8("I18nType\303\201\303\242\303\243\303\244\303\245.qml"))));
}
void tst_qqmllanguage::insertedSemicolon_data()
@@ -620,11 +621,29 @@ void tst_qqmllanguage::errors_data()
QTest::newRow("fuzzed.1") << "fuzzed.1.qml" << "fuzzed.1.errors.txt" << false;
QTest::newRow("fuzzed.2") << "fuzzed.2.qml" << "fuzzed.2.errors.txt" << false;
-}
+ QTest::newRow("fuzzed.3") << "fuzzed.3.qml" << "fuzzed.3.errors.txt" << false;
+
+ QTest::newRow("bareQmlImport") << "bareQmlImport.qml" << "bareQmlImport.errors.txt" << false;
+
+ QTest::newRow("typeAnnotations.2") << "typeAnnotations.2.qml" << "typeAnnotations.2.errors.txt" << false;
+ QTest::newRow("propertyUnknownType") << "propertyUnknownType.qml" << "propertyUnknownType.errors.txt" << false;
+}
void tst_qqmllanguage::errors()
{
+#ifdef Q_OS_ANDROID
+ if (qstrcmp(QTest::currentDataTag(), "fuzzed.2") == 0) {
+ QSKIP("Gives different errors on Android");
+ /* Only gives one error on Android:
+
+ qrc:/data/fuzzed.2.qml:1:1: "
+ import"
+ ^
+ So, it seems to complain about the first import (which is understandable)
+ */
+ }
+#endif
QFETCH(QString, file);
QFETCH(QString, errorFile);
QFETCH(bool, create);
@@ -1458,8 +1477,8 @@ void tst_qqmllanguage::dynamicObjectProperties()
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
- QCOMPARE(object->property("objectProperty"), qVariantFromValue((QObject*)nullptr));
- QVERIFY(object->property("objectProperty2") != qVariantFromValue((QObject*)nullptr));
+ QCOMPARE(object->property("objectProperty"), QVariant::fromValue((QObject*)nullptr));
+ QVERIFY(object->property("objectProperty2") != QVariant::fromValue((QObject*)nullptr));
}
{
QQmlComponent component(&engine, testFileUrl("dynamicObjectProperties.2.qml"));
@@ -1467,7 +1486,7 @@ void tst_qqmllanguage::dynamicObjectProperties()
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
- QVERIFY(object->property("objectProperty") != qVariantFromValue((QObject*)nullptr));
+ QVERIFY(object->property("objectProperty") != QVariant::fromValue((QObject*)nullptr));
}
}
@@ -1736,7 +1755,7 @@ void tst_qqmllanguage::aliasProperties()
// Write through alias
MyQmlObject *v2 = new MyQmlObject();
v2->setParent(object.data());
- object->setProperty("aliasObject", qVariantFromValue(v2));
+ object->setProperty("aliasObject", QVariant::fromValue(v2));
MyQmlObject *v3 =
qvariant_cast<MyQmlObject *>(object->property("aliasObject"));
QVERIFY(v3 != nullptr);
@@ -1951,6 +1970,69 @@ void tst_qqmllanguage::aliasProperties()
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
}
+
+ // Alias to grouped property
+ {
+ QQmlComponent component(&engine, testFileUrl("alias.17.qml"));
+ VERIFY_ERRORS(0);
+
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QVERIFY(object->property("success").toBool());
+ }
+
+ // Alias to grouped property updates
+ {
+ QQmlComponent component(&engine, testFileUrl("alias.17.qml"));
+ VERIFY_ERRORS(0);
+
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QObject *aliasUser = object->findChild<QObject*>(QLatin1String("aliasUser"));
+ QVERIFY(aliasUser);
+ QQmlProperty checkValueProp(object.get(), "checkValue");
+ QVERIFY(checkValueProp.isValid());
+ checkValueProp.write(777);
+ QCOMPARE(object->property("checkValue").toInt(), 777);
+ QCOMPARE(aliasUser->property("topMargin").toInt(), 777);
+ }
+
+ // Write to alias to grouped property
+ {
+ QQmlComponent component(&engine, testFileUrl("alias.17.qml"));
+ VERIFY_ERRORS(0);
+
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QObject *aliasUser = object->findChild<QObject*>(QLatin1String("aliasUser"));
+ QVERIFY(aliasUser);
+ QQmlProperty topMarginProp {aliasUser, "topMargin"};
+ QVERIFY(topMarginProp.isValid());
+ topMarginProp.write(777);
+ QObject *myItem = object->findChild<QObject*>(QLatin1String("myItem"));
+ QVERIFY(myItem);
+ auto anchors = myItem->property("anchors").value<QObject*>();
+ QVERIFY(anchors);
+ QCOMPARE(anchors->property("topMargin").toInt(), 777);
+ }
+
+ // Binding to alias to grouped property gets updated
+ {
+ QQmlComponent component(&engine, testFileUrl("alias.17.qml"));
+ VERIFY_ERRORS(0);
+
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QObject *aliasUser = object->findChild<QObject*>(QLatin1String("aliasUser"));
+ QVERIFY(aliasUser);
+ QQmlProperty topMarginProp {aliasUser, "topMargin"};
+ QVERIFY(topMarginProp.isValid());
+ topMarginProp.write(20);
+ QObject *myText = object->findChild<QObject*>(QLatin1String("myText"));
+ QVERIFY(myText);
+ auto text = myText->property("text").toString();
+ QCOMPARE(text, "alias:\n20");
+ }
}
// QTBUG-13374 Test that alias properties and signals can coexist
@@ -2223,7 +2305,7 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode()
QV4::CompiledData::Unit *qmlUnit = reinterpret_cast<QV4::CompiledData::Unit *>(malloc(readOnlyQmlUnit->unitSize));
memcpy(qmlUnit, readOnlyQmlUnit, readOnlyQmlUnit->unitSize);
qmlUnit->flags &= ~QV4::CompiledData::Unit::StaticData;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = td->compilationUnit();
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit = td->compilationUnit();
compilationUnit->setUnitData(qmlUnit);
const QV4::CompiledData::Object *rootObject = compilationUnit->objectAt(/*root object*/0);
@@ -2233,9 +2315,9 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode()
const QV4::CompiledData::Binding *binding = rootObject->bindingTable() + i;
if (compilationUnit->stringAt(binding->propertyNameIndex) != QString("scriptProperty"))
continue;
- QCOMPARE(binding->valueAsScriptString(compilationUnit.data()), QString("intProperty"));
+ QCOMPARE(compilationUnit->bindingValueAsScriptString(binding), QString("intProperty"));
const_cast<QV4::CompiledData::Binding*>(binding)->stringIndex = 0; // empty string index
- QVERIFY(binding->valueAsScriptString(compilationUnit.data()).isEmpty());
+ QVERIFY(compilationUnit->bindingValueAsScriptString(binding).isEmpty());
break;
}
QVERIFY(i < rootObject->nBindings);
@@ -2481,7 +2563,15 @@ void tst_qqmllanguage::testType(const QString& qml, const QString& type, const Q
VERIFY_ERRORS(0);
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
- QCOMPARE(QString(object->metaObject()->className()), type);
+ const QMetaObject *meta = object->metaObject();
+ for (; meta; meta = meta->superClass()) {
+ const QString className(meta->className());
+ if (!className.contains("_QMLTYPE_") && !className.contains("_QML_")) {
+ QCOMPARE(className, type);
+ break;
+ }
+ }
+ QVERIFY(meta != nullptr);
}
engine.setImportPathList(defaultImportPathList);
@@ -2673,11 +2763,15 @@ void tst_qqmllanguage::importsLocal_data()
"Test {}"
<< (!qmlCheckTypes()?"TestType":"")
<< (!qmlCheckTypes()?"":"Test is ambiguous. Found in org/qtproject/Test/ and in subdir/");
- QTest::newRow("file URL survives percent-encoding")
- << "import \"" + QUrl::fromLocalFile(QDir::currentPath() + "/{subdir}").toString() + "\"\n"
- "Test {}"
- << "QQuickRectangle"
- << "";
+
+ if (dataDirectoryUrl().scheme() != QLatin1String("qrc")) {
+ // file URL doesn't work with qrc scheme
+ QTest::newRow("file URL survives percent-encoding")
+ << "import \"" + QUrl::fromLocalFile(QDir::currentPath() + "/{subdir}").toString() + "\"\n"
+ "Test {}"
+ << "QQuickRectangle"
+ << "";
+ }
}
void tst_qqmllanguage::importsLocal()
@@ -3436,7 +3530,11 @@ void tst_qqmllanguage::uncreatableTypesAsProperties()
void tst_qqmllanguage::initTestCase()
{
QQmlDataTest::initTestCase();
- QVERIFY2(QDir::setCurrent(dataDirectory()), qPrintable("Could not chdir to " + dataDirectory()));
+ if (dataDirectoryUrl().scheme() == QLatin1String("qrc"))
+ engine.addImportPath(dataDirectory());
+ else
+ QVERIFY2(QDir::setCurrent(dataDirectory()), qPrintable("Could not chdir to " + dataDirectory()));
+
defaultImportPathList = engine.importPathList();
@@ -3467,11 +3565,13 @@ void tst_qqmllanguage::initTestCase()
// For POSIX, this will just be data/I18nType.qml, since POSIX is 7-bit
// For iso8859-1 locale, this will just be data/I18nType?????.qml where ????? is 5 8-bit characters
// For utf-8 locale, this will be data/I18nType??????????.qml where ?????????? is 5 8-bit characters, UTF-8 encoded
- QFile in(testFileUrl(QLatin1String("I18nType30.qml")).toLocalFile());
- QVERIFY2(in.open(QIODevice::ReadOnly), qPrintable(QString::fromLatin1("Cannot open '%1': %2").arg(in.fileName(), in.errorString())));
- QFile out(testFileUrl(QString::fromUtf8("I18nType\303\201\303\242\303\243\303\244\303\245.qml")).toLocalFile());
- QVERIFY2(out.open(QIODevice::WriteOnly), qPrintable(QString::fromLatin1("Cannot open '%1': %2").arg(out.fileName(), out.errorString())));
- out.write(in.readAll());
+ if (dataDirectoryUrl().scheme() != QLatin1String("qrc")) {
+ QFile in(testFileUrl(QLatin1String("I18nType30.qml")).toLocalFile());
+ QVERIFY2(in.open(QIODevice::ReadOnly), qPrintable(QString::fromLatin1("Cannot open '%1': %2").arg(in.fileName(), in.errorString())));
+ QFile out(testFileUrl(QString::fromUtf8("I18nType\303\201\303\242\303\243\303\244\303\245.qml")).toLocalFile());
+ QVERIFY2(out.open(QIODevice::WriteOnly), qPrintable(QString::fromLatin1("Cannot open '%1': %2").arg(out.fileName(), out.errorString())));
+ out.write(in.readAll());
+ }
// Register a Composite Singleton.
qmlRegisterSingletonType(testFileUrl("singleton/RegisteredCompositeSingletonType.qml"), "org.qtproject.Test", 1, 0, "RegisteredSingleton");
@@ -3694,6 +3794,38 @@ void tst_qqmllanguage::signalParameterTypes()
QVERIFY(obj != nullptr);
QVERIFY(obj->property("success").toBool());
}
+
+ // dynamic signal connections
+ {
+ QQmlComponent component(&engine, testFileUrl("signalParameterTypes.3.qml"));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(obj != nullptr);
+ QVERIFY(obj->property("success").toBool());
+ }
+}
+
+void tst_qqmllanguage::functionParameterTypes()
+{
+ QQmlComponent component(&engine, testFileUrl("functionParameterTypes.qml"));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(!obj.isNull(), qPrintable(component.errorString()));
+ const QMetaObject *metaObject = obj->metaObject();
+
+ {
+ QMetaMethod slot = metaObject->method(metaObject->indexOfSlot("returnItem()"));
+ QVERIFY(slot.isValid());
+ QCOMPARE(slot.returnType(), QMetaType::type("QObject*"));
+ QObject *returnedPtr = nullptr;
+ slot.invoke(obj.data(), Qt::DirectConnection, Q_RETURN_ARG(QObject*, returnedPtr));
+ QCOMPARE(returnedPtr, obj.data());
+ }
+
+ {
+ QMetaMethod slot = metaObject->method(metaObject->indexOfSlot("takeString(QString)"));
+ QVERIFY(slot.isValid());
+ QCOMPARE(slot.parameterCount(), 1);
+ QCOMPARE(slot.parameterType(0), int(QMetaType::QString));
+ }
}
// QTBUG-20639
@@ -3812,7 +3944,7 @@ void tst_qqmllanguage::scopedEnumsWithNameClash()
{
auto typeId = qmlRegisterUncreatableType<ScopedEnumsWithNameClash>("ScopedEnumsWithNameClashTest", 1, 0, "ScopedEnum", "Dummy reason");
auto registryGuard = qScopeGuard([typeId]() {
- qmlUnregisterType(typeId);
+ QQmlMetaType::unregisterType(typeId);
});
QQmlEngine engine;
@@ -3831,7 +3963,7 @@ void tst_qqmllanguage::scopedEnumsWithResolvedNameClash()
{
auto typeId = qmlRegisterUncreatableType<ScopedEnumsWithResolvedNameClash>("ScopedEnumsWithResolvedNameClashTest", 1, 0, "ScopedEnum", "Dummy reason");
auto registryGuard = qScopeGuard([typeId]() {
- qmlUnregisterType(typeId);
+ QQmlMetaType::unregisterType(typeId);
});
QQmlEngine engine;
@@ -3951,7 +4083,7 @@ void tst_qqmllanguage::objectDeletionNotify()
void tst_qqmllanguage::scopedProperties()
{
- QQmlComponent component(&engine, testFile("scopedProperties.qml"));
+ QQmlComponent component(&engine, testFileUrl("scopedProperties.qml"));
QScopedPointer<QObject> o(component.create());
QVERIFY(o != nullptr);
@@ -3960,7 +4092,7 @@ void tst_qqmllanguage::scopedProperties()
void tst_qqmllanguage::deepProperty()
{
- QQmlComponent component(&engine, testFile("deepProperty.qml"));
+ QQmlComponent component(&engine, testFileUrl("deepProperty.qml"));
QScopedPointer<QObject> o(component.create());
QVERIFY(o != nullptr);
QFont font = qvariant_cast<QFont>(qvariant_cast<QObject*>(o->property("someObject"))->property("font"));
@@ -3978,14 +4110,16 @@ void tst_qqmllanguage::implicitImportsLast()
if (engine.importPathList() == defaultImportPathList)
engine.addImportPath(testFile("lib"));
- QQmlComponent component(&engine, testFile("localOrderTest.qml"));
+ QQmlComponent component(&engine, testFileUrl("localOrderTest.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
- QVERIFY(QString(object->metaObject()->className()).startsWith(QLatin1String("QQuickMouseArea")));
+ QVERIFY(QString(object->metaObject()->superClass()->superClass()->className())
+ .startsWith(QLatin1String("QQuickMouseArea")));
QObject* object2 = object->property("item").value<QObject*>();
QVERIFY(object2 != nullptr);
- QCOMPARE(QString(object2->metaObject()->className()), QLatin1String("QQuickRectangle"));
+ QCOMPARE(QString(object2->metaObject()->superClass()->className()),
+ QLatin1String("QQuickRectangle"));
engine.setImportPathList(defaultImportPathList);
}
@@ -3998,7 +4132,7 @@ void tst_qqmllanguage::getSingletonInstance(QQmlEngine& engine, const char* file
if (!fileName || !propertyName)
return;
- QQmlComponent component(&engine, testFile(fileName));
+ QQmlComponent component(&engine, testFileUrl(fileName));
VERIFY_ERRORS(0);
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
@@ -4040,7 +4174,7 @@ void verifyCompositeSingletonPropertyValues(QObject* o, const char* n1, int v1,
// Reads values from a composite singleton type
void tst_qqmllanguage::compositeSingletonProperties()
{
- QQmlComponent component(&engine, testFile("singletonTest1.qml"));
+ QQmlComponent component(&engine, testFileUrl("singletonTest1.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> o(component.create());
QVERIFY(o != nullptr);
@@ -4087,14 +4221,14 @@ void tst_qqmllanguage::compositeSingletonDifferentEngine()
// pragma Singleton in a non-type qml file fails
void tst_qqmllanguage::compositeSingletonNonTypeError()
{
- QQmlComponent component(&engine, testFile("singletonTest4.qml"));
+ QQmlComponent component(&engine, testFileUrl("singletonTest4.qml"));
VERIFY_ERRORS("singletonTest4.error.txt");
}
// Loads the singleton using a namespace qualifier
void tst_qqmllanguage::compositeSingletonQualifiedNamespace()
{
- QQmlComponent component(&engine, testFile("singletonTest5.qml"));
+ QQmlComponent component(&engine, testFileUrl("singletonTest5.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> o(component.create());
QVERIFY(o != nullptr);
@@ -4119,7 +4253,7 @@ void tst_qqmllanguage::compositeSingletonModule()
{
engine.addImportPath(testFile("singleton/module"));
- QQmlComponent component(&engine, testFile("singletonTest6.qml"));
+ QQmlComponent component(&engine, testFileUrl("singletonTest6.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> o(component.create());
QVERIFY(o != nullptr);
@@ -4145,7 +4279,7 @@ void tst_qqmllanguage::compositeSingletonModuleVersioned()
{
engine.addImportPath(testFile("singleton/module"));
- QQmlComponent component(&engine, testFile("singletonTest7.qml"));
+ QQmlComponent component(&engine, testFileUrl("singletonTest7.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> o(component.create());
QVERIFY(o != nullptr);
@@ -4171,7 +4305,7 @@ void tst_qqmllanguage::compositeSingletonModuleQualified()
{
engine.addImportPath(testFile("singleton/module"));
- QQmlComponent component(&engine, testFile("singletonTest8.qml"));
+ QQmlComponent component(&engine, testFileUrl("singletonTest8.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> o(component.create());
QVERIFY(o != nullptr);
@@ -4195,27 +4329,46 @@ void tst_qqmllanguage::compositeSingletonModuleQualified()
// Tries to instantiate a type with a pragma Singleton and fails
void tst_qqmllanguage::compositeSingletonInstantiateError()
{
- QQmlComponent component(&engine, testFile("singletonTest9.qml"));
+ QQmlComponent component(&engine, testFileUrl("singletonTest9.qml"));
VERIFY_ERRORS("singletonTest9.error.txt");
}
// Having a composite singleton type as dynamic property type is allowed
void tst_qqmllanguage::compositeSingletonDynamicPropertyError()
{
- QQmlComponent component(&engine, testFile("singletonTest10.qml"));
+ QQmlComponent component(&engine, testFileUrl("singletonTest10.qml"));
VERIFY_ERRORS(0);
}
-// Having a composite singleton type as dynamic signal parameter succeeds
-// (like C++ singleton)
-void tst_qqmllanguage::compositeSingletonDynamicSignal()
+void tst_qqmllanguage::compositeSingletonDynamicSignalAndJavaScriptPragma()
{
- QQmlComponent component(&engine, testFile("singletonTest11.qml"));
- VERIFY_ERRORS(0);
- QScopedPointer<QObject> o(component.create());
- QVERIFY(o != nullptr);
+ {
+ // Having a composite singleton type as dynamic signal parameter succeeds
+ // (like C++ singleton)
- verifyCompositeSingletonPropertyValues(o.data(), "value1", 99, "value2", -55);
+ QQmlComponent component(&engine, testFileUrl("singletonTest11.qml"));
+ VERIFY_ERRORS(0);
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(o != nullptr);
+
+ verifyCompositeSingletonPropertyValues(o.data(), "value1", 99, "value2", -55);
+ }
+ {
+ // Load a composite singleton type and a javascript file that has .pragma library
+ // in it. This will make sure that the javascript .pragma does not get mixed with
+ // the pragma Singleton changes.
+
+ QQmlComponent component(&engine, testFileUrl("singletonTest16.qml"));
+ VERIFY_ERRORS(0);
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(o != nullptr);
+
+ // The value1 that is read from the SingletonType was changed from 125 to 99
+ // above. As the type is a singleton and
+ // the engine has not been destroyed, we just retrieve the old instance and
+ // the value is still 99.
+ verifyCompositeSingletonPropertyValues(o.data(), "value1", 99, "value2", 333);
+ }
}
// Use qmlRegisterType to register a qml composite type with pragma Singleton defined in it.
@@ -4224,21 +4377,21 @@ void tst_qqmllanguage::compositeSingletonQmlRegisterTypeError()
{
qmlRegisterType(testFileUrl("singleton/registeredComposite/CompositeType.qml"),
"CompositeSingletonTest", 1, 0, "RegisteredCompositeType");
- QQmlComponent component(&engine, testFile("singletonTest12.qml"));
+ QQmlComponent component(&engine, testFileUrl("singletonTest12.qml"));
VERIFY_ERRORS("singletonTest12.error.txt");
}
// Qmldir defines a type as a singleton, but the qml file does not have a pragma Singleton.
void tst_qqmllanguage::compositeSingletonQmldirNoPragmaError()
{
- QQmlComponent component(&engine, testFile("singletonTest13.qml"));
+ QQmlComponent component(&engine, testFileUrl("singletonTest13.qml"));
VERIFY_ERRORS("singletonTest13.error.txt");
}
// Invalid singleton definition in the qmldir file results in an error
void tst_qqmllanguage::compositeSingletonQmlDirError()
{
- QQmlComponent component(&engine, testFile("singletonTest14.qml"));
+ QQmlComponent component(&engine, testFileUrl("singletonTest14.qml"));
VERIFY_ERRORS("singletonTest14.error.txt");
}
@@ -4267,30 +4420,13 @@ void tst_qqmllanguage::compositeSingletonRemote()
verifyCompositeSingletonPropertyValues(o.data(), "value1", 525, "value2", 355);
}
-// Load a composite singleton type and a javascript file that has .pragma library
-// in it. This will make sure that the javascript .pragma does not get mixed with
-// the pragma Singleton changes.
-void tst_qqmllanguage::compositeSingletonJavaScriptPragma()
-{
- QQmlComponent component(&engine, testFile("singletonTest16.qml"));
- VERIFY_ERRORS(0);
- QScopedPointer<QObject> o(component.create());
- QVERIFY(o != nullptr);
-
- // The value1 that is read from the SingletonType was changed from 125 to 99
- // in compositeSingletonDynamicSignal() above. As the type is a singleton and
- // the engine has not been destroyed, we just retrieve the old instance and
- // the value is still 99.
- verifyCompositeSingletonPropertyValues(o.data(), "value1", 99, "value2", 333);
-}
-
// Reads values from a Singleton accessed through selectors.
void tst_qqmllanguage::compositeSingletonSelectors()
{
QQmlEngine e2;
QQmlFileSelector qmlSelector(&e2);
qmlSelector.setExtraSelectors(QStringList() << "basicSelector");
- QQmlComponent component(&e2, testFile("singletonTest1.qml"));
+ QQmlComponent component(&e2, testFileUrl("singletonTest1.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> o(component.create());
QVERIFY(o != nullptr);
@@ -4302,7 +4438,7 @@ void tst_qqmllanguage::compositeSingletonSelectors()
// qmlRegisterSingletonType.
void tst_qqmllanguage::compositeSingletonRegistered()
{
- QQmlComponent component(&engine, testFile("singletonTest17.qml"));
+ QQmlComponent component(&engine, testFileUrl("singletonTest17.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> o(component.create());
QVERIFY(o != nullptr);
@@ -4312,7 +4448,7 @@ void tst_qqmllanguage::compositeSingletonRegistered()
void tst_qqmllanguage::compositeSingletonCircular()
{
- QQmlComponent component(&engine, testFile("circularSingleton.qml"));
+ QQmlComponent component(&engine, testFileUrl("circularSingleton.qml"));
VERIFY_ERRORS(0);
QQmlTestMessageHandler messageHandler;
@@ -4346,7 +4482,7 @@ void tst_qqmllanguage::singletonsHaveContextAndEngine()
void tst_qqmllanguage::customParserBindingScopes()
{
- QQmlComponent component(&engine, testFile("customParserBindingScopes.qml"));
+ QQmlComponent component(&engine, testFileUrl("customParserBindingScopes.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> o(component.create());
QVERIFY(!o.isNull());
@@ -4357,7 +4493,7 @@ void tst_qqmllanguage::customParserBindingScopes()
void tst_qqmllanguage::customParserEvaluateEnum()
{
- QQmlComponent component(&engine, testFile("customParserEvaluateEnum.qml"));
+ QQmlComponent component(&engine, testFileUrl("customParserEvaluateEnum.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> o(component.create());
QVERIFY(!o.isNull());
@@ -4365,7 +4501,7 @@ void tst_qqmllanguage::customParserEvaluateEnum()
void tst_qqmllanguage::customParserProperties()
{
- QQmlComponent component(&engine, testFile("customParserProperties.qml"));
+ QQmlComponent component(&engine, testFileUrl("customParserProperties.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> o(component.create());
QVERIFY(!o.isNull());
@@ -4379,7 +4515,7 @@ void tst_qqmllanguage::customParserProperties()
void tst_qqmllanguage::customParserWithExtendedObject()
{
- QQmlComponent component(&engine, testFile("customExtendedParserProperties.qml"));
+ QQmlComponent component(&engine, testFileUrl("customExtendedParserProperties.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> o(component.create());
QVERIFY(!o.isNull());
@@ -4397,7 +4533,7 @@ void tst_qqmllanguage::customParserWithExtendedObject()
void tst_qqmllanguage::nestedCustomParsers()
{
- QQmlComponent component(&engine, testFile("nestedCustomParsers.qml"));
+ QQmlComponent component(&engine, testFileUrl("nestedCustomParsers.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> o(component.create());
QVERIFY(!o.isNull());
@@ -4411,7 +4547,7 @@ void tst_qqmllanguage::nestedCustomParsers()
void tst_qqmllanguage::preservePropertyCacheOnGroupObjects()
{
- QQmlComponent component(&engine, testFile("preservePropertyCacheOnGroupObjects.qml"));
+ QQmlComponent component(&engine, testFileUrl("preservePropertyCacheOnGroupObjects.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> o(component.create());
QVERIFY(!o.isNull());
@@ -4430,7 +4566,7 @@ void tst_qqmllanguage::preservePropertyCacheOnGroupObjects()
void tst_qqmllanguage::propertyCacheInSync()
{
- QQmlComponent component(&engine, testFile("propertyCacheInSync.qml"));
+ QQmlComponent component(&engine, testFileUrl("propertyCacheInSync.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> o(component.create());
QVERIFY(!o.isNull());
@@ -4450,7 +4586,7 @@ void tst_qqmllanguage::propertyCacheInSync()
void tst_qqmllanguage::rootObjectInCreationNotForSubObjects()
{
- QQmlComponent component(&engine, testFile("rootObjectInCreationNotForSubObjects.qml"));
+ QQmlComponent component(&engine, testFileUrl("rootObjectInCreationNotForSubObjects.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> o(component.create());
QVERIFY(!o.isNull());
@@ -4476,7 +4612,7 @@ void tst_qqmllanguage::rootObjectInCreationNotForSubObjects()
// QTBUG-63036
void tst_qqmllanguage::lazyDeferredSubObject()
{
- QQmlComponent component(&engine, testFile("lazyDeferredSubObject.qml"));
+ QQmlComponent component(&engine, testFileUrl("lazyDeferredSubObject.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
@@ -4491,7 +4627,7 @@ void tst_qqmllanguage::lazyDeferredSubObject()
// QTBUG-63200
void tst_qqmllanguage::deferredProperties()
{
- QQmlComponent component(&engine, testFile("deferredProperties.qml"));
+ QQmlComponent component(&engine, testFileUrl("deferredProperties.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
@@ -4608,7 +4744,7 @@ static void testExecuteDeferredOnce(const QQmlProperty &property)
void tst_qqmllanguage::executeDeferredPropertiesOnce()
{
- QQmlComponent component(&engine, testFile("deferredProperties.qml"));
+ QQmlComponent component(&engine, testFileUrl("deferredProperties.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
@@ -4708,7 +4844,7 @@ void tst_qqmllanguage::deleteSingletons()
QPointer<QObject> singleton;
{
QQmlEngine tmpEngine;
- QQmlComponent component(&tmpEngine, testFile("singletonTest5.qml"));
+ QQmlComponent component(&tmpEngine, testFileUrl("singletonTest5.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> o(component.create());
QVERIFY(o != nullptr);
@@ -4735,7 +4871,7 @@ void tst_qqmllanguage::arrayBuffer_data()
void tst_qqmllanguage::arrayBuffer()
{
QFETCH(QString, file);
- QQmlComponent component(&engine, testFile(file));
+ QQmlComponent component(&engine, testFileUrl(file));
VERIFY_ERRORS(0);
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
@@ -4969,7 +5105,6 @@ void tst_qqmllanguage::instanceof()
if (QTest::currentDataTag() == QLatin1String("customRectangleWithPropInstance instanceof CustomRectangle") ||
QTest::currentDataTag() == QLatin1String("customRectangleWithPropInstance instanceof CustomImport.CustomRectangle"))
- QEXPECT_FAIL("", "QTBUG-58477: QML type rules are a little lax", Continue);
QCOMPARE(returnValue, expectedValue.toBool());
} else {
QVERIFY(expr.hasError());
@@ -5005,7 +5140,8 @@ void tst_qqmllanguage::accessDeletedObject()
{
QQmlEngine engine;
- engine.rootContext()->setContextProperty("objectCreator", new ObjectCreator);
+ QScopedPointer<ObjectCreator> creator(new ObjectCreator);
+ engine.rootContext()->setContextProperty("objectCreator", creator.get());
QQmlComponent component(&engine, testFileUrl("accessDeletedObject.qml"));
VERIFY_ERRORS(0);
diff --git a/tests/auto/qml/qqmllistcompositor/qqmllistcompositor.pro b/tests/auto/qml/qqmllistcompositor/qqmllistcompositor.pro
index 4ada590a2a..62ad85547e 100644
--- a/tests/auto/qml/qqmllistcompositor/qqmllistcompositor.pro
+++ b/tests/auto/qml/qqmllistcompositor/qqmllistcompositor.pro
@@ -4,4 +4,4 @@ macx:CONFIG -= app_bundle
SOURCES += tst_qqmllistcompositor.cpp
-QT += core-private gui-private qml-private quick-private testlib
+QT += core-private gui-private qml-private quick-private testlib qmlmodels-private
diff --git a/tests/auto/qml/qqmllistmodel/qqmllistmodel.pro b/tests/auto/qml/qqmllistmodel/qqmllistmodel.pro
index 8e3aed0baf..4d44d6b22b 100644
--- a/tests/auto/qml/qqmllistmodel/qqmllistmodel.pro
+++ b/tests/auto/qml/qqmllistmodel/qqmllistmodel.pro
@@ -8,4 +8,4 @@ include (../../shared/util.pri)
TESTDATA = data/*
-QT += core-private gui-private qml-private quick-private testlib
+QT += core-private gui-private qml-private quick-private testlib qmlmodels-private
diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
index 771f3e5c4e..75a932b6f4 100644
--- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
+++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
@@ -30,7 +30,7 @@
#include <QtQuick/private/qquicktext_p.h>
#include <QtQuick/private/qquickanimation_p.h>
#include <QtQml/private/qqmlengine_p.h>
-#include <QtQml/private/qqmllistmodel_p.h>
+#include <QtQmlModels/private/qqmllistmodel_p.h>
#include <QtQml/private/qqmlexpression_p.h>
#include <QQmlComponent>
@@ -128,6 +128,8 @@ private slots:
void qobjectTrackerForDynamicModelObjects();
void crash_append_empty_array();
void dynamic_roles_crash_QTBUG_38907();
+ void nestedListModelIteration();
+ void undefinedAppendShouldCauseError();
};
bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object)
@@ -760,11 +762,11 @@ void tst_qqmllistmodel::set()
RUNEXPR("model.set(0, {test:true})");
QCOMPARE(RUNEXPR("model.get(0).test").toBool(), true); // triggers creation of model cache
- QCOMPARE(model.data(0, 0), qVariantFromValue(true));
+ QCOMPARE(model.data(0, 0), QVariant::fromValue(true));
RUNEXPR("model.set(0, {test:false})");
QCOMPARE(RUNEXPR("model.get(0).test").toBool(), false); // tests model cache is updated
- QCOMPARE(model.data(0, 0), qVariantFromValue(false));
+ QCOMPARE(model.data(0, 0), QVariant::fromValue(false));
QString warning = QString::fromLatin1("<Unknown File>: Can't create role for unsupported data type");
if (isValidErrorMessage(warning, dynamicRoles))
@@ -1667,6 +1669,61 @@ void tst_qqmllistmodel::dynamic_roles_crash_QTBUG_38907()
QVERIFY(retVal.toBool());
}
+void tst_qqmllistmodel::nestedListModelIteration()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ QTest::ignoreMessage(QtMsgType::QtDebugMsg ,R"({"subItems":[{"a":1,"b":0,"c":0},{"a":0,"b":2,"c":0},{"a":0,"b":0,"c":3}]})");
+ component.setData(
+ R"(import QtQuick 2.5
+ Item {
+ visible: true
+ width: 640
+ height: 480
+ ListModel {
+ id : model
+ }
+ Component.onCompleted: {
+ var tempData = {
+ subItems: [{a: 1}, {b: 2}, {c: 3}]
+ }
+ model.insert(0, tempData)
+ console.log(JSON.stringify(model.get(0)))
+ }
+ })",
+ QUrl());
+ QScopedPointer<QObject>(component.create());
+}
+
+// QTBUG-63569
+void tst_qqmllistmodel::undefinedAppendShouldCauseError()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.setData(
+ R"(import QtQuick 2.5
+ Item {
+ width: 640
+ height: 480
+ ListModel {
+ id : model
+ }
+ Component.onCompleted: {
+ var tempData = {
+ faulty: undefined
+ }
+ model.insert(0, tempData)
+ tempData.faulty = null
+ model.insert(0, tempData)
+ }
+ })",
+ QUrl());
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, "<Unknown File>: faulty is undefined. Adding an object with a undefined member does not create a role for it.");
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, "<Unknown File>: faulty is null. Adding an object with a null member does not create a role for it.");
+ QScopedPointer<QObject>(component.create());
+}
+
+
QTEST_MAIN(tst_qqmllistmodel)
#include "tst_qqmllistmodel.moc"
diff --git a/tests/auto/qml/qqmllistmodelworkerscript/qqmllistmodelworkerscript.pro b/tests/auto/qml/qqmllistmodelworkerscript/qqmllistmodelworkerscript.pro
index 9e1cea9867..de58c0c075 100644
--- a/tests/auto/qml/qqmllistmodelworkerscript/qqmllistmodelworkerscript.pro
+++ b/tests/auto/qml/qqmllistmodelworkerscript/qqmllistmodelworkerscript.pro
@@ -8,4 +8,4 @@ include (../../shared/util.pri)
TESTDATA = data/*
-QT += core-private gui-private qml-private quick-private testlib
+QT += core-private gui-private qml-private quick-private testlib qmlmodels-private
diff --git a/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp b/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp
index 21b0508e4d..b5e8800d0e 100644
--- a/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp
+++ b/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp
@@ -29,7 +29,7 @@
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquicktext_p.h>
#include <QtQml/private/qqmlengine_p.h>
-#include <QtQml/private/qqmllistmodel_p.h>
+#include <QtQmlModels/private/qqmllistmodel_p.h>
#include <QtQml/private/qqmlexpression_p.h>
#include <QQmlComponent>
@@ -162,7 +162,7 @@ QQuickItem *tst_qqmllistmodelworkerscript::createWorkerTest(QQmlEngine *eng, QQm
QQuickItem *item = qobject_cast<QQuickItem*>(component->create());
QQmlEngine::setContextForObject(model, eng->rootContext());
if (item)
- item->setProperty("model", qVariantFromValue(model));
+ item->setProperty("model", QVariant::fromValue(model));
return item;
}
diff --git a/tests/auto/qml/qqmllocale/data/localeAsCppProperty.qml b/tests/auto/qml/qqmllocale/data/localeAsCppProperty.qml
new file mode 100644
index 0000000000..ff80f3cf85
--- /dev/null
+++ b/tests/auto/qml/qqmllocale/data/localeAsCppProperty.qml
@@ -0,0 +1,6 @@
+import QtQml 2.2
+import Test 1.0
+Calendar {
+ locale: Qt.locale('en_GB')
+ property var testLocale
+}
diff --git a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp
index eb6eb62648..a90749208c 100644
--- a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp
+++ b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp
@@ -32,6 +32,8 @@
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlcontext.h>
#include <QtCore/QDateTime>
+#include <QtCore/qscopeguard.h>
+#include <QtCore/qscopedpointer.h>
#include <qcolor.h>
#include "../../shared/util.h"
@@ -1214,10 +1216,9 @@ private:
void tst_qqmllocale::localeAsCppProperty()
{
- QQmlComponent component(&engine);
qmlRegisterType<Calendar>("Test", 1, 0, "Calendar");
- component.setData("import QtQml 2.2\nimport Test 1.0\nCalendar { locale: Qt.locale('en_GB'); property var testLocale }", QUrl());
- QVERIFY(!component.isError());
+ QQmlComponent component(&engine, testFileUrl("localeAsCppProperty.qml"));
+ QVERIFY2(!component.isError(), qPrintable(component.errorString()));
QTRY_VERIFY(component.isReady());
Calendar *item = qobject_cast<Calendar*>(component.create());
@@ -1271,13 +1272,21 @@ void tst_qqmllocale::timeZoneUpdated()
// Set the timezone to Brisbane time, AEST-10:00
setTimeZone(QByteArray("Australia/Brisbane"));
+ QScopedPointer<QObject> obj;
+ auto cleanup = qScopeGuard([&original, &obj] {
+ // Restore to original time zone
+ setTimeZone(original);
+ QMetaObject::invokeMethod(obj.data(), "resetTimeZone");
+ });
+
DateFormatter formatter;
QQmlEngine e;
e.rootContext()->setContextObject(&formatter);
QQmlComponent c(&e, testFileUrl("timeZoneUpdated.qml"));
- QScopedPointer<QObject> obj(c.create());
+ QVERIFY2(!c.isError(), qPrintable(c.errorString()));
+ obj.reset(c.create());
QVERIFY(obj);
QVERIFY(obj->property("success").toBool());
@@ -1285,11 +1294,6 @@ void tst_qqmllocale::timeZoneUpdated()
setTimeZone(QByteArray("Asia/Kolkata"));
QMetaObject::invokeMethod(obj.data(), "check");
-
- // Reset to original time
- setTimeZone(original);
- QMetaObject::invokeMethod(obj.data(), "resetTimeZone");
-
QVERIFY(obj->property("success").toBool());
}
#endif
diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
index a7805922a5..76185a97e0 100644
--- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
+++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
@@ -36,7 +36,6 @@
#include <private/qqmlmetatype_p.h>
#include <private/qqmlpropertyvalueinterceptor_p.h>
#include <private/qqmlengine_p.h>
-#include <private/qhashedstring_p.h>
#include "../../shared/util.h"
class tst_qqmlmetatype : public QQmlDataTest
@@ -401,7 +400,7 @@ void tst_qqmlmetatype::unregisterCustomType()
QCOMPARE(enumVal.type(), QVariant::Int);
QCOMPARE(enumVal.toInt(), 1);
}
- qmlUnregisterType(controllerId);
+ QQmlMetaType::unregisterType(controllerId);
{
QQmlEngine engine;
QQmlType type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0);
@@ -424,7 +423,7 @@ void tst_qqmlmetatype::unregisterCustomType()
QCOMPARE(enumVal.type(), QVariant::Int);
QCOMPARE(enumVal.toInt(), 111);
}
- qmlUnregisterType(controllerId);
+ QQmlMetaType::unregisterType(controllerId);
{
QQmlEngine engine;
QQmlType type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0);
@@ -493,7 +492,7 @@ void tst_qqmlmetatype::unregisterCustomSingletonType()
QCOMPARE(stringVal.type(), QVariant::String);
QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #1"));
}
- qmlUnregisterType(staticProviderId);
+ QQmlMetaType::unregisterType(staticProviderId);
{
QQmlEngine engine;
staticProviderId = qmlRegisterSingletonType<StaticProvider2>("mytypes", 1, 0, "StaticProvider", createStaticProvider2);
@@ -509,7 +508,7 @@ void tst_qqmlmetatype::unregisterCustomSingletonType()
QCOMPARE(stringVal.type(), QVariant::String);
QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #2"));
}
- qmlUnregisterType(staticProviderId);
+ QQmlMetaType::unregisterType(staticProviderId);
{
QQmlEngine engine;
staticProviderId = qmlRegisterSingletonType<StaticProvider1>("mytypes", 1, 0, "StaticProvider", createStaticProvider1);
@@ -535,7 +534,7 @@ void tst_qqmlmetatype::normalizeUrls()
QVERIFY(QQmlMetaType::qmlType(url, /*includeNonFileImports=*/true).isValid());
QUrl normalizedURL("qrc:/tstqqmlmetatype/data/CompositeType.qml");
QVERIFY(QQmlMetaType::qmlType(normalizedURL, /*includeNonFileImports=*/true).isValid());
- qmlUnregisterType(registrationId);
+ QQmlMetaType::unregisterType(registrationId);
QVERIFY(!QQmlMetaType::qmlType(url, /*includeNonFileImports=*/true).isValid());
}
diff --git a/tests/auto/qml/qqmlmoduleplugin/data/multiSingleton.qml b/tests/auto/qml/qqmlmoduleplugin/data/multiSingleton.qml
new file mode 100644
index 0000000000..2fc2e9f076
--- /dev/null
+++ b/tests/auto/qml/qqmlmoduleplugin/data/multiSingleton.qml
@@ -0,0 +1,6 @@
+import org.qtproject.ModuleWithQmlSingleton 1.0
+import QtQuick 2.0
+
+Item {
+ Component.onCompleted: MySingleton
+}
diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton.qml b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton.qml
new file mode 100644
index 0000000000..9789be8191
--- /dev/null
+++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton.qml
@@ -0,0 +1,16 @@
+pragma Singleton
+import QtQuick 2.0
+
+QtObject {
+ property Loader _loader: Loader {
+ source: "internal/InternalType.qml"
+ }
+
+ Component.onCompleted: {
+ if (tracker.objectName === "first")
+ tracker.objectName = "second"
+ else
+ tracker.objectName = "first"
+ //console.log("created singleton", this)
+ }
+}
diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton2.qml b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton2.qml
new file mode 100644
index 0000000000..9be34eb061
--- /dev/null
+++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/MySingleton2.qml
@@ -0,0 +1,6 @@
+pragma Singleton
+import QtQuick 2.0
+import org.qtproject.ModuleWithQmlSingleton 1.0
+import "."
+
+QtObject {}
diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/internal/InternalType.qml b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/internal/InternalType.qml
new file mode 100644
index 0000000000..4a8badefd2
--- /dev/null
+++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/internal/InternalType.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+import ".."
+
+QtObject {
+ Component.onCompleted: MySingleton
+}
diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/moduleWithQmlSingleton.pro b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/moduleWithQmlSingleton.pro
new file mode 100644
index 0000000000..b16e0743c8
--- /dev/null
+++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/moduleWithQmlSingleton.pro
@@ -0,0 +1,18 @@
+TEMPLATE = lib
+CONFIG += plugin
+SOURCES = plugin.cpp
+QT = core qml
+DESTDIR = ../imports/org/qtproject/ModuleWithQmlSingleton
+
+QT += core-private gui-private qml-private
+
+IMPORT_FILES = \
+ qmldir \
+ MySingleton.qml \
+ MySingleton2.qml
+
+include (../../../shared/imports.pri)
+
+subfiles.files = internal/InternalType.qml
+subfiles.path = $$DESTDIR/internal
+COPIES += subfiles
diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp
new file mode 100644
index 0000000000..6329927c34
--- /dev/null
+++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/plugin.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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 <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqml.h>
+#include <QDir>
+#include <QDebug>
+
+class MyPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+
+public:
+ MyPlugin() {}
+
+ void registerTypes(const char *uri)
+ {
+ Q_ASSERT(QLatin1String(uri) == "org.qtproject.ModuleWithQmlSingleton");
+ qmlRegisterSingletonType(baseUrl().resolved(QUrl("ModuleWithQmlSingleton/MySingleton.qml")), uri, 1, 0, "MySingleton");
+ qmlRegisterSingletonType(baseUrl().resolved(QUrl("ModuleWithQmlSingleton/MySingleton2.qml")), uri, 1, 0, "MySingleton2");
+ }
+};
+
+#include "plugin.moc"
diff --git a/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/qmldir b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/qmldir
new file mode 100644
index 0000000000..3483f80ab1
--- /dev/null
+++ b/tests/auto/qml/qqmlmoduleplugin/moduleWithQmlSingleton/qmldir
@@ -0,0 +1,2 @@
+module org.qtproject.ModuleWithQmlSingleton
+plugin moduleWithQmlSingleton
diff --git a/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro b/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro
index ae13a041cc..44b3ab14e6 100644
--- a/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro
+++ b/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro
@@ -20,7 +20,8 @@ SUBDIRS =\
plugin/childplugin\
plugin.2/childplugin\
plugin.2.1/childplugin\
- plugin.2.2
+ plugin.2.2\
+ moduleWithQmlSingleton
tst_qqmlmoduleplugin_pro.depends += plugin
SUBDIRS += tst_qqmlmoduleplugin.pro
diff --git a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
index 97ca3fa1de..f89cc9f24a 100644
--- a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
@@ -78,6 +78,7 @@ private slots:
void importsChildPlugin2();
void importsChildPlugin21();
void parallelPluginImport();
+ void multiSingleton();
private:
QString m_importsDirectory;
@@ -99,7 +100,7 @@ public:
qmlRegisterModule(uri, 1, 0);
}
- void initializeEngine(QQmlEngine *engine, const char *uri) override
+ void initializeEngine(QQmlEngine *, const char *) override
{
initializeEngineEntered.lock();
leavingInitializeEngine.lock();
@@ -772,6 +773,20 @@ void tst_qqmlmoduleplugin::parallelPluginImport()
worker.wait();
}
+void tst_qqmlmoduleplugin::multiSingleton()
+{
+ QQmlEngine engine;
+ QObject obj;
+ engine.rootContext()->setContextProperty("tracker", &obj);
+ engine.addImportPath(m_importsDirectory);
+ QQmlComponent component(&engine, testFileUrl("multiSingleton.qml"));
+ QObject *object = component.create();
+ QVERIFY(object != nullptr);
+ QCOMPARE(obj.objectName(), QLatin1String("first"));
+ delete object;
+}
+
+
QTEST_MAIN(tst_qqmlmoduleplugin)
#include "tst_qqmlmoduleplugin.moc"
diff --git a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
index a5332c8860..de762d66c5 100644
--- a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
+++ b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
@@ -342,6 +342,9 @@ void tst_qqmlnotifier::lotsOfBindings()
void tst_qqmlnotifier::deleteFromHandler()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("Android seems to have problems with QProcess");
+#endif
#if !QT_CONFIG(process)
QSKIP("Need QProcess support to test qFatal.");
#else
diff --git a/tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro b/tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro
index 88bb630e29..5746ff754a 100644
--- a/tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro
+++ b/tests/auto/qml/qqmlobjectmodel/qqmlobjectmodel.pro
@@ -5,4 +5,4 @@ osx:CONFIG -= app_bundle
SOURCES += tst_qqmlobjectmodel.cpp
QT += qml testlib
-QT += core-private qml-private
+QT += core-private qml-private qmlmodels-private
diff --git a/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp b/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp
index fb63d811a8..6691fa43a0 100644
--- a/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp
+++ b/tests/auto/qml/qqmlobjectmodel/tst_qqmlobjectmodel.cpp
@@ -25,8 +25,8 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-#include <QtQml/private/qqmlobjectmodel_p.h>
-#include <QtQml/private/qqmlchangeset_p.h>
+#include <QtQmlModels/private/qqmlobjectmodel_p.h>
+#include <QtQmlModels/private/qqmlchangeset_p.h>
#include <QtTest/qsignalspy.h>
#include <QtTest/qtest.h>
diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/anonfunctionexpr.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/anonfunctionexpr.js
new file mode 100644
index 0000000000..f660edb69e
--- /dev/null
+++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/anonfunctionexpr.js
@@ -0,0 +1,3 @@
+(function () : string {
+ return "ko"
+})
diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classmemberparam.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classmemberparam.js
new file mode 100644
index 0000000000..ab85d90880
--- /dev/null
+++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classmemberparam.js
@@ -0,0 +1,6 @@
+
+class Foo {
+ member(param: string) {
+ return "ko"
+ }
+}
diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classreturnvalue.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classreturnvalue.js
new file mode 100644
index 0000000000..a7da9e0ca7
--- /dev/null
+++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classreturnvalue.js
@@ -0,0 +1,6 @@
+
+class Foo {
+ member(): string {
+ return "ko"
+ }
+}
diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/function.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/function.js
new file mode 100644
index 0000000000..4d6021e835
--- /dev/null
+++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/function.js
@@ -0,0 +1,4 @@
+
+function x() : string {
+ return "ok"
+}
diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionexpr.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionexpr.js
new file mode 100644
index 0000000000..33f2abbb61
--- /dev/null
+++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionexpr.js
@@ -0,0 +1,3 @@
+(function x() : string {
+ return "ko"
+})
diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionparams.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionparams.js
new file mode 100644
index 0000000000..2c23628e3f
--- /dev/null
+++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionparams.js
@@ -0,0 +1,3 @@
+
+function test(x: string, y: string) {
+}
diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration.js
new file mode 100644
index 0000000000..a6cc00e38a
--- /dev/null
+++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration.js
@@ -0,0 +1,3 @@
+
+for (var i: int = 0; i < 100; ++i) {
+}
diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration2.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration2.js
new file mode 100644
index 0000000000..24d5acce98
--- /dev/null
+++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration2.js
@@ -0,0 +1,5 @@
+
+let y = [1, 2, 3];
+
+for (let x: int of y) {
+}
diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/qmlnestedfunction.qml b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/qmlnestedfunction.qml
new file mode 100644
index 0000000000..f435bf1b25
--- /dev/null
+++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/qmlnestedfunction.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.12 as MyQuick
+MyQuick.Item {
+ function factory(param: string) : MyQuick.Item {
+ function nested(foo: string) {
+ return this
+ }
+ return nested()
+ }
+}
diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/variables.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/variables.js
new file mode 100644
index 0000000000..bf332ac7a8
--- /dev/null
+++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/variables.js
@@ -0,0 +1,2 @@
+
+var x: string = "ko"
diff --git a/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_bool.qml b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_bool.qml
new file mode 100644
index 0000000000..79a9ede3d4
--- /dev/null
+++ b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_bool.qml
@@ -0,0 +1,4 @@
+import QtQml 2.0
+QtObject {
+ function test() : bool { return true; }
+}
diff --git a/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_double.qml b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_double.qml
new file mode 100644
index 0000000000..f58e4b92d9
--- /dev/null
+++ b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_double.qml
@@ -0,0 +1,4 @@
+import QtQml 2.0
+QtObject {
+ function test() : double { return 0; }
+}
diff --git a/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_int.qml b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_int.qml
new file mode 100644
index 0000000000..267ad7191f
--- /dev/null
+++ b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_int.qml
@@ -0,0 +1,4 @@
+import QtQml 2.0
+QtObject {
+ function test() : int { return 0; }
+}
diff --git a/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_real.qml b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_real.qml
new file mode 100644
index 0000000000..9973819ad2
--- /dev/null
+++ b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_real.qml
@@ -0,0 +1,4 @@
+import QtQml 2.0
+QtObject {
+ function test() : real { return 0; }
+}
diff --git a/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_string.qml b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_string.qml
new file mode 100644
index 0000000000..e632ec7154
--- /dev/null
+++ b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_string.qml
@@ -0,0 +1,4 @@
+import QtQml 2.0
+QtObject {
+ function test(s: string) {}
+}
diff --git a/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_url.qml b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_url.qml
new file mode 100644
index 0000000000..ebf3f32561
--- /dev/null
+++ b/tests/auto/qml/qqmlparser/data/typeannotations/basic_qmltypes_url.qml
@@ -0,0 +1,4 @@
+import QtQml 2.0
+QtObject {
+ function test(u: url) {}
+}
diff --git a/tests/auto/qml/qqmlparser/data/typeannotations/parametrized.qml b/tests/auto/qml/qqmlparser/data/typeannotations/parametrized.qml
new file mode 100644
index 0000000000..d80b5e3f87
--- /dev/null
+++ b/tests/auto/qml/qqmlparser/data/typeannotations/parametrized.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0 as MyQuick
+MyQuick.Item {
+ function factory() : list<MyQuick.Item> {
+ }
+}
diff --git a/tests/auto/qml/qqmlparser/data/typeannotations/qmlfunction.qml b/tests/auto/qml/qqmlparser/data/typeannotations/qmlfunction.qml
new file mode 100644
index 0000000000..cd5c1f51e5
--- /dev/null
+++ b/tests/auto/qml/qqmlparser/data/typeannotations/qmlfunction.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.12 as MyQuick
+MyQuick.Item {
+ function factory(param: string) : MyQuick.Item {
+ return this
+ }
+}
diff --git a/tests/auto/qml/qqmlparser/qqmlparser.pro b/tests/auto/qml/qqmlparser/qqmlparser.pro
index 74cb620f06..d8e4b0dd06 100644
--- a/tests/auto/qml/qqmlparser/qqmlparser.pro
+++ b/tests/auto/qml/qqmlparser/qqmlparser.pro
@@ -7,3 +7,7 @@ SOURCES += tst_qqmlparser.cpp
DEFINES += SRCDIR=\\\"$$PWD\\\"
cross_compile: DEFINES += QTEST_CROSS_COMPILED
+
+TESTDATA = data/*
+
+include (../../shared/util.pri)
diff --git a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp
index f16e96a385..9d8818d01e 100644
--- a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp
+++ b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp
@@ -32,12 +32,14 @@
#include <private/qqmljsastvisitor_p.h>
#include <private/qqmljsast_p.h>
+#include "../../shared/util.h"
+
#include <qtest.h>
#include <QDir>
#include <QDebug>
#include <cstdlib>
-class tst_qqmlparser : public QObject
+class tst_qqmlparser : public QQmlDataTest
{
Q_OBJECT
public:
@@ -55,6 +57,11 @@ private slots:
void templateLiteral();
void leadingSemicolonInClass();
void templatedReadonlyProperty();
+ void qmlImportInJSRequiresFullVersion();
+ void typeAnnotations_data();
+ void typeAnnotations();
+ void disallowedTypeAnnotations_data();
+ void disallowedTypeAnnotations();
private:
QStringList excludedDirs;
@@ -87,7 +94,7 @@ public:
qDebug() << "first source loc failed: node:" << node->kind << "at" << node->firstSourceLocation().startLine << "/" << node->firstSourceLocation().startColumn
<< "parent" << parent->kind << "at" << parent->firstSourceLocation().startLine << "/" << parent->firstSourceLocation().startColumn;
if (node->lastSourceLocation().end() > parentEnd)
- qDebug() << "first source loc failed: node:" << node->kind << "at" << node->lastSourceLocation().startLine << "/" << node->lastSourceLocation().startColumn
+ qDebug() << "last source loc failed: node:" << node->kind << "at" << node->lastSourceLocation().startLine << "/" << node->lastSourceLocation().startColumn
<< "parent" << parent->kind << "at" << parent->lastSourceLocation().startLine << "/" << parent->lastSourceLocation().startColumn;
QVERIFY(node->firstSourceLocation().begin() >= parentBegin);
@@ -113,6 +120,27 @@ public:
}
};
+struct TypeAnnotationObserver: public AST::Visitor
+{
+ bool typeAnnotationSeen = false;
+
+ void operator()(AST::Node *node)
+ {
+ AST::Node::accept(node, this);
+ }
+
+ virtual bool visit(AST::TypeAnnotation *)
+ {
+ typeAnnotationSeen = true;
+ return true;
+ }
+
+ void throwRecursionDepthError() final
+ {
+ QFAIL("Maximum statement or expression depth exceeded");
+ }
+};
+
}
tst_qqmlparser::tst_qqmlparser()
@@ -121,6 +149,7 @@ tst_qqmlparser::tst_qqmlparser()
void tst_qqmlparser::initTestCase()
{
+ QQmlDataTest::initTestCase();
// Add directories you want excluded here
// These snippets are not expected to run on their own.
@@ -299,6 +328,116 @@ void tst_qqmlparser::templatedReadonlyProperty()
QVERIFY(parser.parse());
}
+void tst_qqmlparser::qmlImportInJSRequiresFullVersion()
+{
+ {
+ QQmlJS::Engine engine;
+ QQmlJS::Lexer lexer(&engine);
+ lexer.setCode(QLatin1String(".import Test 1.0 as T"), 0, false);
+ QQmlJS::Parser parser(&engine);
+ bool b = parser.parseProgram();
+ qDebug() << parser.errorMessage();
+ QVERIFY(b);
+ }
+ {
+ QQmlJS::Engine engine;
+ QQmlJS::Lexer lexer(&engine);
+ lexer.setCode(QLatin1String(".import Test 1 as T"), 0, false);
+ QQmlJS::Parser parser(&engine);
+ QVERIFY(!parser.parseProgram());
+ }
+ {
+ QQmlJS::Engine engine;
+ QQmlJS::Lexer lexer(&engine);
+ lexer.setCode(QLatin1String(".import Test 1 as T"), 0, false);
+ QQmlJS::Parser parser(&engine);
+ QVERIFY(!parser.parseProgram());
+ }
+ {
+ QQmlJS::Engine engine;
+ QQmlJS::Lexer lexer(&engine);
+ lexer.setCode(QLatin1String(".import Test as T"), 0, false);
+ QQmlJS::Parser parser(&engine);
+ QVERIFY(!parser.parseProgram());
+ }
+}
+
+void tst_qqmlparser::typeAnnotations_data()
+{
+ QTest::addColumn<QString>("file");
+
+ QString tests = dataDirectory() + "/typeannotations/";
+
+ QStringList files;
+ files << findFiles(QDir(tests));
+
+ for (const QString &file: qAsConst(files))
+ QTest::newRow(qPrintable(file)) << file;
+}
+
+void tst_qqmlparser::typeAnnotations()
+{
+ using namespace QQmlJS;
+
+ QFETCH(QString, file);
+
+ QString code;
+
+ QFile f(file);
+ if (f.open(QFile::ReadOnly))
+ code = QString::fromUtf8(f.readAll());
+
+ const bool qmlMode = file.endsWith(QLatin1String(".qml"));
+
+ Engine engine;
+ Lexer lexer(&engine);
+ lexer.setCode(code, 1, qmlMode);
+ Parser parser(&engine);
+ bool ok = qmlMode ? parser.parse() : parser.parseProgram();
+ QVERIFY(ok);
+
+ check::TypeAnnotationObserver observer;
+ observer(parser.rootNode());
+
+ QVERIFY(observer.typeAnnotationSeen);
+}
+
+void tst_qqmlparser::disallowedTypeAnnotations_data()
+{
+ QTest::addColumn<QString>("file");
+
+ QString tests = dataDirectory() + "/disallowedtypeannotations/";
+
+ QStringList files;
+ files << findFiles(QDir(tests));
+
+ for (const QString &file: qAsConst(files))
+ QTest::newRow(qPrintable(file)) << file;
+}
+
+void tst_qqmlparser::disallowedTypeAnnotations()
+{
+ using namespace QQmlJS;
+
+ QFETCH(QString, file);
+
+ QString code;
+
+ QFile f(file);
+ if (f.open(QFile::ReadOnly))
+ code = QString::fromUtf8(f.readAll());
+
+ const bool qmlMode = file.endsWith(QLatin1String(".qml"));
+
+ Engine engine;
+ Lexer lexer(&engine);
+ lexer.setCode(code, 1, qmlMode);
+ Parser parser(&engine);
+ bool ok = qmlMode ? parser.parse() : parser.parseProgram();
+ QVERIFY(!ok);
+ QVERIFY2(parser.errorMessage().startsWith("Type annotations are not permitted "), qPrintable(parser.errorMessage()));
+}
+
QTEST_MAIN(tst_qqmlparser)
#include "tst_qqmlparser.moc"
diff --git a/tests/auto/qml/qqmlproperty/data/interfaceBinding.qml b/tests/auto/qml/qqmlproperty/data/interfaceBinding.qml
new file mode 100644
index 0000000000..4e72a75f42
--- /dev/null
+++ b/tests/auto/qml/qqmlproperty/data/interfaceBinding.qml
@@ -0,0 +1,27 @@
+import QtQuick 2.12
+import io.qt.bugreports 1.0
+Item {
+ InterfaceConsumer {
+ objectName: "a1"
+ i: A {
+ property int i: 42
+ }
+ }
+
+ InterfaceConsumer {
+ objectName: "a2"
+ property A a: A {
+ property int i: 43
+ }
+ i: a
+ }
+
+ InterfaceConsumer {
+ objectName: "a3"
+ property A a: A {
+ id : aa
+ property int i: 44
+ }
+ i: aa
+ }
+}
diff --git a/tests/auto/qml/qqmlproperty/data/nullPropertyBinding.qml b/tests/auto/qml/qqmlproperty/data/nullPropertyBinding.qml
new file mode 100644
index 0000000000..4fffc7aead
--- /dev/null
+++ b/tests/auto/qml/qqmlproperty/data/nullPropertyBinding.qml
@@ -0,0 +1,22 @@
+import QtQuick 2.12
+
+Item {
+ id: root
+
+ width: 640
+ height: 480
+
+ property bool toggle: false
+ property Item bound
+ property string message: "defined"
+
+ readonly property Item item: root.toggle ? root : null
+
+ Binding { target: root; property: "bound"; value: item}
+
+ function tog() {
+ console.info(root.bound ? root.bound.message: "undefined")
+ root.toggle = !root.toggle
+ return 42;
+ }
+}
diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
index ed213cd01a..a15c00ad62 100644
--- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
+++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
@@ -35,8 +35,12 @@
#include <private/qqmlboundsignal_p.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qdir.h>
+#if QT_CONFIG(regularexpression)
+#include <QtCore/qregularexpression.h>
+#endif
#include <QtCore/private/qobject_p.h>
#include "../../shared/util.h"
+#include "qobject.h"
#include <QtQml/QQmlPropertyMap>
#include <QDebug>
@@ -145,6 +149,8 @@ private slots:
void deeplyNestedObject();
void readOnlyDynamicProperties();
void aliasToIdWithMatchingQmlFileNameOnCaseInsensitiveFileSystem();
+ void nullPropertyBinding();
+ void interfaceBinding();
void floatToStringPrecision_data();
void floatToStringPrecision();
@@ -1627,7 +1633,7 @@ void tst_qqmlproperty::writeObjectToList()
MyQmlObject *object = new MyQmlObject;
QQmlProperty prop(container, "children");
- prop.write(qVariantFromValue(object));
+ prop.write(QVariant::fromValue(object));
QCOMPARE(list.count(), 1);
QCOMPARE(list.at(0), qobject_cast<QObject*>(object));
}
@@ -1644,13 +1650,13 @@ void tst_qqmlproperty::writeListToList()
QList<QObject*> objList;
objList << new MyQmlObject() << new MyQmlObject() << new MyQmlObject() << new MyQmlObject();
QQmlProperty prop(container, "children");
- prop.write(qVariantFromValue(objList));
+ prop.write(QVariant::fromValue(objList));
QCOMPARE(list.count(), 4);
//XXX need to try this with read/write prop (for read-only it correctly doesn't write)
/*QList<MyQmlObject*> typedObjList;
typedObjList << new MyQmlObject();
- prop.write(qVariantFromValue(&typedObjList));
+ prop.write(QVariant::fromValue(&typedObjList));
QCOMPARE(container->children()->size(), 1);*/
}
@@ -2017,9 +2023,13 @@ void tst_qqmlproperty::warnOnInvalidBinding()
expectedWarning = testUrl.toString() + QString::fromLatin1(":7:5: Unable to assign QQuickText to QQuickRectangle");
QTest::ignoreMessage(QtWarningMsg, expectedWarning.toLatin1().constData());
+#if QT_CONFIG(regularexpression)
// V8 error message for invalid binding to anchor
- expectedWarning = testUrl.toString() + QString::fromLatin1(":14:9: Unable to assign QQuickItem_QML_8 to QQuickAnchorLine");
- QTest::ignoreMessage(QtWarningMsg, expectedWarning.toLatin1().constData());
+ const QRegularExpression warning(
+ "^" + testUrl.toString()
+ + ":14:9: Unable to assign QQuickItem_QML_\\d+ to QQuickAnchorLine$");
+ QTest::ignoreMessage(QtWarningMsg, warning);
+#endif
QQmlComponent component(&engine, testUrl);
QObject *obj = component.create();
@@ -2062,6 +2072,99 @@ void tst_qqmlproperty::aliasToIdWithMatchingQmlFileNameOnCaseInsensitiveFileSyst
QVERIFY(property.isValid());
}
+// QTBUG-77027
+void tst_qqmlproperty::nullPropertyBinding()
+{
+ const QUrl url = testFileUrl("nullPropertyBinding.qml");
+ QQmlEngine engine;
+ QQmlComponent component(&engine, url);
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(root);
+ QTest::ignoreMessage(QtMsgType::QtInfoMsg, "undefined");
+ QMetaObject::invokeMethod(root.get(), "tog");
+ QTest::ignoreMessage(QtMsgType::QtInfoMsg, "defined");
+ QMetaObject::invokeMethod(root.get(), "tog");
+ QTest::ignoreMessage(QtMsgType::QtInfoMsg, "undefined");
+ QMetaObject::invokeMethod(root.get(), "tog");
+}
+
+struct Interface {
+};
+
+QT_BEGIN_NAMESPACE
+#define MyInterface_iid "io.qt.bugreports.Interface"
+Q_DECLARE_INTERFACE(Interface, MyInterface_iid);
+QT_END_NAMESPACE
+
+class A : public QObject, Interface {
+ Q_OBJECT
+ Q_INTERFACES(Interface)
+};
+
+class B : public QObject, Interface {
+ Q_OBJECT
+ Q_INTERFACES(Interface)
+};
+
+class C : public QObject {
+ Q_OBJECT
+};
+
+class InterfaceConsumer : public QObject {
+ Q_OBJECT
+ Q_PROPERTY(Interface* i READ interface WRITE setInterface NOTIFY interfaceChanged)
+ Q_PROPERTY(int testValue READ testValue NOTIFY testValueChanged)
+
+
+public:
+
+ Interface* interface() const
+ {
+ return m_interface;
+ }
+ void setInterface(Interface* interface)
+ {
+ QObject* object = reinterpret_cast<QObject*>(interface);
+ m_testValue = object->property("i").toInt();
+ emit testValueChanged();
+ if (m_interface == interface)
+ return;
+
+ m_interface = interface;
+ emit interfaceChanged();
+ }
+
+ int testValue() {
+ return m_testValue;
+ }
+
+signals:
+ void interfaceChanged();
+ void testValueChanged();
+
+private:
+ Interface* m_interface = nullptr;
+ int m_testValue = 0;
+};
+void tst_qqmlproperty::interfaceBinding()
+{
+
+ qmlRegisterInterface<Interface>("Interface");
+ qmlRegisterType<A>("io.qt.bugreports", 1, 0, "A");
+ qmlRegisterType<B>("io.qt.bugreports", 1, 0, "B");
+ qmlRegisterType<C>("io.qt.bugreports", 1, 0, "C");
+ qmlRegisterType<InterfaceConsumer>("io.qt.bugreports", 1, 0, "InterfaceConsumer");
+
+ const QUrl url = testFileUrl("interfaceBinding.qml");
+ QQmlEngine engine;
+ QQmlComponent component(&engine, url);
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(root);
+ QCOMPARE(root->findChild<QObject*>("a1")->property("testValue").toInt(), 42);
+ QCOMPARE(root->findChild<QObject*>("a2")->property("testValue").toInt(), 43);
+ QCOMPARE(root->findChild<QObject*>("a3")->property("testValue").toInt(), 44);
+}
+
void tst_qqmlproperty::floatToStringPrecision_data()
{
QTest::addColumn<QString>("propertyName");
diff --git a/tests/auto/qml/qqmlpropertycache/data/SpecialObject1.qml b/tests/auto/qml/qqmlpropertycache/data/SpecialObject1.qml
new file mode 100644
index 0000000000..9559bc0b5f
--- /dev/null
+++ b/tests/auto/qml/qqmlpropertycache/data/SpecialObject1.qml
@@ -0,0 +1,5 @@
+import QtQml 2.0
+
+QtObject {
+ readonly property bool fakeProperty: false
+}
diff --git a/tests/auto/qml/qqmlpropertycache/data/SpecialObject2.qml b/tests/auto/qml/qqmlpropertycache/data/SpecialObject2.qml
new file mode 100644
index 0000000000..ed4ad04fef
--- /dev/null
+++ b/tests/auto/qml/qqmlpropertycache/data/SpecialObject2.qml
@@ -0,0 +1,5 @@
+import QtQml 2.0
+
+QtObject {
+ objectName: "special"
+}
diff --git a/tests/auto/qml/qqmlpropertycache/data/noDuckType.qml b/tests/auto/qml/qqmlpropertycache/data/noDuckType.qml
new file mode 100644
index 0000000000..5e1ea233b9
--- /dev/null
+++ b/tests/auto/qml/qqmlpropertycache/data/noDuckType.qml
@@ -0,0 +1,7 @@
+import QtQml 2.9
+
+QtObject {
+ property SpecialObject1 obj1: SpecialObject1 {}
+ property SpecialObject2 obj2: SpecialObject2 {}
+ property string result: (obj1 instanceof SpecialObject2) ? "bad" : "good"
+}
diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
index 02b5302a45..c9e92cd3c9 100644
--- a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
+++ b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
@@ -31,7 +31,6 @@
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlcomponent.h>
-#include <private/qv8engine_p.h>
#include <private/qmetaobjectbuilder_p.h>
#include <QCryptographicHash>
#include "../../shared/util.h"
@@ -55,6 +54,7 @@ private slots:
void metaObjectSize_data();
void metaObjectSize();
void metaObjectChecksum();
+ void metaObjectsForRootElements();
private:
QQmlEngine engine;
@@ -544,4 +544,14 @@ void tst_qqmlpropertycache::metaObjectChecksum()
}
}
+void tst_qqmlpropertycache::metaObjectsForRootElements()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("noDuckType.qml"));
+ QVERIFY(c.isReady());
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY(!obj.isNull());
+ QCOMPARE(obj->property("result").toString(), QString::fromLatin1("good"));
+}
+
QTEST_MAIN(tst_qqmlpropertycache)
diff --git a/tests/auto/qml/qqmlpropertymap/dummy_imports.qml b/tests/auto/qml/qqmlpropertymap/dummy_imports.qml
new file mode 100644
index 0000000000..4ae9d3f2cf
--- /dev/null
+++ b/tests/auto/qml/qqmlpropertymap/dummy_imports.qml
@@ -0,0 +1,8 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in the
+// C++ code belonging to the test.
+
+import QtQuick 2.0
+
+QtObject { } // This is needed in order to keep importscanner happy
diff --git a/tests/auto/qml/qqmlpropertymap/qqmlpropertymap.pro b/tests/auto/qml/qqmlpropertymap/qqmlpropertymap.pro
index 8da300171d..b83e1e0da2 100644
--- a/tests/auto/qml/qqmlpropertymap/qqmlpropertymap.pro
+++ b/tests/auto/qml/qqmlpropertymap/qqmlpropertymap.pro
@@ -7,3 +7,5 @@ SOURCES += tst_qqmlpropertymap.cpp
include (../../shared/util.pri)
QT += core-private gui-private qml-private quick-private testlib
+
+TESTDATA = data/*
diff --git a/tests/auto/qml/qqmlsqldatabase/dummy_imports.qml b/tests/auto/qml/qqmlsqldatabase/dummy_imports.qml
new file mode 100644
index 0000000000..4ae9d3f2cf
--- /dev/null
+++ b/tests/auto/qml/qqmlsqldatabase/dummy_imports.qml
@@ -0,0 +1,8 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in the
+// C++ code belonging to the test.
+
+import QtQuick 2.0
+
+QtObject { } // This is needed in order to keep importscanner happy
diff --git a/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp b/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp
index 68ed22c01c..a6f0d65453 100644
--- a/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp
+++ b/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp
@@ -78,7 +78,7 @@ void tst_qqmlstatemachine::tst_cppObjectSignal()
CppObject cppObject;
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("cppsignal.qml"));
- QVERIFY(!component.isError());
+ QVERIFY2(!component.isError(), qPrintable(component.errorString()));
QQmlContext *ctxt = engine.rootContext();
ctxt->setContextProperty("_cppObject", &cppObject);
diff --git a/tests/auto/qml/qqmltablemodel/data/TestModel.qml b/tests/auto/qml/qqmltablemodel/data/TestModel.qml
new file mode 100644
index 0000000000..00e1fa65a7
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/data/TestModel.qml
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import Qt.labs.qmlmodels 1.0
+
+import "TestUtils.js" as TestUtils
+
+TableModel {
+ id: testModel
+ objectName: "testModel"
+
+ TableModelColumn { display: "name" }
+ TableModelColumn { display: "age" }
+
+ rows: [
+ {
+ name: "John",
+ age: 22
+ },
+ {
+ name: "Oliver",
+ age: 33
+ }
+ ]
+}
diff --git a/tests/auto/qml/qqmltablemodel/data/TestUtils.js b/tests/auto/qml/qqmltablemodel/data/TestUtils.js
new file mode 100644
index 0000000000..0b92a377bb
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/data/TestUtils.js
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+function testModelRoleDataProvider(index, role, cellData) {
+ switch (role) {
+ case "display":
+ switch (index.column) {
+ case 0:
+ return cellData.name
+ case 1:
+ return cellData.age
+ }
+ break
+ case "name":
+ return cellData.name
+ case "age":
+ return cellData.age
+ }
+ return cellData
+}
diff --git a/tests/auto/qml/qqmltablemodel/data/common.qml b/tests/auto/qml/qqmltablemodel/data/common.qml
new file mode 100644
index 0000000000..2f8b0c072b
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/data/common.qml
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.13
+import Qt.labs.qmlmodels 1.0
+
+Item {
+ id: root
+ width: 200
+ height: 200
+
+ property alias testModel: testModel
+ property alias tableView: tableView
+
+ function appendRow(personName, personAge) {
+ testModel.appendRow({
+ name: personName,
+ age: personAge
+ })
+ }
+
+ function appendRowExtraData() {
+ testModel.appendRow({
+ name: "Foo",
+ age: 99,
+ nonExistentRole: 123
+ })
+ }
+
+ function appendRowInvalid1() {
+ testModel.appendRow(123)
+ }
+
+ function appendRowInvalid2() {
+ testModel.appendRow({
+ name: "Foo",
+ age: []
+ })
+ }
+
+ function appendRowInvalid3() {
+ testModel.appendRow([
+ { name: "Bar" },
+ { age: "111" }
+ ])
+ }
+
+ function insertRow(personName, personAge, rowIndex) {
+ testModel.insertRow(rowIndex, {
+ name: personName,
+ age: personAge
+ })
+ }
+
+ function insertRowExtraData() {
+ testModel.insertRow(0, {
+ name: "Foo",
+ age: 99,
+ nonExistentRole: 123
+ })
+ }
+
+ function insertRowInvalid1() {
+ testModel.insertRow(0, 123)
+ }
+
+ function insertRowInvalid2() {
+ testModel.insertRow(0, {
+ name: "Foo",
+ age: []
+ })
+ }
+
+ function insertRowInvalid3() {
+ testModel.insertRow(0, [
+ { name: "Bar" },
+ { age: "111" }
+ ])
+ }
+
+ function setRow(rowIndex, personName, personAge) {
+ testModel.setRow(rowIndex, {
+ name: personName,
+ age: personAge
+ })
+ }
+
+ function setRowExtraData() {
+ testModel.setRow(0, {
+ name: "Foo",
+ age: 99,
+ nonExistentRole: 123
+ })
+ }
+
+ function setRowInvalid1() {
+ testModel.setRow(0, 123)
+ }
+
+ function setRowInvalid2() {
+ testModel.setRow(0, {
+ name: "Foo",
+ age: []
+ })
+ }
+
+ function setRowInvalid3() {
+ testModel.setRow(0, [
+ { name: "Bar" },
+ { age: "111" }
+ ])
+ }
+
+ TableView {
+ id: tableView
+ anchors.fill: parent
+ model: TestModel {
+ id: testModel
+ }
+ delegate: Text {
+ text: model.display
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmltablemodel/data/complex.qml b/tests/auto/qml/qqmltablemodel/data/complex.qml
new file mode 100644
index 0000000000..dbf53bac7e
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/data/complex.qml
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.13
+import Qt.labs.qmlmodels 1.0
+
+TableView {
+ width: 100
+ height: 100
+ delegate: Item {
+ implicitWidth: 50
+ implicitHeight: 50
+
+ Text {
+ text: model.display
+ anchors.centerIn: parent
+ }
+ }
+ model: TableModel {
+ id: testModel
+ objectName: "testModel"
+
+ TableModelColumn {
+ display: function(modelIndex) { return testModel.rows[modelIndex.row][0].name }
+ setDisplay: function(modelIndex, cellData) { testModel.rows[modelIndex.row][0].name = cellData }
+ }
+ TableModelColumn {
+ display: function(modelIndex) { return testModel.rows[modelIndex.row][1].age }
+ setDisplay: function(modelIndex, cellData) { testModel.rows[modelIndex.row][1].age = cellData }
+ }
+
+ rows: [
+ [
+ { name: "John" },
+ { age: 22 }
+ ],
+ [
+ { name: "Oliver" },
+ { age: 33 }
+ ]
+ ]
+ }
+}
diff --git a/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml b/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml
new file mode 100644
index 0000000000..d3f726bfa1
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import Qt.labs.qmlmodels 1.0
+
+TableView {
+ width: 200; height: 200
+ model: TestModel {
+ id: testModel
+
+ // This is silly: in real life, store the birthdate instead of the age,
+ // and let the delegate calculate the age, so it won't need updating
+ function happyBirthday(dude) {
+ var row = -1;
+ for (var r = 0; row < 0 && r < testModel.rowCount; ++r)
+ if (testModel.data(testModel.index(r, 0), "display") === dude)
+ row = r;
+ var index = testModel.index(row, 1)
+ testModel.setData(index, "display", testModel.data(index, "display") + 1)
+ }
+ }
+ delegate: Text {
+ id: textItem
+ text: model.display
+ TapHandler {
+ onTapped: testModel.happyBirthday(testModel.data(testModel.index(row, 0), "display"))
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmltablemodel/data/empty.qml b/tests/auto/qml/qqmltablemodel/data/empty.qml
new file mode 100644
index 0000000000..f5afbd1d27
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/data/empty.qml
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import Qt.labs.qmlmodels 1.0
+
+Item {
+ id: root
+ width: 200
+ height: 200
+
+ property alias testModel: testModel
+ property alias tableView: tableView
+
+ function setRows() {
+ testModel.rows = [
+ {
+ name: "John",
+ age: 22
+ },
+ {
+ name: "Oliver",
+ age: 33
+ }
+ ]
+ }
+
+ function appendJohn() {
+ testModel.appendRow({
+ name: "John",
+ age: 22
+ })
+ }
+
+ function appendOliver() {
+ testModel.appendRow({
+ name: "Oliver",
+ age: 33
+ })
+ }
+
+ TableModel {
+ id: testModel
+ objectName: "testModel"
+
+ TableModelColumn { display: "name" }
+ TableModelColumn { display: "age" }
+ }
+ TableView {
+ id: tableView
+ anchors.fill: parent
+ model: testModel
+ delegate: Text {
+ text: model.display
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml b/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml
new file mode 100644
index 0000000000..86bcb08fa2
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import Qt.labs.qmlmodels 1.0
+
+TableModel {
+ objectName: "testModel"
+
+ TableModelColumn { display: "name" }
+ TableModelColumn { display: "age" }
+
+ rows: [
+ {
+ name: "John",
+ age: 22
+ },
+ {
+ name: "Oliver",
+ age: 33
+ }
+ ]
+}
diff --git a/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml b/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml
new file mode 100644
index 0000000000..ebfe4ed930
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import Qt.labs.qmlmodels 1.0
+
+Item {
+ id: root
+ width: 200
+ height: 200
+
+ property alias testModel: testModel
+
+ signal shouldModify()
+ signal shouldModifyInvalidRole()
+ signal shouldModifyInvalidType()
+
+ function modify() {
+ shouldModify()
+ }
+
+ function modifyInvalidRole() {
+ shouldModifyInvalidRole()
+ }
+
+ function modifyInvalidType() {
+ shouldModifyInvalidType()
+ }
+
+ TableView {
+ anchors.fill: parent
+ model: TestModel {
+ id: testModel
+ }
+
+ delegate: Text {
+ id: textItem
+ text: model.display
+
+ Connections {
+ target: root
+ enabled: column === 1
+ onShouldModify: model.display = 18
+ }
+
+ Connections {
+ target: root
+ enabled: column === 0
+ // Invalid: should be "display".
+ onShouldModifyInvalidRole: model.age = 100
+ }
+
+ Connections {
+ target: root
+ enabled: column === 1
+ // Invalid: should be string.
+ onShouldModifyInvalidType: model.display = "Whoops"
+ }
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml b/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml
new file mode 100644
index 0000000000..01ec40270c
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import Qt.labs.qmlmodels 1.0
+
+Item {
+ id: root
+ width: 200
+ height: 200
+
+ property alias testModel: testModel
+ property alias tableView: tableView
+
+ function setRowsValid() {
+ testModel.rows = [
+ {
+ name: "Max",
+ age: 20
+ },
+ {
+ name: "Imum",
+ age: 41
+ },
+ {
+ name: "Power",
+ age: 89
+ }
+ ]
+ }
+
+ function setRowsInvalid() {
+ testModel.rows = [
+ {
+ nope: "Nope",
+ age: 20
+ },
+ {
+ nope: "Nah",
+ age: 41
+ },
+ {
+ nope: "No",
+ age: 89
+ }
+ ]
+ }
+
+ TableView {
+ id: tableView
+ anchors.fill: parent
+ model: TestModel {
+ id: testModel
+ }
+ delegate: Text {
+ text: model.display
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro b/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro
new file mode 100644
index 0000000000..9d298dfdf2
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/qqmltablemodel.pro
@@ -0,0 +1,10 @@
+CONFIG += testcase
+TARGET = tst_qqmltablemodel
+
+SOURCES += tst_qqmltablemodel.cpp
+
+include (../../shared/util.pri)
+
+TESTDATA = data/*
+
+QT += core gui qml-private qml quick-private quick testlib qmlmodels-private
diff --git a/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp
new file mode 100644
index 0000000000..d913bcdf9a
--- /dev/null
+++ b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp
@@ -0,0 +1,980 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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 <QtTest/qtest.h>
+#include <QtTest/qsignalspy.h>
+#include <QtCore/qregularexpression.h>
+#include <QtQml/private/qqmlengine_p.h>
+#include <QtQmlModels/private/qqmltablemodel_p.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQuick/qquickitem.h>
+#include <QtQuick/qquickview.h>
+#include <QtQuick/private/qquicktableview_p.h>
+
+#include "../../shared/util.h"
+
+class tst_QQmlTableModel : public QQmlDataTest
+{
+ Q_OBJECT
+
+public:
+ tst_QQmlTableModel() {}
+
+private slots:
+ void appendRemoveRow();
+ void appendRowToEmptyModel();
+ void clear();
+ void getRow();
+ void insertRow();
+ void moveRow();
+ void setRow();
+ void setDataThroughDelegate();
+ void setRowsImperatively();
+ void setRowsMultipleTimes();
+ void dataAndEditing();
+ void omitTableModelColumnIndex();
+ void complexRow();
+};
+
+void tst_QQmlTableModel::appendRemoveRow()
+{
+ QQuickView view(testFileUrl("common.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+
+ QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
+ QVERIFY(columnCountSpy.isValid());
+
+ QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
+ QVERIFY(rowCountSpy.isValid());
+ int rowCountSignalEmissions = 0;
+
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QCOMPARE(roleNames.size(), 1);
+ QVERIFY(roleNames.values().contains("display"));
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+
+ // Call remove() with a negative rowIndex.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*removeRow\\(\\): \"rowIndex\" cannot be negative"));
+ QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, -1)));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Call remove() with an rowIndex that is too large.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*removeRow\\(\\): \"rowIndex\" 2 is greater than or equal to rowCount\\(\\) of 2"));
+ QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 2)));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Call remove() with a valid rowIndex but negative rows.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*removeRow\\(\\): \"rows\" is less than or equal to zero"));
+ QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0), Q_ARG(int, -1)));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Call remove() with a valid rowIndex but excessive rows.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*removeRow\\(\\): \"rows\" 3 exceeds available rowCount\\(\\) of 2 when removing from \"rowIndex\" 0"));
+ QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0), Q_ARG(int, 3)));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Call remove() without specifying the number of rows to remove; it should remove one row.
+ QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0)));
+ QCOMPARE(model->rowCount(), 1);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
+
+ // Call append() with a row that has an unexpected role; the row should be added and the extra data ignored.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowExtraData"));
+ // Nothing should change.
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
+
+ // Call append() with a row that is an int.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*appendRow\\(\\): expected \"row\" argument to be a QJSValue, but got int instead:\nQVariant\\(int, 123\\)"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowInvalid1"));
+ // Nothing should change.
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Call append() with a row with a role of the wrong type.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*appendRow\\(\\): expected the property named \"age\" to be of type \"int\", but got \"QVariantList\" instead"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowInvalid2"));
+ // Nothing should change.
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Call append() with a row that is an array instead of a simple object.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*appendRow\\(\\): row manipulation functions do not support complex rows \\(row index: -1\\)"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowInvalid3"));
+ // Nothing should change.
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Call append() to insert one row.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40)));
+ QCOMPARE(model->rowCount(), 3);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
+
+ // Call remove() and specify rowIndex and rows, removing all remaining rows.
+ QVERIFY(QMetaObject::invokeMethod(model, "removeRow", Q_ARG(int, 0), Q_ARG(int, 3)));
+ QCOMPARE(model->rowCount(), 0);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")), QVariant());
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")), QVariant());
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
+}
+
+void tst_QQmlTableModel::appendRowToEmptyModel()
+{
+ QQuickView view(testFileUrl("empty.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 0);
+ QCOMPARE(model->columnCount(), 2);
+
+ QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
+ QVERIFY(columnCountSpy.isValid());
+
+ QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
+ QVERIFY(rowCountSpy.isValid());
+
+ QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>();
+ QVERIFY(tableView);
+ QCOMPARE(tableView->rows(), 0);
+ QCOMPARE(tableView->columns(), 2);
+
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendJohn"));
+ QCOMPARE(model->rowCount(), 1);
+ QCOMPARE(model->columnCount(), 2);
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), 1);
+ QTRY_COMPARE(tableView->rows(), 1);
+ QCOMPARE(tableView->columns(), 2);
+}
+
+void tst_QQmlTableModel::clear()
+{
+ QQuickView view(testFileUrl("common.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+
+ QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
+ QVERIFY(columnCountSpy.isValid());
+
+ QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
+ QVERIFY(rowCountSpy.isValid());
+
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QVERIFY(roleNames.values().contains("display"));
+ QCOMPARE(roleNames.size(), 1);
+
+ QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>();
+ QVERIFY(tableView);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ QVERIFY(QMetaObject::invokeMethod(model, "clear"));
+ QCOMPARE(model->rowCount(), 0);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")), QVariant());
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")), QVariant());
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), 1);
+ // Wait until updatePolish() gets called, which is where the size is recalculated.
+ QTRY_COMPARE(tableView->rows(), 0);
+ QCOMPARE(tableView->columns(), 2);
+}
+
+void tst_QQmlTableModel::getRow()
+{
+ QQuickView view(testFileUrl("common.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+
+ // Call get() with a negative row index.
+ QVariant returnValue;
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*getRow\\(\\): \"rowIndex\" cannot be negative"));
+ QVERIFY(QMetaObject::invokeMethod(model, "getRow", Q_RETURN_ARG(QVariant, returnValue), Q_ARG(int, -1)));
+ QVERIFY(!returnValue.isValid());
+
+ // Call get() with a row index that is too large.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*getRow\\(\\): \"rowIndex\" 2 is greater than or equal to rowCount\\(\\) of 2"));
+ QVERIFY(QMetaObject::invokeMethod(model, "getRow", Q_RETURN_ARG(QVariant, returnValue), Q_ARG(int, 2)));
+ QVERIFY(!returnValue.isValid());
+
+ // Call get() with a valid row index.
+ QVERIFY(QMetaObject::invokeMethod(model, "getRow", Q_RETURN_ARG(QVariant, returnValue), Q_ARG(int, 0)));
+ const QVariantMap rowAsVariantMap = returnValue.toMap();
+ QCOMPARE(rowAsVariantMap.value(QLatin1String("name")).toString(), QLatin1String("John"));
+ QCOMPARE(rowAsVariantMap.value(QLatin1String("age")).toInt(), 22);
+}
+
+void tst_QQmlTableModel::insertRow()
+{
+ QQuickView view(testFileUrl("common.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+
+ QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
+ QVERIFY(columnCountSpy.isValid());
+
+ QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
+ QVERIFY(rowCountSpy.isValid());
+ int rowCountSignalEmissions = 0;
+
+ QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>();
+ QVERIFY(tableView);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to insert with a negative index.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*insertRow\\(\\): \"rowIndex\" cannot be negative"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow",
+ Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40), Q_ARG(QVariant, -1)));
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->rowCount(), 2);
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to insert past the last allowed index.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*insertRow\\(\\): \"rowIndex\" 3 is greater than rowCount\\(\\) of 2"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow",
+ Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40), Q_ARG(QVariant, 3)));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Call insert() with a row that is an int.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*insertRow\\(\\): expected \"row\" argument to be a QJSValue, but got int instead:\nQVariant\\(int, 123\\)"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowInvalid1"));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to insert a row with a role of the wrong type.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*insertRow\\(\\): expected the property named \"age\" to be of type \"int\", but got \"QVariantList\" instead"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowInvalid2"));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to insert a row that is an array instead of a simple object.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*insertRow\\(\\): row manipulation functions do not support complex rows \\(row index: 0\\)"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowInvalid3"));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to insert a row has an unexpected role; the row should be added and the extra data ignored.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRowExtraData"));
+ QCOMPARE(model->rowCount(), 3);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
+ QTRY_COMPARE(tableView->rows(), 3);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Insert a row at the bottom of the table.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow",
+ Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40), Q_ARG(QVariant, 3)));
+ QCOMPARE(model->rowCount(), 4);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
+ QTRY_COMPARE(tableView->rows(), 4);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Insert a row in the middle of the table.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow",
+ Q_ARG(QVariant, QLatin1String("Daisy")), Q_ARG(QVariant, 30), Q_ARG(QVariant, 2)));
+ QCOMPARE(model->rowCount(), 5);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 30);
+ QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
+ QTRY_COMPARE(tableView->rows(), 5);
+ QCOMPARE(tableView->columns(), 2);
+}
+
+void tst_QQmlTableModel::moveRow()
+{
+ QQuickView view(testFileUrl("common.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->rowCount(), 2);
+
+ QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
+ QVERIFY(columnCountSpy.isValid());
+
+ QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
+ QVERIFY(rowCountSpy.isValid());
+ int rowCountSignalEmissions = 0;
+
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+
+ // Append some rows.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40)));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Daisy")), Q_ARG(QVariant, 30)));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRow", Q_ARG(QVariant, QLatin1String("Trev")), Q_ARG(QVariant, 48)));
+ QCOMPARE(model->rowCount(), 5);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy"));
+ QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 30);
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48);
+ rowCountSignalEmissions = 3;
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Try to move with a fromRowIndex that is negative.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"fromRowIndex\" cannot be negative"));
+ QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, -1), Q_ARG(int, 1)));
+ // Shouldn't have changed.
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Try to move with a fromRowIndex that is too large.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"fromRowIndex\" 5 is greater than or equal to rowCount\\(\\)"));
+ QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 5), Q_ARG(int, 1)));
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Try to move with a toRowIndex that is negative.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"toRowIndex\" cannot be negative"));
+ QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, -1)));
+ // Shouldn't have changed.
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Try to move with a toRowIndex that is too large.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*moveRow\\(\\): \"toRowIndex\" 5 is greater than or equal to rowCount\\(\\)"));
+ QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, 5)));
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Move the first row to the end.
+ QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, 4)));
+ // The counts shouldn't have changed.
+ QCOMPARE(model->rowCount(), 5);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 30);
+ QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev"));
+ QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 48);
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Move it back again.
+ QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 4), Q_ARG(int, 0)));
+ QCOMPARE(model->rowCount(), 5);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy"));
+ QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 30);
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+
+ // Move the first row down one by one row.
+ QVERIFY(QMetaObject::invokeMethod(model, "moveRow", Q_ARG(int, 0), Q_ARG(int, 1)));
+ QCOMPARE(model->rowCount(), 5);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy"));
+ QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("display")).toInt(), 30);
+ QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Trev"));
+ QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("display")).toInt(), 48);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+}
+
+void tst_QQmlTableModel::setRow()
+{
+ QQuickView view(testFileUrl("common.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->rowCount(), 2);
+
+ QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
+ QVERIFY(columnCountSpy.isValid());
+
+ QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
+ QVERIFY(rowCountSpy.isValid());
+ int rowCountSignalEmissions = 0;
+
+ QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>();
+ QVERIFY(tableView);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to set with a negative index.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*setRow\\(\\): \"rowIndex\" cannot be negative"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow",
+ Q_ARG(QVariant, -1), Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40)));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to set at an index past the last allowed index.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*setRow\\(\\): \"rowIndex\" 3 is greater than rowCount\\(\\) of 2"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow",
+ Q_ARG(QVariant, 3), Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40)));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to set a row that has an unexpected role; the row should be set and the extra data ignored.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowExtraData"));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to insert a row that is not an array.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*setRow\\(\\): expected \"row\" argument to be a QJSValue, but got int instead:\nQVariant\\(int, 123\\)"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowInvalid1"));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to insert a row with a role that is of the wrong type.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*setRow\\(\\): expected the property named \"age\" to be of type \"int\", but got \"QVariantList\" instead"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowInvalid2"));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Try to insert a row that is an array instead of a simple object.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*setRow\\(\\): row manipulation functions do not support complex rows \\(row index: 0\\)"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowInvalid3"));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Foo"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Set the first row.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow",
+ Q_ARG(QVariant, 0), Q_ARG(QVariant, QLatin1String("Max")), Q_ARG(QVariant, 40)));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Set the last row.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow",
+ Q_ARG(QVariant, 1), Q_ARG(QVariant, QLatin1String("Daisy")), Q_ARG(QVariant, 30)));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 30);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Append a row by passing an index that is equal to rowCount().
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow",
+ Q_ARG(QVariant, 2), Q_ARG(QVariant, QLatin1String("Wot")), Q_ARG(QVariant, 99)));
+ QCOMPARE(model->rowCount(), 3);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 40);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Daisy"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 30);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Wot"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 99);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
+ QTRY_COMPARE(tableView->rows(), 3);
+ QCOMPARE(tableView->columns(), 2);
+}
+
+void tst_QQmlTableModel::setDataThroughDelegate()
+{
+ QQuickView view(testFileUrl("setDataThroughDelegate.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+
+ QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
+ QVERIFY(columnCountSpy.isValid());
+
+ QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
+ QVERIFY(rowCountSpy.isValid());
+
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QCOMPARE(roleNames.size(), 1);
+ QVERIFY(roleNames.values().contains("display"));
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), 0);
+
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "modify"));
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 18);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 18);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), 0);
+
+ // Test setting a role that doesn't exist for a certain column.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "modifyInvalidRole"));
+ // Should be unchanged.
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 18);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 18);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), 0);
+
+ // Test setting a role with a value of the wrong type.
+ // There are two rows, so two delegates respond to the signal, which means we need to ignore two warnings.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*setData\\(\\): failed converting value QVariant\\(QString, \"Whoops\"\\) " \
+ "set at row 0 column 1 with role \"display\" to \"int\""));
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*setData\\(\\): failed converting value QVariant\\(QString, \"Whoops\"\\) " \
+ "set at row 1 column 1 with role \"display\" to \"int\""));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "modifyInvalidType"));
+ // Should be unchanged.
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 18);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 18);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), 0);
+}
+
+// Start off with empty rows and then set them to test rowCountChanged().
+void tst_QQmlTableModel::setRowsImperatively()
+{
+ QQuickView view(testFileUrl("empty.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 0);
+ QCOMPARE(model->columnCount(), 2);
+
+ QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
+ QVERIFY(columnCountSpy.isValid());
+
+ QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
+ QVERIFY(rowCountSpy.isValid());
+
+ QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>();
+ QVERIFY(tableView);
+ QCOMPARE(tableView->rows(), 0);
+ QCOMPARE(tableView->columns(), 2);
+
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRows"));
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), 1);
+ QTRY_COMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+}
+
+void tst_QQmlTableModel::setRowsMultipleTimes()
+{
+ QQuickView view(testFileUrl("setRowsMultipleTimes.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+
+ QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
+ QVERIFY(columnCountSpy.isValid());
+
+ QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
+ QVERIFY(rowCountSpy.isValid());
+
+ QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>();
+ QVERIFY(tableView);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Set valid rows after they've already been declared.
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowsValid"));
+ QCOMPARE(model->rowCount(), 3);
+ QCOMPARE(model->columnCount(), 2);
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 20);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Imum"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 41);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Power"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 89);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), 1);
+ QTRY_COMPARE(tableView->rows(), 3);
+ QCOMPARE(tableView->columns(), 2);
+
+ // Set invalid rows; we should get a warning and nothing should change.
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ ".*setRows\\(\\): expected a property named \"name\" in row at index 0, but couldn't find one"));
+ QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowsInvalid"));
+ QCOMPARE(model->rowCount(), 3);
+ QCOMPARE(model->columnCount(), 2);
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Max"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 20);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Imum"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 41);
+ QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Power"));
+ QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("display")).toInt(), 89);
+ QCOMPARE(columnCountSpy.count(), 0);
+ QCOMPARE(rowCountSpy.count(), 1);
+ QCOMPARE(tableView->rows(), 3);
+ QCOMPARE(tableView->columns(), 2);
+}
+
+void tst_QQmlTableModel::dataAndEditing()
+{
+ QQuickView view(testFileUrl("dataAndSetData.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQmlTableModel *model = view.rootObject()->property("model").value<QQmlTableModel*>();
+ QVERIFY(model);
+
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QVERIFY(roleNames.values().contains("display"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+ QVERIFY(QMetaObject::invokeMethod(model, "happyBirthday", Q_ARG(QVariant, QLatin1String("Oliver"))));
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 34);
+}
+
+void tst_QQmlTableModel::omitTableModelColumnIndex()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("omitTableModelColumnIndex.qml"));
+ QCOMPARE(component.status(), QQmlComponent::Ready);
+
+ QScopedPointer<QQmlTableModel> model(qobject_cast<QQmlTableModel*>(component.create()));
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+}
+
+void tst_QQmlTableModel::complexRow()
+{
+ QQuickView view(testFileUrl("complex.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
+
+ QQuickTableView *tableView = qobject_cast<QQuickTableView*>(view.rootObject());
+ QVERIFY(tableView);
+ QCOMPARE(tableView->rows(), 2);
+ QCOMPARE(tableView->columns(), 2);
+
+ QQmlTableModel *model = tableView->model().value<QQmlTableModel*>();
+ QVERIFY(model);
+ QCOMPARE(model->rowCount(), 2);
+ QCOMPARE(model->columnCount(), 2);
+
+ const QHash<int, QByteArray> roleNames = model->roleNames();
+ QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("John"));
+ QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 22);
+ QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Oliver"));
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
+}
+
+QTEST_MAIN(tst_QQmlTableModel)
+
+#include "tst_qqmltablemodel.moc"
diff --git a/tests/auto/qml/qqmltimer/dummy_imports.qml b/tests/auto/qml/qqmltimer/dummy_imports.qml
new file mode 100644
index 0000000000..f78e04d489
--- /dev/null
+++ b/tests/auto/qml/qqmltimer/dummy_imports.qml
@@ -0,0 +1,9 @@
+// This file exists for the sole purpose for qmlimportscanner to find
+// which modules it needs to extract for deployment.
+// Otherwise, it fails to find the imports that are expressed in the
+// C++ code belonging to the test.
+
+import QtQml 2.0
+import QtQuick 2.0
+
+QtObject { } // This is needed in order to keep importscanner happy
diff --git a/tests/auto/qml/qqmltranslation/data/mylibrary.js b/tests/auto/qml/qqmltranslation/data/mylibrary.js
new file mode 100644
index 0000000000..5903db3b4b
--- /dev/null
+++ b/tests/auto/qml/qqmltranslation/data/mylibrary.js
@@ -0,0 +1,5 @@
+Qt.include("nested_js_translation.js")
+
+function translation_success() {
+ return qsTr("English in mylibrary");
+}
diff --git a/tests/auto/qml/qqmltranslation/data/nested_js_translation.js b/tests/auto/qml/qqmltranslation/data/nested_js_translation.js
new file mode 100644
index 0000000000..336cdedfea
--- /dev/null
+++ b/tests/auto/qml/qqmltranslation/data/nested_js_translation.js
@@ -0,0 +1,3 @@
+function translation_fail() {
+ return qsTr("English in translation")
+}
diff --git a/tests/auto/qml/qqmltranslation/data/preferjs.qml b/tests/auto/qml/qqmltranslation/data/preferjs.qml
new file mode 100644
index 0000000000..040fa12e4e
--- /dev/null
+++ b/tests/auto/qml/qqmltranslation/data/preferjs.qml
@@ -0,0 +1,8 @@
+import QtQml 2.12
+
+import "mylibrary.js" as Lib
+
+QtObject {
+ property string german1: Lib.translation_fail()
+ property string german2: Lib.translation_success()
+}
diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
index 809a9bd9db..a75a00bd01 100644
--- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
+++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
@@ -32,6 +32,7 @@
#include <QTranslator>
#include <QQmlContext>
#include <private/qqmlengine_p.h>
+#include <private/qqmltypedata_p.h>
#include "../../shared/util.h"
class tst_qqmltranslation : public QQmlDataTest
@@ -45,6 +46,7 @@ private slots:
void translation();
void idTranslation();
void translationChange();
+ void preferJSContext();
};
void tst_qqmltranslation::translation_data()
@@ -85,7 +87,8 @@ void tst_qqmltranslation::translation()
<< QStringLiteral("disambiguation")
<< QStringLiteral("singular") << QStringLiteral("plural");
- const QV4::CompiledData::Object *rootObject = compilationUnit->objectAt(/*root object*/0);
+ const QV4::CompiledData::Object *rootObject
+ = compilationUnit->qmlData->objectAt(/*root object*/0);
const QV4::CompiledData::Binding *binding = rootObject->bindingTable();
for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) {
const QString propertyName = compilationUnit->stringAt(binding->propertyNameIndex);
@@ -139,7 +142,8 @@ void tst_qqmltranslation::idTranslation()
QV4::CompiledData::CompilationUnit *compilationUnit = typeData->compilationUnit();
QVERIFY(compilationUnit);
- const QV4::CompiledData::Object *rootObject = compilationUnit->objectAt(/*root object*/0);
+ const QV4::CompiledData::Object *rootObject
+ = compilationUnit->qmlData->objectAt(/*root object*/0);
const QV4::CompiledData::Binding *binding = rootObject->bindingTable();
for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) {
const QString propertyName = compilationUnit->stringAt(binding->propertyNameIndex);
@@ -172,6 +176,10 @@ class DummyTranslator : public QTranslator
Q_UNUSED(n);
if (!qstrcmp(sourceText, "translate me"))
return QString::fromUtf8("xxx");
+ if (!qstrcmp(sourceText, "English in mylibrary") && !qstrcmp(context, "mylibrary"))
+ return QString::fromUtf8("Deutsch in mylibrary");
+ if (!qstrcmp(sourceText, "English in translation") && !qstrcmp(context, "nested_js_translation"))
+ return QString::fromUtf8("Deutsch in Setzung");
return QString();
}
@@ -210,6 +218,24 @@ void tst_qqmltranslation::translationChange()
QCoreApplication::removeTranslator(&translator);
}
+void tst_qqmltranslation::preferJSContext()
+{
+ DummyTranslator translator;
+ QCoreApplication::installTranslator(&translator);
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("preferjs.qml"));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QCOMPARE(object->property("german1").toString(),
+ QStringLiteral("Deutsch in Setzung"));
+ QCOMPARE(object->property("german2").toString(),
+ QStringLiteral("Deutsch in mylibrary"));
+
+ QCoreApplication::removeTranslator(&translator);
+}
+
QTEST_MAIN(tst_qqmltranslation)
#include "tst_qqmltranslation.moc"
diff --git a/tests/auto/qml/qqmltypeloader/data/Com/Orga/BaseStyle.qml b/tests/auto/qml/qqmltypeloader/data/Com/Orga/BaseStyle.qml
new file mode 100644
index 0000000000..28521e3af2
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/Com/Orga/BaseStyle.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.6
+
+Item {
+
+}
diff --git a/tests/auto/qml/qqmltypeloader/data/Com/Orga/Handlers/Handler.qml b/tests/auto/qml/qqmltypeloader/data/Com/Orga/Handlers/Handler.qml
new file mode 100644
index 0000000000..b20a2def11
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/Com/Orga/Handlers/Handler.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.6
+import Com.Orga 1.0
+
+Rectangle {
+ color: Style.name
+ Text {text: "Hello world!"}
+}
diff --git a/tests/auto/qml/qqmltypeloader/data/Com/Orga/Handlers/qmldir b/tests/auto/qml/qqmltypeloader/data/Com/Orga/Handlers/qmldir
new file mode 100644
index 0000000000..368cb65b35
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/Com/Orga/Handlers/qmldir
@@ -0,0 +1,2 @@
+module Com.Orga.Handlers
+Handler 1.0 Handler.qml
diff --git a/tests/auto/qml/qqmltypeloader/data/Com/Orga/Style.qml b/tests/auto/qml/qqmltypeloader/data/Com/Orga/Style.qml
new file mode 100644
index 0000000000..7951f5e768
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/Com/Orga/Style.qml
@@ -0,0 +1,6 @@
+pragma Singleton
+import QtQuick 2.6
+
+BaseStyle {
+ property color name: "black"
+}
diff --git a/tests/auto/qml/qqmltypeloader/data/Com/Orga/qmldir b/tests/auto/qml/qqmltypeloader/data/Com/Orga/qmldir
new file mode 100644
index 0000000000..9c5560b323
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/Com/Orga/qmldir
@@ -0,0 +1,2 @@
+singleton Style 1.0 Style.qml
+BaseStyle 1.0 BaseStyle.qml
diff --git a/tests/auto/qml/qqmltypeloader/data/implicitimporttest.qml b/tests/auto/qml/qqmltypeloader/data/implicitimporttest.qml
new file mode 100644
index 0000000000..7a054e199b
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/implicitimporttest.qml
@@ -0,0 +1,5 @@
+import modulewithimplicitimport 2.0 as MyNS
+MyNS.Test {
+ MyNS.Item {} // Implicitly imported from QtQuick
+ MyNS.ListModel {}
+}
diff --git a/tests/auto/qml/qqmltypeloader/data/imports/modulewithimplicitimport/Test.qml b/tests/auto/qml/qqmltypeloader/data/imports/modulewithimplicitimport/Test.qml
new file mode 100644
index 0000000000..2f78302506
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/imports/modulewithimplicitimport/Test.qml
@@ -0,0 +1,3 @@
+import QtQuick 2.0
+Item {
+}
diff --git a/tests/auto/qml/qqmltypeloader/data/imports/modulewithimplicitimport/qmldir b/tests/auto/qml/qqmltypeloader/data/imports/modulewithimplicitimport/qmldir
new file mode 100644
index 0000000000..10e8f90f62
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/imports/modulewithimplicitimport/qmldir
@@ -0,0 +1,2 @@
+import QtQuick
+Test 2.0 Test.qml
diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
index 52c722aac8..9ad53aaa8b 100644
--- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
+++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
@@ -28,6 +28,7 @@
#include <QtTest/QtTest>
#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlfile.h>
#include <QtQml/qqmlnetworkaccessmanagerfactory.h>
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickitem.h>
@@ -35,6 +36,7 @@
#include <QtCore/qprocess.h>
#endif
#include <QtQml/private/qqmlengine_p.h>
+#include <QtQml/private/qqmltypedata_p.h>
#include <QtQml/private/qqmltypeloader_p.h>
#include "../../shared/testhttpserver.h"
#include "../../shared/util.h"
@@ -57,6 +59,8 @@ private slots:
void multiSingletonModule();
void implicitComponentModule();
void qrcRootPathUrl();
+ void implicitImport();
+ void compositeSingletonCycle();
};
void tst_QQMLTypeLoader::testLoadComplete()
@@ -431,7 +435,7 @@ void tst_QQMLTypeLoader::redirect()
component.loadUrl(server.urlString("/Load.qml"), QQmlComponent::Asynchronous);
QTRY_VERIFY2(component.isReady(), qPrintable(component.errorString()));
- QObject *object = component.create();
+ QScopedPointer<QObject> object {component.create()};
QTRY_COMPARE(object->property("xy").toInt(), 323232);
}
@@ -511,6 +515,33 @@ void tst_QQMLTypeLoader::qrcRootPathUrl()
QCOMPARE(component.status(), QQmlComponent::Ready);
}
+void tst_QQMLTypeLoader::implicitImport()
+{
+ QQmlEngine engine;
+ engine.addImportPath(testFile("imports"));
+ QQmlComponent component(&engine, testFileUrl("implicitimporttest.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+}
+
+void tst_QQMLTypeLoader::compositeSingletonCycle()
+{
+ TestHTTPServer server;
+ QVERIFY2(server.listen(), qPrintable(server.errorString()));
+ QVERIFY(server.serveDirectory(dataDirectory()));
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ engine.addImportPath(server.baseUrl().toString());
+ component.loadUrl(server.urlString("Com/Orga/Handlers/Handler.qml"), QQmlComponent::Asynchronous);
+ QTRY_VERIFY2(component.isReady(), qPrintable(component.errorString()));
+
+ QScopedPointer<QObject> object {component.create()};
+ QVERIFY(object);
+ QCOMPARE(qvariant_cast<QColor>(object->property("color")), QColorConstants::Black);
+}
+
QTEST_MAIN(tst_QQMLTypeLoader)
#include "tst_qqmltypeloader.moc"
diff --git a/tests/auto/qml/qqmlvaluetypes/data/color_read.qml b/tests/auto/qml/qqmlvaluetypes/data/color_read.qml
index 73d2b921a7..a2d303b507 100644
--- a/tests/auto/qml/qqmlvaluetypes/data/color_read.qml
+++ b/tests/auto/qml/qqmlvaluetypes/data/color_read.qml
@@ -12,4 +12,7 @@ MyTypeObject {
property real hsl_s: color.hslSaturation
property real hsl_l: color.hslLightness
property variant copy: color
+
+ property bool valid: color.valid
+ property bool invalid: invalidColor.valid
}
diff --git a/tests/auto/qml/qqmlvaluetypes/testtypes.h b/tests/auto/qml/qqmlvaluetypes/testtypes.h
index bcfe4028c6..798c96e188 100644
--- a/tests/auto/qml/qqmlvaluetypes/testtypes.h
+++ b/tests/auto/qml/qqmlvaluetypes/testtypes.h
@@ -69,6 +69,7 @@ class MyTypeObject : public QObject
Q_PROPERTY(QMatrix4x4 matrix READ matrix WRITE setMatrix NOTIFY changed)
Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY changed)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY changed)
+ Q_PROPERTY(QColor invalidColor READ invalidColor CONSTANT)
Q_PROPERTY(QVariant variant READ variant NOTIFY changed)
public:
@@ -168,6 +169,8 @@ public:
QColor color() const { return m_color; }
void setColor(const QColor &v) { m_color = v; emit changed(); }
+ QColor invalidColor() const { return QColor(); }
+
QVariant variant() const { return sizef(); }
void emitRunScript() { emit runScript(); }
diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
index 15bd031ce3..3e9047cc5a 100644
--- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
+++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
@@ -97,6 +97,7 @@ private slots:
void enumerableProperties();
void enumProperties();
void scarceTypes();
+ void nonValueTypes();
private:
QQmlEngine engine;
@@ -784,6 +785,7 @@ void tst_qqmlvaluetypes::font()
{
QQmlComponent component(&engine, testFileUrl("font_read.qml"));
MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
QVERIFY(object != nullptr);
QCOMPARE(object->property("f_family").toString(), object->font().family());
@@ -793,8 +795,19 @@ void tst_qqmlvaluetypes::font()
QCOMPARE(object->property("f_underline").toBool(), object->font().underline());
QCOMPARE(object->property("f_overline").toBool(), object->font().overline());
QCOMPARE(object->property("f_strikeout").toBool(), object->font().strikeOut());
- QCOMPARE(object->property("f_pointSize").toDouble(), object->font().pointSizeF());
- QCOMPARE(object->property("f_pixelSize").toInt(), int((object->font().pointSizeF() * qt_defaultDpi()) / qreal(72.)));
+
+ // If QFont::pixelSize() was set, QFont::pointSizeF() would return -1.
+ // If QFont::pointSizeF() was set, QFont::pixelSize() would return -1.
+ // QQuickFontValueType doesn't follow this semantic (if its -1 it calculates the value of
+ // the property from the other one)
+ double expectedPointSizeF = object->font().pointSizeF();
+ if (expectedPointSizeF == -1) expectedPointSizeF = object->font().pixelSize() * qreal(72.) / qreal(qt_defaultDpi());
+ int expectedPixelSize = object->font().pixelSize();
+ if (expectedPixelSize == -1) expectedPixelSize = int((object->font().pointSizeF() * qt_defaultDpi()) / qreal(72.));
+
+ QCOMPARE(object->property("f_pointSize").toDouble(), expectedPointSizeF);
+ QCOMPARE(object->property("f_pixelSize").toInt(), expectedPixelSize);
+
QCOMPARE(object->property("f_capitalization").toInt(), (int)object->font().capitalization());
QCOMPARE(object->property("f_letterSpacing").toDouble(), object->font().letterSpacing());
QCOMPARE(object->property("f_wordSpacing").toDouble(), object->font().wordSpacing());
@@ -922,6 +935,11 @@ void tst_qqmlvaluetypes::color()
QCOMPARE(qRound(object->property("hsl_s").toDouble() * 100), 74);
QCOMPARE(qRound(object->property("hsl_l").toDouble() * 100), 54);
+ QCOMPARE(object->property("valid").userType(), QMetaType::Bool);
+ QVERIFY(object->property("valid").toBool());
+ QCOMPARE(object->property("invalid").userType(), QMetaType::Bool);
+ QVERIFY(!object->property("invalid").toBool());
+
QColor comparison;
comparison.setRedF(0.2);
comparison.setGreenF(0.88);
@@ -1713,7 +1731,7 @@ void tst_qqmlvaluetypes::sequences()
QJSValue value = engine.toScriptValue(qcharVector);
QCOMPARE(value.property("length").toInt(), qcharVector.length());
for (int i = 0; i < qcharVector.length(); ++i)
- QCOMPARE(value.property(i).toInt(), qcharVector.at(i));
+ QCOMPARE(value.property(i).toString(), qcharVector.at(i));
}
{
MyTypeObject a, b, c;
@@ -1832,6 +1850,16 @@ void tst_qqmlvaluetypes::scarceTypes()
QCOMPARE(QByteArray(pixmapValue->vtable()->className), QByteArray("VariantObject"));
}
+#define CHECK_TYPE_IS_NOT_VALUETYPE(Type, typeId, cppType) \
+ QVERIFY(!QQmlValueTypeFactory::isValueType(QMetaType::Type));
+
+void tst_qqmlvaluetypes::nonValueTypes()
+{
+ CHECK_TYPE_IS_NOT_VALUETYPE(UnknownType, 0, void)
+ QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(CHECK_TYPE_IS_NOT_VALUETYPE);
+}
+
+#undef CHECK_TYPE_IS_NOT_VALUETYPE
QTEST_MAIN(tst_qqmlvaluetypes)
diff --git a/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp b/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp
index 4b2ae45bae..ae99e35467 100644
--- a/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp
+++ b/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp
@@ -75,6 +75,7 @@ private slots:
void introspectQrc();
void sortCaseSensitive_data();
void sortCaseSensitive();
+ void updateProperties();
private:
void checkNoErrors(const QQmlComponent& component);
QQmlEngine engine;
@@ -112,6 +113,10 @@ void tst_qquickfolderlistmodel::initTestCase()
void tst_qquickfolderlistmodel::basicProperties()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("[QTBUG-77335] Initial folder of FolderListModel on Android does not work properly,"
+ " and from there on it is unreliable to change the folder");
+#endif
QQmlComponent component(&engine, testFileUrl("basic.qml"));
checkNoErrors(component);
@@ -356,6 +361,9 @@ void tst_qquickfolderlistmodel::showDotAndDotDot()
void tst_qquickfolderlistmodel::showDotAndDotDot_data()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("Resource file system does not list '.' and '..' due to QDir::entryList() behavior");
+#endif
QTest::addColumn<QUrl>("folder");
QTest::addColumn<QUrl>("rootFolder");
QTest::addColumn<bool>("showDotAndDotDot");
@@ -411,13 +419,54 @@ void tst_qquickfolderlistmodel::sortCaseSensitive()
QAbstractListModel *flm = qobject_cast<QAbstractListModel*>(component.create());
QVERIFY(flm != 0);
- flm->setProperty("folder", QUrl::fromLocalFile(dataDirectoryUrl().path() + QLatin1String("/sortdir")));
+ flm->setProperty("folder", testFileUrl("sortdir"));
flm->setProperty("sortCaseSensitive", sortCaseSensitive);
QTRY_COMPARE(flm->property("count").toInt(), 2); // wait for refresh
for (int i = 0; i < 2; ++i)
QTRY_COMPARE(flm->data(flm->index(i),FileNameRole).toString(), expectedOrder.at(i));
}
+void tst_qquickfolderlistmodel::updateProperties()
+{
+ QQmlComponent component(&engine, testFileUrl("basic.qml"));
+ checkNoErrors(component);
+
+ QObject *folderListModel = component.create();
+ QVERIFY(folderListModel);
+
+ QVariant caseSensitive = folderListModel->property("caseSensitive");
+ QVERIFY(caseSensitive.isValid());
+ QCOMPARE(caseSensitive.toBool(), true);
+ folderListModel->setProperty("caseSensitive", false);
+ caseSensitive = folderListModel->property("caseSensitive");
+ QVERIFY(caseSensitive.isValid());
+ QCOMPARE(caseSensitive.toBool(), false);
+
+ QVariant showOnlyReadable = folderListModel->property("showOnlyReadable");
+ QVERIFY(showOnlyReadable.isValid());
+ QCOMPARE(showOnlyReadable.toBool(), false);
+ folderListModel->setProperty("showOnlyReadable", true);
+ showOnlyReadable = folderListModel->property("showOnlyReadable");
+ QVERIFY(showOnlyReadable.isValid());
+ QCOMPARE(showOnlyReadable.toBool(), true);
+
+ QVariant showDotAndDotDot = folderListModel->property("showDotAndDotDot");
+ QVERIFY(showDotAndDotDot.isValid());
+ QCOMPARE(showDotAndDotDot.toBool(), false);
+ folderListModel->setProperty("showDotAndDotDot", true);
+ showDotAndDotDot = folderListModel->property("showDotAndDotDot");
+ QVERIFY(showDotAndDotDot.isValid());
+ QCOMPARE(showDotAndDotDot.toBool(), true);
+
+ QVariant showHidden = folderListModel->property("showHidden");
+ QVERIFY(showHidden.isValid());
+ QCOMPARE(showHidden.toBool(), false);
+ folderListModel->setProperty("showHidden", true);
+ showHidden = folderListModel->property("showHidden");
+ QVERIFY(showHidden.isValid());
+ QCOMPARE(showHidden.toBool(), true);
+}
+
QTEST_MAIN(tst_qquickfolderlistmodel)
#include "tst_qquickfolderlistmodel.moc"
diff --git a/tests/auto/qml/qquickworkerscript/qquickworkerscript.pro b/tests/auto/qml/qquickworkerscript/qquickworkerscript.pro
index be8b9089a2..f58fd4543f 100644
--- a/tests/auto/qml/qquickworkerscript/qquickworkerscript.pro
+++ b/tests/auto/qml/qquickworkerscript/qquickworkerscript.pro
@@ -8,4 +8,4 @@ include (../../shared/util.pri)
TESTDATA = data/*
-QT += core-private gui-private qml-private testlib
+QT += core-private gui-private qml-private testlib qmlworkerscript-private
diff --git a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp
index 4ad58ba56c..bea9978f0b 100644
--- a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp
+++ b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp
@@ -30,6 +30,7 @@
#include <QtCore/qtimer.h>
#include <QtCore/qdir.h>
#include <QtCore/qfileinfo.h>
+#include <QtCore/qregularexpression.h>
#include <QtQml/qjsengine.h>
#include <QtQml/qqmlcomponent.h>
@@ -91,14 +92,14 @@ void tst_QQuickWorkerScript::source()
QCOMPARE(worker->source(), source);
QVERIFY(QMetaObject::invokeMethod(worker.data(), "testSend", Q_ARG(QVariant, value)));
waitForEchoMessage(worker.data());
- QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.data()).value<QVariant>(), qVariantFromValue(QString("Hello_World")));
+ QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.data()).value<QVariant>(), QVariant::fromValue(QString("Hello_World")));
source = testFileUrl("script_module.mjs");
worker->setSource(source);
QCOMPARE(worker->source(), source);
QVERIFY(QMetaObject::invokeMethod(worker.data(), "testSend", Q_ARG(QVariant, value)));
waitForEchoMessage(worker.data());
- QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.data()).value<QVariant>(), qVariantFromValue(QString("Hello from the module")));
+ QCOMPARE(mo->property(mo->indexOfProperty("response")).read(worker.data()).value<QVariant>(), QVariant::fromValue(QString("Hello from the module")));
qApp->processEvents();
}
@@ -118,7 +119,18 @@ void tst_QQuickWorkerScript::messaging()
QVariant response = mo->property(mo->indexOfProperty("response")).read(worker).value<QVariant>();
if (response.userType() == qMetaTypeId<QJSValue>())
response = response.value<QJSValue>().toVariant();
- QCOMPARE(response, value);
+
+ if (value.type() == QMetaType::QRegExp && response.type() == QMetaType::QRegularExpression) {
+ // toVariant() doesn't know if we want QRegExp or QRegularExpression. It always creates
+ // a QRegularExpression from a JavaScript regular expression.
+ const QRegularExpression responseRegExp = response.toRegularExpression();
+ const QRegExp valueRegExp = value.toRegExp();
+ QCOMPARE(responseRegExp.pattern(), valueRegExp.pattern());
+ QCOMPARE(bool(responseRegExp.patternOptions() & QRegularExpression::CaseInsensitiveOption),
+ bool(valueRegExp.caseSensitivity() == Qt::CaseInsensitive));
+ } else {
+ QCOMPARE(response, value);
+ }
qApp->processEvents();
delete worker;
@@ -129,16 +141,16 @@ void tst_QQuickWorkerScript::messaging_data()
QTest::addColumn<QVariant>("value");
QTest::newRow("invalid") << QVariant();
- QTest::newRow("bool") << qVariantFromValue(true);
- QTest::newRow("int") << qVariantFromValue(1001);
- QTest::newRow("real") << qVariantFromValue(10334.375);
- QTest::newRow("string") << qVariantFromValue(QString("More cheeeese, Gromit!"));
- QTest::newRow("variant list") << qVariantFromValue((QVariantList() << "a" << "b" << "c"));
- QTest::newRow("date time") << qVariantFromValue(QDateTime::currentDateTime());
-#ifndef QT_NO_REGEXP
- // Qt Script's QScriptValue -> QRegExp uses RegExp2 pattern syntax
- QTest::newRow("regexp") << qVariantFromValue(QRegExp("^\\d\\d?$", Qt::CaseInsensitive, QRegExp::RegExp2));
-#endif
+ QTest::newRow("bool") << QVariant::fromValue(true);
+ QTest::newRow("int") << QVariant::fromValue(1001);
+ QTest::newRow("real") << QVariant::fromValue(10334.375);
+ QTest::newRow("string") << QVariant::fromValue(QString("More cheeeese, Gromit!"));
+ QTest::newRow("variant list") << QVariant::fromValue((QVariantList() << "a" << "b" << "c"));
+ QTest::newRow("date time") << QVariant::fromValue(QDateTime::currentDateTime());
+ QTest::newRow("regexp") << QVariant::fromValue(QRegExp("^\\d\\d?$", Qt::CaseInsensitive,
+ QRegExp::RegExp2));
+ QTest::newRow("regularexpression") << QVariant::fromValue(QRegularExpression(
+ "^\\d\\d?$", QRegularExpression::CaseInsensitiveOption));
}
void tst_QQuickWorkerScript::messaging_sendQObjectList()
@@ -153,9 +165,9 @@ void tst_QQuickWorkerScript::messaging_sendQObjectList()
QVariantList objects;
for (int i=0; i<3; i++)
- objects << qVariantFromValue(new QObject(this));
+ objects << QVariant::fromValue(new QObject(this));
- QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, qVariantFromValue(objects))));
+ QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, QVariant::fromValue(objects))));
waitForEchoMessage(worker);
const QMetaObject *mo = worker->metaObject();
@@ -181,10 +193,10 @@ void tst_QQuickWorkerScript::messaging_sendJsObject()
map.insert("name", "zyz");
map.insert("spell power", 3101);
- QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, qVariantFromValue(map))));
+ QVERIFY(QMetaObject::invokeMethod(worker, "testSend", Q_ARG(QVariant, QVariant::fromValue(map))));
waitForEchoMessage(worker);
- QVariant result = qVariantFromValue(false);
+ QVariant result = QVariant::fromValue(false);
QVERIFY(QMetaObject::invokeMethod(worker, "compareLiteralResponse", Qt::DirectConnection,
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, jsObject)));
QVERIFY(result.toBool());
diff --git a/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp b/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp
index fd50ff5020..5dd8e9dcc0 100644
--- a/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp
+++ b/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp
@@ -59,7 +59,7 @@ void tst_QV4Assembler::initTestCase()
void tst_QV4Assembler::perfMapFile()
{
-#if !defined(Q_OS_LINUX)
+#if !defined(Q_OS_LINUX) || defined(Q_OS_ANDROID)
QSKIP("perf map files are only generated on linux");
#else
const QString qmljs = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmljs";
@@ -140,23 +140,17 @@ void tst_QV4Assembler::functionTable()
#endif
}
-#ifdef V4_ENABLE_JIT
-#define JIT_ENABLED 1
-#else
-#define JIT_ENABLED 0
-#endif
-
void tst_QV4Assembler::jitEnabled()
{
#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
/* JIT should be disabled on iOS and tvOS. */
- QCOMPARE(JIT_ENABLED, 0);
+ QVERIFY(!QT_CONFIG(qml_jit));
#elif defined(Q_OS_WIN) && defined(Q_PROCESSOR_ARM)
/* JIT should be disabled Windows on ARM/ARM64 for now. */
- QCOMPARE(JIT_ENABLED, 0);
+ QVERIFY(!QT_CONFIG(qml_jit));
#else
/* JIT should be enabled on all other architectures/OSes tested in CI. */
- QCOMPARE(JIT_ENABLED, 1);
+ QVERIFY(QT_CONFIG(qml_jit));
#endif
}
diff --git a/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp b/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp
index 308fba9049..157d0f2a62 100644
--- a/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp
+++ b/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp
@@ -1,5 +1,6 @@
/****************************************************************************
**
+** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2016 basysKom GmbH.
** Contact: https://www.qt.io/licensing/
**
@@ -110,8 +111,8 @@ void tst_qv4identifiertable::sweepCenterEntryInBucket()
table.asPropertyKey(entry2);
table.asPropertyKey(entry3);
- QCOMPARE(table.size, 3);
- QCOMPARE(table.alloc, 5);
+ QCOMPARE(table.size, 3u);
+ QCOMPARE(table.alloc, 5u);
QCOMPARE(table.entriesByHash[0], entry1);
QCOMPARE(table.entriesByHash[1], entry2);
@@ -153,8 +154,8 @@ void tst_qv4identifiertable::sweepLastEntryInBucket()
table.asPropertyKey(entry2);
table.asPropertyKey(entry3);
- QCOMPARE(table.size, 3);
- QCOMPARE(table.alloc, 5);
+ QCOMPARE(table.size, 3u);
+ QCOMPARE(table.alloc, 5u);
QCOMPARE(table.entriesByHash[0], entry1);
QCOMPARE(table.entriesByHash[1], entry2);
@@ -193,8 +194,8 @@ void tst_qv4identifiertable::sweepFirstEntryInSameBucketWithDifferingHash()
table.asPropertyKey(entry1);
table.asPropertyKey(entry2);
- QCOMPARE(table.size, 2);
- QCOMPARE(table.alloc, 5);
+ QCOMPARE(table.size, 2u);
+ QCOMPARE(table.alloc, 5u);
QCOMPARE(table.entriesByHash[0], entry1);
QCOMPARE(table.entriesByHash[1], entry2);
@@ -231,8 +232,8 @@ void tst_qv4identifiertable::dontSweepAcrossBucketBoundaries()
table.asPropertyKey(entry1);
table.asPropertyKey(entry2);
- QCOMPARE(table.size, 2);
- QCOMPARE(table.alloc, 5);
+ QCOMPARE(table.size, 2u);
+ QCOMPARE(table.alloc, 5u);
QCOMPARE(table.entriesByHash[0], entry1);
QCOMPARE(table.entriesByHash[1], entry2);
@@ -279,8 +280,8 @@ void tst_qv4identifiertable::sweepAcrossBucketBoundariesIfFirstBucketFull()
table.asPropertyKey(entry3);
table.asPropertyKey(entry4);
- QCOMPARE(table.size, 4);
- QCOMPARE(table.alloc, 11);
+ QCOMPARE(table.size, 4u);
+ QCOMPARE(table.alloc, 11u);
QCOMPARE(table.entriesByHash[0], entry1);
QCOMPARE(table.entriesByHash[1], entry2);
@@ -336,8 +337,8 @@ void tst_qv4identifiertable::sweepBucketGap()
table.asPropertyKey(entry3);
table.asPropertyKey(entry4);
- QCOMPARE(table.size, 4);
- QCOMPARE(table.alloc, 11);
+ QCOMPARE(table.size, 4u);
+ QCOMPARE(table.alloc, 11u);
QCOMPARE(table.entriesByHash[0], entry1);
QCOMPARE(table.entriesByHash[1], entry2);
diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
index 1e34b79954..5d635aa63b 100644
--- a/tests/auto/qml/qv4mm/tst_qv4mm.cpp
+++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
@@ -136,7 +136,7 @@ void tst_qv4mm::clearICParent()
// to change this test.
for (uint i = 0; i < 16 * 1024; ++i) {
QV4::Scope scope(&engine);
- QV4::ScopedString s(scope, identifiers->getIndexed(i));
+ QV4::ScopedString s(scope, identifiers->get(i));
QV4::Scoped<QV4::InternalClass> ic(scope, object->internalClass());
QVERIFY(ic->d()->parent != nullptr);
object->deleteProperty(s->toPropertyKey());
diff --git a/tests/auto/qml/v4traced/tst_v4traced.cpp b/tests/auto/qml/v4traced/tst_v4traced.cpp
deleted file mode 100644
index f82cc0ed5e..0000000000
--- a/tests/auto/qml/v4traced/tst_v4traced.cpp
+++ /dev/null
@@ -1,325 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite 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 <QtTest/QtTest>
-#include <QJSEngine>
-#include <private/qjsvalue_p.h>
-#include <private/qv4scopedvalue_p.h>
-#include <private/qv4script_p.h>
-
-class EnvVarSaver
-{
-public:
- EnvVarSaver(const char *name, const QByteArray &newValue)
- : _name(name)
- {
- _wasSet = qEnvironmentVariableIsSet(name);
- if (_wasSet)
- _oldValue = qgetenv(name);
- qputenv(name, newValue);
- }
-
- ~EnvVarSaver()
- {
- if (_wasSet)
- qputenv(_name, _oldValue);
- else
- qunsetenv(_name);
- }
-
-private:
- const char *_name;
- bool _wasSet;
- QByteArray _oldValue;
-};
-
-class tst_v4traced : public QObject
-{
- Q_OBJECT
-
-private slots:
- void collectTraces_data();
- void collectTraces();
-
- void binopI32deopt_data();
- void binopI32deopt();
-
- void calls_data();
- void calls();
-
- void setLookup();
- void construct();
-};
-
-void tst_v4traced::collectTraces_data()
-{
- QTest::addColumn<QString>("code");
- QTest::addColumn<int>("tracePointCount");
- QTest::addColumn<int>("interestingTracePoint");
- QTest::addColumn<quint8>("expectedBits");
-
- QTest::newRow("int+") << "var a = 4; a + 2" << 2 << 1 << quint8(QV4::ObservedTraceValues::Integer);
- QTest::newRow("double+") << "var a = 4.1; a + 1.9" << 2 << 1 << quint8(QV4::ObservedTraceValues::Double);
- QTest::newRow("object+") << "var a = '4'; a + '2'" << 2 << 1 << quint8(QV4::ObservedTraceValues::Other);
-}
-
-void tst_v4traced::collectTraces()
-{
- QFETCH(QString, code);
- QFETCH(int, tracePointCount);
- QFETCH(int, interestingTracePoint);
- QFETCH(quint8, expectedBits);
-
- EnvVarSaver forceInterpreter("QV4_FORCE_INTERPRETER", "1");
- EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1");
-
- QV4::ExecutionEngine vm;
- QV4::Scope scope(&vm);
- QV4::ScopedContext ctx(scope, vm.rootContext());
- QV4::ScopedValue result(scope);
- QScopedPointer<QV4::Script> script;
- script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "collectTraces"));
- script->parseAsBinding = false;
-
- QVERIFY(!scope.engine->hasException);
- script->parse();
- QVERIFY(!scope.engine->hasException);
-
- QVERIFY(script->function()->tracingEnabled());
- result = script->run();
- QVERIFY(!scope.engine->hasException);
-
- QCOMPARE(int(script->function()->compiledFunction->nTraceInfos), tracePointCount);
- QCOMPARE(*script->function()->traceInfo(interestingTracePoint), expectedBits);
-}
-
-void tst_v4traced::binopI32deopt_data()
-{
- QTest::addColumn<QString>("operand");
- QTest::addColumn<int>("int_arg1");
- QTest::addColumn<int>("int_arg2");
- QTest::addColumn<int>("int_result");
- QTest::addColumn<QString>("other_arg1");
- QTest::addColumn<QString>("other_arg2");
- QTest::addColumn<double>("other_result");
-
- QTest::newRow("+") << "+" << 1 << 2 << 3 << "1.1" << "1.9" << 3.0;
- QTest::newRow("-") << "-" << 3 << 2 << 1 << "3.1" << "2.1" << 1.0;
- QTest::newRow("*") << "*" << 2 << 3 << 6 << "2.1" << "1.9" << 3.99;
- QTest::newRow("/") << "/" << 6 << 3 << 2 << "6.6" << "3.3" << 2.0;
-
- QTest::newRow("&") << "&" << 6 << 3 << 2 << "'6'" << "'3'" << 2.0;
- QTest::newRow("|") << "|" << 6 << 3 << 7 << "'6'" << "'3'" << 7.0;
- QTest::newRow("^") << "^" << 6 << 3 << 5 << "'6'" << "'3'" << 5.0;
-
- QTest::newRow("<<") << "<<" << 5 << 1 << 10 << "'5'" << "'1'" << 10.0;
- QTest::newRow(">>") << ">>" << -1 << 1 << -1 << "'-1'" << "'1'" << -1.0;
- QTest::newRow(">>>") << ">>>" << -1 << 1 << 0x7FFFFFFF << "'-1'" << "'1'" << 2147483647.0;
-
- QTest::newRow("==") << "==" << 2 << 1 << 0 << "'2'" << "'1'" << 0.0;
- QTest::newRow("!=") << "!=" << 2 << 1 << 1 << "'2'" << "'1'" << 1.0;
- QTest::newRow("<" ) << "<" << 2 << 1 << 0 << "'2'" << "'1'" << 0.0;
- QTest::newRow("<=") << "<=" << 2 << 1 << 0 << "'2'" << "'1'" << 0.0;
- QTest::newRow(">" ) << ">" << 2 << 1 << 1 << "'2'" << "'1'" << 1.0;
- QTest::newRow(">=") << ">=" << 2 << 1 << 1 << "'2'" << "'1'" << 1.0;
-}
-
-void tst_v4traced::binopI32deopt()
-{
- QFETCH(QString, operand);
- QFETCH(int, int_arg1);
- QFETCH(int, int_arg2);
- QFETCH(int, int_result);
- QFETCH(QString, other_arg1);
- QFETCH(QString, other_arg2);
- QFETCH(double, other_result);
-
- QString func = QStringLiteral("function binopI32(a, b) { return a %1 b }").arg(operand);
- QString intCall = QStringLiteral("binopI32(%1, %2)").arg(int_arg1).arg(int_arg2);
- QString otherCall = QStringLiteral("binopI32(%1, %2)").arg(other_arg1).arg(other_arg2);
-
- QJSEngine engine;
- engine.evaluate(func);
-
- QCOMPARE(engine.evaluate(intCall).toInt(), int_result); // interpret + trace
- QCOMPARE(engine.evaluate(intCall).toInt(), int_result); // jit
- QCOMPARE(engine.evaluate(otherCall).toNumber(), other_result); // deopt
- QCOMPARE(engine.evaluate(otherCall).toNumber(), other_result); // retrace
- QCOMPARE(engine.evaluate(otherCall).toNumber(), other_result); // rejit
-}
-
-void tst_v4traced::calls_data()
-{
- QTest::addColumn<QString>("call");
-
- QTest::newRow("callGlobalLookup") << "globalLookup";
- QTest::newRow("callPropertyLookup") << "obj.propertyLookup";
-}
-
-class Calls
-{
-public:
- static int callCount;
-
- static QV4::ReturnedValue doSomething(const QV4::FunctionObject */*o*/,
- const QV4::Value */*thiz*/,
- const QV4::Value *argv, int argc)
- {
- ++callCount;
-
- if (argc == 0)
- return QV4::Encode(42);
-
- int prod = 1;
- for (int i = 0; i < argc; ++i) {
- Q_ASSERT(argv[i].isInteger());
- prod *= argv[i].int_32();
- }
- return QV4::Encode(prod);
- }
-};
-
-int Calls::callCount = 0;
-
-void tst_v4traced::calls()
-{
- QFETCH(QString, call);
-
- EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1");
- EnvVarSaver jitCallThreshold("QV4_JIT_CALL_THRESHOLD", "1");
-
- QV4::ExecutionEngine vm;
- QV4::Scope scope(&vm);
- QV4::ScopedContext ctx(scope, vm.rootContext());
- vm.globalObject->defineDefaultProperty(QLatin1String("globalLookup"),
- Calls::doSomething);
- QV4::ScopedObject obj(scope, vm.newObject());
- vm.globalObject->defineDefaultProperty(QLatin1String("obj"), obj);
- obj->defineDefaultProperty("propertyLookup", Calls::doSomething);
-
- QString code = QStringLiteral(
- "function doCalls() {\n"
- " if (%1() != 42) return false\n"
- " if (%1(21, 2) != 42) return false\n"
- " if (%1(2, 3, 7) != 42) return false\n"
- " return true\n"
- "}\n"
- "var result = true\n"
- "for (var i = 0; i < 10; ++i) {\n"
- " if (!doCalls()) { result = false; break }"
- "}\n"
- "result\n").arg(call);
- QScopedPointer<QV4::Script> script;
- script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "call"));
- script->parseAsBinding = false;
-
- QVERIFY(!scope.engine->hasException);
- script->parse();
- QVERIFY(!scope.engine->hasException);
-
- Calls::callCount = 0;
- QV4::ScopedValue result(scope, script->run());
- QVERIFY(!scope.engine->hasException);
-
- QVERIFY(result->isBoolean());
- QVERIFY(result->booleanValue());
- QCOMPARE(Calls::callCount, 30);
-}
-
-void tst_v4traced::setLookup()
-{
- EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1");
- EnvVarSaver jitCallThreshold("QV4_JIT_CALL_THRESHOLD", "1");
-
- QV4::ExecutionEngine vm;
- QV4::Scope scope(&vm);
- QV4::ScopedContext ctx(scope, vm.rootContext());
- QV4::ScopedObject obj(scope, vm.newObject());
- vm.globalObject->defineDefaultProperty(QLatin1String("oracle"), obj);
- obj->defineDefaultProperty("answer", QV4::Primitive::fromInt32(32));
-
- QString code = QStringLiteral(
- "function doit() {\n"
- " ++oracle.answer\n"
- "}\n"
- "for (var i = 0; i < 10; ++i) doit()\n"
- "oracle.answer\n");
- QScopedPointer<QV4::Script> script;
- script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "setLookup"));
- script->parseAsBinding = false;
-
- QVERIFY(!scope.engine->hasException);
- script->parse();
- QVERIFY(!scope.engine->hasException);
-
- QV4::ScopedValue result(scope, script->run());
- QVERIFY(!scope.engine->hasException);
-
- QVERIFY(result->isInteger());
- QCOMPARE(result->int_32(), 42);
-}
-
-void tst_v4traced::construct()
-{
- EnvVarSaver forceTracing("QV4_FORCE_TRACING", "1");
- EnvVarSaver jitCallThreshold("QV4_JIT_CALL_THRESHOLD", "1");
-
- QV4::ExecutionEngine vm;
- QV4::Scope scope(&vm);
- QV4::ScopedContext ctx(scope, vm.rootContext());
- QV4::ScopedObject obj(scope, vm.newObject());
- vm.globalObject->defineDefaultProperty(QLatin1String("oracle"), obj);
- obj->defineDefaultProperty("answer", QV4::Primitive::fromInt32(32));
-
- QString code = QStringLiteral(
- "function doit() {\n"
- " this.arr = new Array()\n"
- " this.arr[0] = 0\n"
- " this.arr[1] = 1\n"
- " this.arr[2] = 2\n"
- "}\n"
- "var o\n"
- "for (var i = 0; i < 10; ++i) o = new doit()\n"
- "o.arr\n");
- QScopedPointer<QV4::Script> script;
- script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, "setLookup"));
- script->parseAsBinding = false;
-
- QVERIFY(!scope.engine->hasException);
- script->parse();
- QVERIFY(!scope.engine->hasException);
-
- QV4::ScopedValue result(scope, script->run());
- QVERIFY(!scope.engine->hasException);
-
- QVERIFY(result->as<QV4::ArrayObject>());
-}
-
-QTEST_MAIN(tst_v4traced)
-
-#include "tst_v4traced.moc"
diff --git a/tests/auto/qml/v4traced/v4traced.pro b/tests/auto/qml/v4traced/v4traced.pro
deleted file mode 100644
index cbc8f38046..0000000000
--- a/tests/auto/qml/v4traced/v4traced.pro
+++ /dev/null
@@ -1,5 +0,0 @@
-CONFIG += testcase
-TARGET = tst_v4traced
-macos:CONFIG -= app_bundle
-QT += core-private qml-private testlib
-SOURCES += tst_v4traced.cpp
diff --git a/tests/auto/qmltest/statemachine/tst_parallelmachine.qml b/tests/auto/qmltest/statemachine/tst_parallelmachine.qml
index be7d73fbe5..eb996c7718 100644
--- a/tests/auto/qmltest/statemachine/tst_parallelmachine.qml
+++ b/tests/auto/qmltest/statemachine/tst_parallelmachine.qml
@@ -32,25 +32,29 @@ import QtQml.StateMachine 1.0
TestCase {
StateMachine {
id: myStateMachine
- childMode: State.ParallelStates
+ initialState: rootState
State {
- id: childState1
+ id: rootState
childMode: State.ParallelStates
State {
- id: childState11
+ id: childState1
+ childMode: State.ParallelStates
+ State {
+ id: childState11
+ }
+ State {
+ id: childState12
+ }
}
State {
- id: childState12
- }
- }
- State {
- id: childState2
- initialState: childState21
- State {
- id: childState21
- }
- State {
- id: childState22
+ id: childState2
+ initialState: childState21
+ State {
+ id: childState21
+ }
+ State {
+ id: childState22
+ }
}
}
}
diff --git a/tests/auto/quick/drawingmodes/tst_drawingmodes.cpp b/tests/auto/quick/drawingmodes/tst_drawingmodes.cpp
index afc66948b0..9bcc21c77d 100644
--- a/tests/auto/quick/drawingmodes/tst_drawingmodes.cpp
+++ b/tests/auto/quick/drawingmodes/tst_drawingmodes.cpp
@@ -69,6 +69,9 @@ private slots:
void triangles();
void triangleStrip();
void triangleFan();
+
+private:
+ bool isRunningOnRhi() const;
};
class DrawingModeItem : public QQuickItem
@@ -260,6 +263,9 @@ void tst_drawingmodes::lineLoop()
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms");
+ if (isRunningOnRhi())
+ QSKIP("Line loops are not supported by some modern graphics APIs - skipping test");
+
QImage fb = runTest("DrawingModes.qml");
QCOMPARE(fb.width(), 200);
@@ -350,6 +356,9 @@ void tst_drawingmodes::triangleFan()
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms");
+ if (isRunningOnRhi())
+ QSKIP("Triangle fans are not supported by some modern graphics APIs - skipping test");
+
QImage fb = runTest("DrawingModes.qml");
QCOMPARE(fb.width(), 200);
@@ -368,6 +377,23 @@ void tst_drawingmodes::triangleFan()
QVERIFY(!hasPixelAround(fb, 37, 100));
}
+bool tst_drawingmodes::isRunningOnRhi() const
+{
+ static bool retval = false;
+ static bool decided = false;
+ if (!decided) {
+ decided = true;
+ QQuickView dummy;
+ dummy.show();
+ if (QTest::qWaitForWindowExposed(&dummy)) {
+ QSGRendererInterface::GraphicsApi api = dummy.rendererInterface()->graphicsApi();
+ retval = QSGRendererInterface::isApiRhiBased(api);
+ }
+ dummy.hide();
+ }
+ return retval;
+}
+
QTEST_MAIN(tst_drawingmodes)
diff --git a/tests/auto/quick/examples/tst_examples.cpp b/tests/auto/quick/examples/tst_examples.cpp
index 9b3fa8fd2c..fdefa855e4 100644
--- a/tests/auto/quick/examples/tst_examples.cpp
+++ b/tests/auto/quick/examples/tst_examples.cpp
@@ -74,6 +74,7 @@ tst_examples::tst_examples()
{
// Add files to exclude here
excludedFiles << "snippets/qml/listmodel/listmodel.qml"; //Just a ListModel, no root QQuickItem
+ excludedFiles << "snippets/qml/tablemodel/fruit-example-delegatechooser.qml"; // Requires QtQuick.Controls import.
// Add directories you want excluded here
excludedDirs << "shared"; //Not an example
diff --git a/tests/auto/quick/nodes/tst_nodestest.cpp b/tests/auto/quick/nodes/tst_nodestest.cpp
index 79a9e5f757..bd5e6c6383 100644
--- a/tests/auto/quick/nodes/tst_nodestest.cpp
+++ b/tests/auto/quick/nodes/tst_nodestest.cpp
@@ -39,7 +39,7 @@
#include <QtQuick/qsgsimplerectnode.h>
#include <QtQuick/qsgsimpletexturenode.h>
-#include <QtQuick/private/qsgtexture_p.h>
+#include <QtQuick/private/qsgplaintexture_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformintegration.h>
@@ -99,7 +99,10 @@ void NodesTest::initTestCase()
auto rc = renderLoop->createRenderContext(renderLoop->sceneGraphContext());
renderContext = static_cast<QSGDefaultRenderContext *>(rc);
QVERIFY(renderContext);
- renderContext->initialize(context);
+ QSGDefaultRenderContext::InitParams rcParams;
+ rcParams.openGLContext = context;
+ rcParams.initialSurfacePixelSize = QSize(512, 512); // dummy, make up something
+ renderContext->initialize(&rcParams);
QVERIFY(renderContext->isValid());
}
diff --git a/tests/auto/quick/nokeywords/tst_nokeywords.cpp b/tests/auto/quick/nokeywords/tst_nokeywords.cpp
index e6655589a3..c0b66f1c3f 100644
--- a/tests/auto/quick/nokeywords/tst_nokeywords.cpp
+++ b/tests/auto/quick/nokeywords/tst_nokeywords.cpp
@@ -49,7 +49,7 @@
#include <QtQuick/private/qsgcontext_p.h>
#include <QtQuick/private/qsgcontextplugin_p.h>
#if QT_CONFIG(opengl)
-#include <QtQuick/private/qsgdefaultdistancefieldglyphcache_p.h>
+#include <QtQuick/private/qsgopengldistancefieldglyphcache_p.h>
#include <QtQuick/private/qsgdefaultglyphnode_p.h>
#include <QtQuick/private/qsgdefaultinternalimagenode_p.h>
#include <QtQuick/private/qsgdefaultinternalrectanglenode_p.h>
@@ -65,9 +65,16 @@
#include <QtQuick/private/qsgrendernode_p.h>
#include <QtQuick/private/qsgtexturematerial_p.h>
#include <QtQuick/private/qsgtexture_p.h>
+#include <QtQuick/private/qsgplaintexture_p.h>
#include <QtQuick/private/qsgthreadedrenderloop_p.h>
#include <QtQuick/private/qsgwindowsrenderloop_p.h>
+#include <QtQuick/private/qsgrhiatlastexture_p.h>
+#include <QtQuick/private/qsgrhidistancefieldglyphcache_p.h>
+#include <QtQuick/private/qsgrhilayer_p.h>
+#include <QtQuick/private/qsgrhishadereffectnode_p.h>
+#include <QtQuick/private/qsgrhitextureglyphcache_p.h>
+
#undef signals
#undef slots
#undef emit
diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp
index cd18580ccf..35fed99e8b 100644
--- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp
+++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp
@@ -137,7 +137,6 @@ void tst_MptaInterop::touchDrag()
// TODO touchesThenPinch_data with press/release sequences somehow: vectors of touchpoint IDs? or a string representation?
void tst_MptaInterop::touchesThenPinch()
{
- const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
QScopedPointer<QQuickView> windowPtr;
createView(windowPtr, "pinchDragMPTA.qml");
QQuickView * window = windowPtr.data();
diff --git a/tests/auto/quick/pointerhandlers/pointerhandlers.pro b/tests/auto/quick/pointerhandlers/pointerhandlers.pro
index 950d6835eb..4d6311bdb2 100644
--- a/tests/auto/quick/pointerhandlers/pointerhandlers.pro
+++ b/tests/auto/quick/pointerhandlers/pointerhandlers.pro
@@ -10,4 +10,5 @@ qtConfig(private_tests) {
qquickpointerhandler \
qquickpointhandler \
qquicktaphandler \
+ qquickwheelhandler \
}
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml
new file mode 100644
index 0000000000..d6eb791700
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+
+Item {
+ id: root
+ objectName: "snapMode"
+ width: 640
+ height: 480
+
+ Rectangle {
+ id: rect1
+ objectName: "rect1"
+ width: 90
+ height: 100
+ x: 100
+ y: 100
+ color: "teal"
+
+ Rectangle {
+ width: parent.width/2
+ height: parent.width/2
+ x: width/2
+ y: -x
+ color: dragHandler1.active ? "red" : "salmon"
+
+ DragHandler {
+ id: dragHandler1
+ objectName: "dragHandler1"
+ target: rect1
+ }
+ }
+ }
+
+
+ Rectangle {
+ id: rect2
+ objectName: "rect2"
+ width: 90
+ height: 100
+ x: 200
+ y: 100
+ color: "teal"
+
+ DragHandler {
+ id: dragHandler2
+ objectName: "dragHandler2"
+ target: rect2b
+ }
+
+ Rectangle {
+ id: rect2b
+ width: parent.width/2
+ height: parent.width/2
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: -width/2
+ color: dragHandler2.active ? "red" : "salmon"
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro b/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro
index 42c4e46c4f..6258fb9392 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro
@@ -19,3 +19,4 @@ OTHER_FILES += data/DragAnywhereSlider.qml \
data/grabberstate.qml \
data/multipleSliders.qml \
data/reparenting.qml \
+ data/snapMode.qml \
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
index fb0192893f..66314f88a2 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
@@ -57,6 +57,8 @@ private slots:
void mouseDrag_data();
void mouseDrag();
void dragFromMargin();
+ void snapMode_data();
+ void snapMode();
void touchDragMulti();
void touchDragMultiSliders_data();
void touchDragMultiSliders();
@@ -312,6 +314,78 @@ void tst_DragHandler::dragFromMargin() // QTBUG-74966
QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton);
}
+void tst_DragHandler::snapMode_data()
+{
+ const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+ QTest::addColumn<QString>("subTree");
+ QTest::addColumn<int>("snapMode");
+ QTest::addColumn<QPoint>("startDragPos");
+ QTest::addColumn<QPoint>("dragMovement");
+ QTest::addColumn<QPoint>("expectedMovement");
+
+ struct TestEntry {
+ const char *desc;
+ const char *subTree;
+ QQuickDragHandler::SnapMode mode;
+ QPoint startDragPos;
+ QPoint dragMovement;
+ QPoint expectedMovement;
+ };
+
+ TestEntry testdata[] = {
+ {"outside the target", "rect1", QQuickDragHandler::SnapAuto, QPoint(45, -10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, 0)},
+ {"inside the target", "rect1", QQuickDragHandler::SnapAuto, QPoint(45, 10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, 0)},
+ {"outside the target", "rect1", QQuickDragHandler::SnapAlways, QPoint(45, -10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, -50-10)},
+ {"outside the target", "rect1", QQuickDragHandler::NoSnap, QPoint(45, -10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, 0)},
+ {"outside the target", "rect1", QQuickDragHandler::SnapIfPressedOutsideTarget, QPoint(45, -10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, -50-10)},
+ {"inside the target", "rect1", QQuickDragHandler::SnapIfPressedOutsideTarget, QPoint(45, 10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, 0)},
+ //targets y pos moves from -25 to (25 + dragThreshold*2) because of snapping to center:
+ {"outside target, should snap", "rect2", QQuickDragHandler::SnapAuto, QPoint(45, 50), QPoint(0, dragThreshold*2), QPoint(0, 25 + 25 + dragThreshold*2)},
+ {"inside target, shouldn't snap", "rect2", QQuickDragHandler::SnapAuto, QPoint(45, 10), QPoint(0, dragThreshold*2), QPoint(0, dragThreshold*2)}
+ };
+
+ for (const TestEntry& e : testdata) {
+ const QMetaEnum menum = QMetaEnum::fromType<QQuickDragHandler::SnapMode>();
+ const QString dataTag = QString::fromLatin1("%1, %2, %3").arg(e.subTree).arg(menum.valueToKey(e.mode)).arg(e.desc);
+ QTest::newRow(dataTag.toUtf8().constData()) << e.subTree << (int)e.mode
+ << e.startDragPos << e.dragMovement << e.expectedMovement;
+ }
+}
+
+void tst_DragHandler::snapMode()
+{
+ QFETCH(QString, subTree);
+ QFETCH(QPoint, startDragPos);
+ QFETCH(QPoint, dragMovement);
+ QFETCH(int, snapMode);
+ QFETCH(QPoint, expectedMovement);
+
+ QScopedPointer<QQuickView> windowPtr;
+ createView(windowPtr, "snapMode.qml");
+ QQuickView * window = windowPtr.data();
+
+ QQuickItem *rect1 = window->rootObject()->findChild<QQuickItem*>(subTree);
+ QVERIFY(rect1);
+ QQuickItem *rect1b = rect1->childItems().first();
+ QVERIFY(rect1b);
+ QQuickDragHandler *dragHandler1 = rect1->findChild<QQuickDragHandler*>();
+ QVERIFY(dragHandler1);
+ dragHandler1->setSnapMode((QQuickDragHandler::SnapMode)snapMode);
+ QQuickItem *dragTarget = dragHandler1->target();
+ QPointF oldTargetPos = dragTarget->position();
+
+ QPoint p1 = rect1->mapToScene(QPointF(startDragPos)).toPoint();
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1);
+ QVERIFY(!dragHandler1->active());
+ p1 += dragMovement;
+ QTest::mouseMove(window, p1);
+ QTRY_VERIFY(dragHandler1->active());
+ QCOMPARE(dragTarget->position(), oldTargetPos + expectedMovement);
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1);
+ QTRY_VERIFY(!dragHandler1->active());
+ QCOMPARE(dragHandler1->centroid().pressedButtons(), Qt::NoButton);
+}
+
void tst_DragHandler::touchDragMulti()
{
const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml
new file mode 100644
index 0000000000..49e44f2b1f
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+
+Rectangle {
+ width: 320; height: 240
+ color: "lightsteelblue"; antialiasing: true
+ border.color: outerWheelHandler.active ? "red" : "white"
+
+ WheelHandler {
+ id: outerWheelHandler
+ objectName: "outerWheelHandler"
+ property: "x"
+ }
+
+ Rectangle {
+ width: 120; height: 120; x: 100; y: 60
+ color: "beige"; antialiasing: true
+ border.color: innerWheelHandler.active ? "red" : "white"
+
+ WheelHandler {
+ id: innerWheelHandler
+ objectName: "innerWheelHandler"
+ // TODO should ideally deactivate because events go to the outer handler, not because of timeout
+ activeTimeout: 0.5
+ property: "x"
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml
new file mode 100644
index 0000000000..d4875d5313
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+
+Rectangle {
+ width: 320; height: 240
+ color: "green"; antialiasing: true
+
+ Rectangle {
+ width: 100; height: 2; anchors.centerIn: parent
+ Rectangle {
+ width: 2; height: 100; anchors.centerIn: parent
+ }
+ }
+
+ WheelHandler {
+ activeTimeout: 0.5
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/qquickwheelhandler.pro b/tests/auto/quick/pointerhandlers/qquickwheelhandler/qquickwheelhandler.pro
new file mode 100644
index 0000000000..7509e38dd3
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/qquickwheelhandler.pro
@@ -0,0 +1,14 @@
+CONFIG += testcase
+TARGET = tst_qquickwheelhandler
+macos:CONFIG -= app_bundle
+
+SOURCES += tst_qquickwheelhandler.cpp
+OTHER_FILES = \
+ data/rectWheel.qml \
+
+include (../../../shared/util.pri)
+include (../../shared/util.pri)
+
+TESTDATA = data/*
+
+QT += core-private gui-private qml-private quick-private testlib
diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp b/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp
new file mode 100644
index 0000000000..2abf2ea8c3
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp
@@ -0,0 +1,344 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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 <QtTest/QtTest>
+#include <QtTest/QSignalSpy>
+#include <QtGui/QStyleHints>
+#include <qpa/qwindowsysteminterface.h>
+#include <private/qquickwheelhandler_p.h>
+#include <QtQuick/private/qquickrectangle_p.h>
+#include <QtQuick/qquickview.h>
+#include <QtQml/qqmlcontext.h>
+#include "../../../shared/util.h"
+#include "../../shared/viewtestutil.h"
+
+Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests")
+
+class tst_QQuickWheelHandler: public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_QQuickWheelHandler() { }
+
+private slots:
+ void singleHandler_data();
+ void singleHandler();
+ void nestedHandler_data();
+ void nestedHandler();
+
+private:
+ void sendWheelEvent(QQuickView &window, QPoint pos, QPoint angleDelta,
+ QPoint pixelDelta = QPoint(), Qt::KeyboardModifiers modifiers = Qt::NoModifier,
+ Qt::ScrollPhase phase = Qt::NoScrollPhase, bool inverted = false);
+};
+
+void tst_QQuickWheelHandler::sendWheelEvent(QQuickView &window, QPoint pos, QPoint angleDelta,
+ QPoint pixelDelta, Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase, bool inverted)
+{
+ QWheelEvent wheelEvent(pos, window.mapToGlobal(pos), pixelDelta, angleDelta,
+ Qt::NoButton, modifiers, phase, inverted);
+ QGuiApplication::sendEvent(&window, &wheelEvent);
+ qApp->processEvents();
+}
+
+void tst_QQuickWheelHandler::singleHandler_data()
+{
+ // handler properties
+ QTest::addColumn<Qt::Orientation>("orientation");
+ QTest::addColumn<bool>("invertible");
+ QTest::addColumn<int>("rotationScale");
+ QTest::addColumn<QString>("property");
+ QTest::addColumn<qreal>("targetScaleMultiplier");
+ QTest::addColumn<bool>("targetTransformAroundCursor");
+ // event
+ QTest::addColumn<QPoint>("eventPos");
+ QTest::addColumn<QPoint>("eventAngleDelta");
+ QTest::addColumn<QPoint>("eventPixelDelta");
+ QTest::addColumn<Qt::KeyboardModifiers>("eventModifiers");
+ QTest::addColumn<bool>("eventPhases");
+ QTest::addColumn<bool>("eventInverted");
+ // result
+ QTest::addColumn<QPoint>("expectedPosition");
+ QTest::addColumn<qreal>("expectedScale");
+ QTest::addColumn<int>("expectedRotation");
+
+ // move the item
+ QTest::newRow("vertical wheel angle delta to adjust x")
+ << Qt::Vertical << false << 1 << "x" << 1.5 << true
+ << QPoint(160, 120) << QPoint(-360, 120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false
+ << QPoint(15, 0) << 1.0 << 0;
+ QTest::newRow("horizontal wheel angle delta to adjust y")
+ << Qt::Horizontal << false << 1 << "y" << 1.5 << false
+ << QPoint(160, 120) << QPoint(-360, 120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false
+ << QPoint(0, -45) << 1.0 << 0;
+ QTest::newRow("vertical wheel angle delta to adjust y, amplified and inverted")
+ << Qt::Vertical << true << 4 << "y" << 1.5 << true
+ << QPoint(160, 120) << QPoint(60, 60) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << true
+ << QPoint(0, 30) << 1.0 << 0;
+ QTest::newRow("horizontal wheel angle delta to adjust x, amplified and reversed")
+ << Qt::Horizontal << false << -4 << "x" << 1.5 << false
+ << QPoint(160, 120) << QPoint(60, 60) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false
+ << QPoint(-30, 0) << 1.0 << 0;
+ QTest::newRow("vertical wheel pixel delta to adjust x")
+ << Qt::Vertical << false << 1 << "x" << 1.5 << true
+ << QPoint(160, 120) << QPoint(-360, 120) << QPoint(20, 20) << Qt::KeyboardModifiers(Qt::NoModifier) << true << false
+ << QPoint(20, 0) << 1.0 << 0;
+ QTest::newRow("horizontal wheel pixel delta to adjust y")
+ << Qt::Horizontal << false << 1 << "y" << 1.5 << false
+ << QPoint(160, 120) << QPoint(-360, 120) << QPoint(20, 20) << Qt::KeyboardModifiers(Qt::NoModifier) << true << false
+ << QPoint(0, 20) << 1.0 << 0;
+ QTest::newRow("vertical wheel pixel delta to adjust y, amplified and inverted")
+ << Qt::Vertical << true << 4 << "y" << 1.5 << true
+ << QPoint(160, 120) << QPoint(60, 60) << QPoint(20, 20) << Qt::KeyboardModifiers(Qt::NoModifier) << true << true
+ << QPoint(0, 80) << 1.0 << 0;
+ QTest::newRow("horizontal wheel pixel delta to adjust x, amplified and reversed")
+ << Qt::Horizontal << false << -4 << "x" << 1.5 << false
+ << QPoint(160, 120) << QPoint(60, 60) << QPoint(20, 20) << Qt::KeyboardModifiers(Qt::NoModifier) << true << false
+ << QPoint(-80, 0) << 1.0 << 0;
+
+ // scale the item
+ QTest::newRow("vertical wheel angle delta to adjust scale")
+ << Qt::Vertical << false << 1 << "scale" << 1.5 << true
+ << QPoint(50, 32) << QPoint(360, 120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false
+ << QPoint(55, 44) << 1.5 << 0;
+ QTest::newRow("horizontal wheel angle delta to adjust scale, amplified and reversed, don't adjust position")
+ << Qt::Horizontal << false << -2 << "scale" << 1.5 << false
+ << QPoint(50, 32) << QPoint(-240, 360) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false
+ << QPoint(0, 0) << 5.0625 << 0;
+
+ // rotate the item
+ QTest::newRow("vertical wheel angle delta to adjust rotation")
+ << Qt::Vertical << false << 1 << "rotation" << 1.5 << true
+ << QPoint(50, 32) << QPoint(360, -120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false
+ << QPoint(19, -31) << 1.0 << -15;
+ QTest::newRow("horizontal wheel angle delta to adjust rotation, amplified and reversed, don't adjust position")
+ << Qt::Horizontal << false << -2 << "rotation" << 1.5 << false
+ << QPoint(80, 80) << QPoint(240, 360) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false
+ << QPoint(0, 0) << 1.0 << -60;
+}
+
+void tst_QQuickWheelHandler::singleHandler()
+{
+ // handler properties
+ QFETCH(Qt::Orientation, orientation);
+ QFETCH(bool, invertible);
+ QFETCH(int, rotationScale);
+ QFETCH(QString, property);
+ QFETCH(qreal, targetScaleMultiplier);
+ QFETCH(bool, targetTransformAroundCursor);
+ // event
+ QFETCH(QPoint, eventPos);
+ QFETCH(QPoint, eventAngleDelta);
+ QFETCH(QPoint, eventPixelDelta);
+ QFETCH(Qt::KeyboardModifiers, eventModifiers);
+ QFETCH(bool, eventPhases);
+ QFETCH(bool, eventInverted);
+ // result
+ QFETCH(QPoint, expectedPosition);
+ QFETCH(qreal, expectedScale);
+ QFETCH(int, expectedRotation);
+
+ QQuickView window;
+ QByteArray errorMessage;
+ QVERIFY2(QQuickTest::initView(window, testFileUrl("rectWheel.qml"), true, &errorMessage), errorMessage.constData());
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ QQuickItem *rect = window.rootObject();
+ QVERIFY(rect != nullptr);
+ QQuickWheelHandler *handler = rect->findChild<QQuickWheelHandler*>();
+ QVERIFY(handler != nullptr);
+ handler->setOrientation(orientation);
+ handler->setInvertible(invertible);
+ handler->setRotationScale(rotationScale);
+ handler->setProperty(property);
+ handler->setTargetScaleMultiplier(targetScaleMultiplier);
+ handler->setTargetTransformAroundCursor(targetTransformAroundCursor);
+ QSignalSpy activeChangedSpy(handler, SIGNAL(activeChanged()));
+
+ if (eventPhases) {
+ sendWheelEvent(window, eventPos, QPoint(), QPoint(), eventModifiers, Qt::ScrollBegin, eventInverted);
+ sendWheelEvent(window, eventPos, eventAngleDelta, eventPixelDelta, eventModifiers, Qt::ScrollUpdate, eventInverted);
+ } else {
+ sendWheelEvent(window, eventPos, eventAngleDelta, eventPixelDelta, eventModifiers, Qt::NoScrollPhase, eventInverted);
+ }
+ QCOMPARE(rect->position().toPoint(), expectedPosition);
+ QCOMPARE(activeChangedSpy.count(), 1);
+ QCOMPARE(handler->active(), true);
+ QCOMPARE(rect->scale(), expectedScale);
+ QCOMPARE(rect->rotation(), expectedRotation);
+ if (!eventPhases) {
+ QTRY_COMPARE(handler->active(), false);
+ QCOMPARE(activeChangedSpy.count(), 2);
+ }
+
+ // restore by rotating backwards
+ if (eventPhases) {
+ sendWheelEvent(window, eventPos, eventAngleDelta * -1, eventPixelDelta * -1, eventModifiers, Qt::ScrollUpdate, eventInverted);
+ sendWheelEvent(window, eventPos, QPoint(), QPoint(), eventModifiers, Qt::ScrollEnd, eventInverted);
+ } else {
+ sendWheelEvent(window, eventPos, eventAngleDelta * -1, eventPixelDelta * -1, eventModifiers, Qt::NoScrollPhase, eventInverted);
+ }
+ QCOMPARE(activeChangedSpy.count(), eventPhases ? 2 : 3);
+ QCOMPARE(handler->active(), !eventPhases);
+ QCOMPARE(rect->position().toPoint(), QPoint(0, 0));
+ QCOMPARE(rect->scale(), 1);
+ QCOMPARE(rect->rotation(), 0);
+}
+
+void tst_QQuickWheelHandler::nestedHandler_data()
+{
+ // handler properties
+ QTest::addColumn<Qt::Orientation>("orientation");
+ QTest::addColumn<bool>("invertible");
+ QTest::addColumn<int>("rotationScale");
+ QTest::addColumn<QString>("property");
+ QTest::addColumn<qreal>("targetScaleMultiplier");
+ QTest::addColumn<bool>("targetTransformAroundCursor");
+ // event
+ QTest::addColumn<QPoint>("eventPos");
+ QTest::addColumn<QPoint>("eventAngleDelta");
+ QTest::addColumn<QPoint>("eventPixelDelta");
+ QTest::addColumn<Qt::KeyboardModifiers>("eventModifiers");
+ QTest::addColumn<bool>("eventPhases");
+ QTest::addColumn<bool>("eventInverted");
+ QTest::addColumn<int>("eventCount");
+ // result: inner handler
+ QTest::addColumn<QPoint>("innerPosition");
+ QTest::addColumn<qreal>("innerScale");
+ QTest::addColumn<int>("innerRotation");
+ // result: outer handler
+ QTest::addColumn<QPoint>("outerPosition");
+ QTest::addColumn<qreal>("outerScale");
+ QTest::addColumn<int>("outerRotation");
+
+ // move the item
+ QTest::newRow("vertical wheel angle delta to adjust x")
+ << Qt::Vertical << false << 1 << "x" << 1.5 << true
+ << QPoint(160, 120) << QPoint(120, 120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false << 10
+ << QPoint(175,60) << 1.0 << 0
+ << QPoint(75, 0) << 1.0 << 0;
+ QTest::newRow("horizontal wheel pixel delta to adjust y")
+ << Qt::Horizontal << false << 1 << "y" << 1.5 << false
+ << QPoint(160, 120) << QPoint(120, 120) << QPoint(50, 50) << Qt::KeyboardModifiers(Qt::NoModifier) << true << false << 4
+ << QPoint(100, 160) << 1.0 << 0
+ << QPoint(0, 100) << 1.0 << 0;
+}
+
+void tst_QQuickWheelHandler::nestedHandler()
+{
+ // handler properties
+ QFETCH(Qt::Orientation, orientation);
+ QFETCH(bool, invertible);
+ QFETCH(int, rotationScale);
+ QFETCH(QString, property);
+ QFETCH(qreal, targetScaleMultiplier);
+ QFETCH(bool, targetTransformAroundCursor);
+ // event
+ QFETCH(QPoint, eventPos);
+ QFETCH(QPoint, eventAngleDelta);
+ QFETCH(QPoint, eventPixelDelta);
+ QFETCH(Qt::KeyboardModifiers, eventModifiers);
+ QFETCH(bool, eventPhases);
+ QFETCH(bool, eventInverted);
+ QFETCH(int, eventCount);
+ // result: inner handler
+ QFETCH(QPoint, innerPosition);
+ QFETCH(qreal, innerScale);
+ QFETCH(int, innerRotation);
+ // result: outer handler
+ QFETCH(QPoint, outerPosition);
+ QFETCH(qreal, outerScale);
+ QFETCH(int, outerRotation);
+
+ QQuickView window;
+ QByteArray errorMessage;
+ QVERIFY2(QQuickTest::initView(window, testFileUrl("nested.qml"), true, &errorMessage), errorMessage.constData());
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ QQuickItem *outerRect = window.rootObject();
+ QVERIFY(outerRect != nullptr);
+ QQuickWheelHandler *outerHandler = outerRect->findChild<QQuickWheelHandler*>("outerWheelHandler");
+ QVERIFY(outerHandler != nullptr);
+ QQuickWheelHandler *innerHandler = outerRect->findChild<QQuickWheelHandler*>("innerWheelHandler");
+ QVERIFY(innerHandler != nullptr);
+ QQuickItem *innerRect = innerHandler->parentItem();
+ QVERIFY(innerRect != nullptr);
+ innerHandler->setOrientation(orientation);
+ innerHandler->setInvertible(invertible);
+ innerHandler->setRotationScale(rotationScale);
+ innerHandler->setProperty(property);
+ innerHandler->setTargetScaleMultiplier(targetScaleMultiplier);
+ innerHandler->setTargetTransformAroundCursor(targetTransformAroundCursor);
+ outerHandler->setOrientation(orientation);
+ outerHandler->setInvertible(invertible);
+ outerHandler->setRotationScale(rotationScale);
+ outerHandler->setProperty(property);
+ outerHandler->setTargetScaleMultiplier(targetScaleMultiplier);
+ outerHandler->setTargetTransformAroundCursor(targetTransformAroundCursor);
+ QSignalSpy innerActiveChangedSpy(innerHandler, SIGNAL(activeChanged()));
+ QSignalSpy outerActiveChangedSpy(outerHandler, SIGNAL(activeChanged()));
+
+ if (eventPhases)
+ sendWheelEvent(window, eventPos, QPoint(), QPoint(), eventModifiers, Qt::ScrollBegin, eventInverted);
+ for (int i = 0; i < eventCount; ++i)
+ sendWheelEvent(window, eventPos, eventAngleDelta, eventPixelDelta, eventModifiers,
+ (eventPhases ? Qt::ScrollUpdate : Qt::NoScrollPhase), eventInverted);
+ QCOMPARE(innerRect->position().toPoint(), innerPosition);
+
+ /*
+ If outer is activated, maybe inner should be deactivated? But the event
+ doesn't get delivered to inner anymore, so it doesn't find out that
+ it's no longer getting events. It will get deactivated after the
+ timeout, just as if the user stopped scrolling.
+
+ This situation is similar to QTBUG-50199, but it's questionable whether
+ that was really so important. So far in Qt Quick, if you move the mouse
+ while wheel momentum continues, or if the item moves out from under the
+ mouse, a different item starts getting the events immediately. In
+ non-Qt applications on most OSes, that's quite normal.
+ */
+ // QCOMPARE(innerActiveChangedSpy.count(), 2);
+ // QCOMPARE(innerHandler->active(), false);
+ QCOMPARE(innerRect->scale(), innerScale);
+ QCOMPARE(innerRect->rotation(), innerRotation);
+ QCOMPARE(outerRect->position().toPoint(), outerPosition);
+ QCOMPARE(outerActiveChangedSpy.count(), 1);
+ QCOMPARE(outerHandler->active(), true);
+ QCOMPARE(outerRect->scale(), outerScale);
+ QCOMPARE(outerRect->rotation(), outerRotation);
+ if (!eventPhases) {
+ QTRY_COMPARE(outerHandler->active(), false);
+ QCOMPARE(outerActiveChangedSpy.count(), 2);
+ }
+}
+
+QTEST_MAIN(tst_QQuickWheelHandler)
+
+#include "tst_qquickwheelhandler.moc"
diff --git a/tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp b/tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp
index 34be4d98b4..ca348eea03 100644
--- a/tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp
+++ b/tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp
@@ -28,7 +28,6 @@
#include <qtest.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlengine.h>
-#include <QtQml/private/qhashedstring_p.h>
#include <QtQml/private/qqmlmetatype_p.h>
#include <QtCore/QDebug>
#include <QtCore/QHash>
@@ -121,7 +120,7 @@ void tst_PropertyRequirements::constantOrNotifyableFull()
}
static const QString messagePattern("\nProperty %1 neither CONSTANT nor NOTIFYable. Affected types:\n\t%2");
- QStringList occurrencesList = occurrences.toList();
+ QStringList occurrencesList = occurrences.values();
occurrencesList.sort();
messages.append(messagePattern.arg(it.key(), occurrencesList.join("\n\t")));
@@ -159,7 +158,8 @@ void tst_PropertyRequirements::testAllQmlTypes(TestDepth testDepth, FailuresByPr
testQmlType(testDepth, qmlType, failuresByProperty);
}
}
- seenTypes.unite(QSet<QString>::fromList(QQmlMetaType::qmlTypeNames()));
+ const auto &typeNameList = QQmlMetaType::qmlTypeNames();
+ seenTypes.unite(QSet<QString>(typeNameList.cbegin(), typeNameList.cend()));
}
}
diff --git a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp
index c5fdb6c1b9..d1f6d67aa1 100644
--- a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp
+++ b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp
@@ -338,7 +338,7 @@ void tst_QQuickAccessible::basicPropertiesTest()
QCOMPARE(text2->rect().y(), item->rect().y() + 40);
QCOMPARE(text2->role(), QAccessible::StaticText);
QCOMPARE(item->indexOfChild(text2), 1);
- QCOMPARE(text2->state().editable, 0);
+ QCOMPARE(text2->state().editable, 0u);
QCOMPARE(text2->state().readOnly, 1);
QCOMPARE(iface->indexOfChild(text2), -1);
diff --git a/tests/auto/quick/qquickanchors/tst_qquickanchors.cpp b/tests/auto/quick/qquickanchors/tst_qquickanchors.cpp
index 77fa1292c4..128a154492 100644
--- a/tests/auto/quick/qquickanchors/tst_qquickanchors.cpp
+++ b/tests/auto/quick/qquickanchors/tst_qquickanchors.cpp
@@ -364,7 +364,7 @@ void tst_qquickanchors::reset()
const QMetaObject *meta = itemPrivate->anchors()->metaObject();
QMetaProperty p = meta->property(meta->indexOfProperty(side.toUtf8().constData()));
- QVERIFY(p.write(itemPrivate->anchors(), qVariantFromValue(anchorLine)));
+ QVERIFY(p.write(itemPrivate->anchors(), QVariant::fromValue(anchorLine)));
QCOMPARE(itemPrivate->anchors()->usedAnchors().testFlag(anchor), true);
QVERIFY(p.reset(itemPrivate->anchors()));
@@ -423,7 +423,7 @@ void tst_qquickanchors::nullItem()
QMetaProperty p = meta->property(meta->indexOfProperty(side.toUtf8().constData()));
QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML Item: Cannot anchor to a null item.");
- QVERIFY(p.write(itemPrivate->anchors(), qVariantFromValue(anchor)));
+ QVERIFY(p.write(itemPrivate->anchors(), QVariant::fromValue(anchor)));
delete item;
}
diff --git a/tests/auto/quick/qquickanimatedimage/data/currentframe.qml b/tests/auto/quick/qquickanimatedimage/data/currentframe.qml
new file mode 100644
index 0000000000..b679da2a99
--- /dev/null
+++ b/tests/auto/quick/qquickanimatedimage/data/currentframe.qml
@@ -0,0 +1,13 @@
+import QtQuick 2.0
+
+AnimatedImage {
+ property int currentFrameChangeCount: 0
+ property int frameChangeCount: 0
+ source: "stickman.gif"
+ onCurrentFrameChanged: if (currentFrame > 0) ++currentFrameChangeCount;
+ onFrameChanged: if (currentFrame > 0) ++frameChangeCount;
+ function scriptedSetCurrentFrame(frame) {
+ currentFrame = frame;
+ }
+}
+
diff --git a/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp b/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp
index 8026bafb9e..31c3fb9946 100644
--- a/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp
+++ b/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp
@@ -27,6 +27,7 @@
****************************************************************************/
#include <qtest.h>
#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlexpression.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQuick/qquickview.h>
#include <QtQuick/private/qquickrectangle_p.h>
@@ -40,6 +41,23 @@
Q_DECLARE_METATYPE(QQuickImageBase::Status)
+template <typename T> static T evaluate(QObject *scope, const QString &expression)
+{
+ QQmlExpression expr(qmlContext(scope), scope, expression);
+ QVariant result = expr.evaluate();
+ if (expr.hasError())
+ qWarning() << expr.error().toString();
+ return result.value<T>();
+}
+
+template <> void evaluate<void>(QObject *scope, const QString &expression)
+{
+ QQmlExpression expr(qmlContext(scope), scope, expression);
+ expr.evaluate();
+ if (expr.hasError())
+ qWarning() << expr.error().toString();
+}
+
class tst_qquickanimatedimage : public QQmlDataTest
{
Q_OBJECT
@@ -68,6 +86,7 @@ private slots:
void playingAndPausedChanges();
void noCaching();
void sourceChangesOnFrameChanged();
+ void currentFrame();
};
void tst_qquickanimatedimage::cleanup()
@@ -618,6 +637,33 @@ void tst_qquickanimatedimage::sourceChangesOnFrameChanged()
qDeleteAll(images);
}
+void tst_qquickanimatedimage::currentFrame()
+{
+ QQuickView window;
+ window.setSource(testFileUrl("currentframe.qml"));
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage *>(window.rootObject());
+ QVERIFY(anim);
+ QSignalSpy frameChangedSpy(anim, SIGNAL(frameChanged()));
+ QSignalSpy currentFrameChangedSpy(anim, SIGNAL(currentFrameChanged()));
+
+ anim->setCurrentFrame(1);
+ QCOMPARE(anim->currentFrame(), 1);
+ QCOMPARE(frameChangedSpy.count(), 1);
+ QCOMPARE(currentFrameChangedSpy.count(), 1);
+ QCOMPARE(anim->property("currentFrameChangeCount"), 1);
+ QCOMPARE(anim->property("frameChangeCount"), 1);
+
+ evaluate<void>(anim, "scriptedSetCurrentFrame(2)");
+ QCOMPARE(anim->currentFrame(), 2);
+ QCOMPARE(frameChangedSpy.count(), 2);
+ QCOMPARE(currentFrameChangedSpy.count(), 2);
+ QCOMPARE(anim->property("currentFrameChangeCount"), 2);
+ QCOMPARE(anim->property("frameChangeCount"), 2);
+}
+
QTEST_MAIN(tst_qquickanimatedimage)
#include "tst_qquickanimatedimage.moc"
diff --git a/tests/auto/quick/qquickanimations/qquickanimations.pro b/tests/auto/quick/qquickanimations/qquickanimations.pro
index 1c5494a24a..94f694181d 100644
--- a/tests/auto/quick/qquickanimations/qquickanimations.pro
+++ b/tests/auto/quick/qquickanimations/qquickanimations.pro
@@ -8,7 +8,7 @@ macx:CONFIG -= app_bundle
TESTDATA = data/*
-QT += core-private gui-private qml-private quick-private testlib
+QT += core-private gui-private qml-private quick-private testlib qmlmodels-private
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
OTHER_FILES += \
diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
index 17dfa4a3d7..48f779a490 100644
--- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
+++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
@@ -30,7 +30,7 @@
#include <QtQml/qqmlcomponent.h>
#include <QtQuick/qquickview.h>
#include <QtQml/private/qqmltimer_p.h>
-#include <QtQml/private/qqmllistmodel_p.h>
+#include <QtQmlModels/private/qqmllistmodel_p.h>
#include <QtQml/private/qanimationgroupjob_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuick/private/qquickitemanimation_p.h>
diff --git a/tests/auto/quick/qquickbehaviors/data/delete.qml b/tests/auto/quick/qquickbehaviors/data/delete.qml
new file mode 100644
index 0000000000..1bf0267b84
--- /dev/null
+++ b/tests/auto/quick/qquickbehaviors/data/delete.qml
@@ -0,0 +1,37 @@
+import QtQuick 2.12
+
+Item {
+ visible: true
+ width: 640
+ height: 480
+
+ Component.onCompleted: {
+ myLoader.active = false
+ }
+
+ Loader {
+ id: myLoader
+
+ active: true
+ sourceComponent: Item {
+ width: 100
+ height: 100
+ id: myPopup
+
+ NumberAnimation {
+ id: anim
+ }
+
+ Rectangle {
+ color: "black"
+ Component.onCompleted: {
+ opacity = 20
+ }
+
+ Behavior on opacity {
+ animation: anim
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp b/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp
index 6367f327da..64e32dcdfd 100644
--- a/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp
+++ b/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp
@@ -74,6 +74,7 @@ private slots:
void aliasedProperty();
void innerBehaviorOverwritten();
void oneWay();
+ void safeToDelete();
};
void tst_qquickbehaviors::simpleBehavior()
@@ -647,6 +648,16 @@ void tst_qquickbehaviors::oneWay()
QCOMPARE(myAnimation->isRunning(), false);
}
+// QTBUG-76749
+void tst_qquickbehaviors::safeToDelete()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("delete.qml"));
+ QVERIFY(c.create());
+}
+
+
+
QTEST_MAIN(tst_qquickbehaviors)
#include "tst_qquickbehaviors.moc"
diff --git a/tests/auto/quick/qquickborderimage/data/multi.ico b/tests/auto/quick/qquickborderimage/data/multi.ico
new file mode 100644
index 0000000000..b748ceaa29
--- /dev/null
+++ b/tests/auto/quick/qquickborderimage/data/multi.ico
Binary files differ
diff --git a/tests/auto/quick/qquickborderimage/data/multiframe.qml b/tests/auto/quick/qquickborderimage/data/multiframe.qml
new file mode 100644
index 0000000000..8bd32da5a6
--- /dev/null
+++ b/tests/auto/quick/qquickborderimage/data/multiframe.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.14
+
+BorderImage {
+ source: "multi.ico"
+ border { left: 19; top: 19; right: 19; bottom: 19 }
+ width: 160; height: 160
+ horizontalTileMode: BorderImage.Stretch
+}
diff --git a/tests/auto/quick/qquickborderimage/data/multiframeAsync.qml b/tests/auto/quick/qquickborderimage/data/multiframeAsync.qml
new file mode 100644
index 0000000000..059e4becf3
--- /dev/null
+++ b/tests/auto/quick/qquickborderimage/data/multiframeAsync.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.14
+
+BorderImage {
+ source: "multi.ico"
+ asynchronous: true
+ border { left: 19; top: 19; right: 19; bottom: 19 }
+ width: 160; height: 160
+ horizontalTileMode: BorderImage.Stretch
+}
diff --git a/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp b/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp
index 9292e1886a..dc3a783600 100644
--- a/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp
+++ b/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp
@@ -46,6 +46,8 @@
#include "../../shared/util.h"
#include "../shared/visualtestutil.h"
+Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
+
Q_DECLARE_METATYPE(QQuickImageBase::Status)
class tst_qquickborderimage : public QQmlDataTest
@@ -79,6 +81,8 @@ private slots:
#if QT_CONFIG(opengl)
void borderImageMesh();
#endif
+ void multiFrame_data();
+ void multiFrame();
private:
QQmlEngine engine;
@@ -601,6 +605,67 @@ void tst_qquickborderimage::borderImageMesh()
qPrintable(errorMessage));
}
#endif
+
+void tst_qquickborderimage::multiFrame_data()
+{
+ QTest::addColumn<QString>("qmlfile");
+ QTest::addColumn<bool>("asynchronous");
+
+ QTest::addRow("default") << "multiframe.qml" << false;
+ QTest::addRow("async") << "multiframeAsync.qml" << true;
+}
+
+void tst_qquickborderimage::multiFrame()
+{
+ if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
+ || (QGuiApplication::platformName() == QLatin1String("minimal")))
+ QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms");
+
+ QFETCH(QString, qmlfile);
+ QFETCH(bool, asynchronous);
+ Q_UNUSED(asynchronous)
+
+ QQuickView view(testFileUrl(qmlfile));
+ QQuickBorderImage *image = qobject_cast<QQuickBorderImage*>(view.rootObject());
+ QVERIFY(image);
+ QSignalSpy countSpy(image, SIGNAL(frameCountChanged()));
+ QSignalSpy currentSpy(image, SIGNAL(currentFrameChanged()));
+ if (asynchronous) {
+ QCOMPARE(image->frameCount(), 0);
+ QTRY_COMPARE(image->frameCount(), 4);
+ QCOMPARE(countSpy.count(), 1);
+ } else {
+ QCOMPARE(image->frameCount(), 4);
+ }
+ QCOMPARE(image->currentFrame(), 0);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+ QCoreApplication::processEvents(); // Process all queued events
+
+ QImage contents = view.grabWindow();
+ if (contents.width() < 160)
+ QSKIP("Skipping due to grabWindow not functional");
+
+ // The middle of the first frame looks blue, approximately qRgba(0x43, 0x7e, 0xd6, 0xff)
+ QColor color = contents.pixelColor(60, 60);
+ qCDebug(lcTests) << "expected bluish color, got" << color;
+ QVERIFY(color.redF() < 0.75);
+ QVERIFY(color.greenF() < 0.75);
+ QVERIFY(color.blueF() > 0.75);
+
+ image->setCurrentFrame(1);
+ QTRY_COMPARE(image->status(), QQuickImageBase::Ready);
+ QCOMPARE(currentSpy.count(), 1);
+ QCOMPARE(image->currentFrame(), 1);
+ contents = view.grabWindow();
+ // The middle of the second frame looks green, approximately qRgba(0x3a, 0xd2, 0x31, 0xff)
+ color = contents.pixelColor(60, 60);
+ qCDebug(lcTests) << "expected greenish color, got" << color;
+ QVERIFY(color.redF() < 0.75);
+ QVERIFY(color.green() > 0.75);
+ QVERIFY(color.blueF() < 0.75);
+}
+
QTEST_MAIN(tst_qquickborderimage)
#include "tst_qquickborderimage.moc"
diff --git a/tests/auto/quick/qquickboundaryrule/data/dragHandler.qml b/tests/auto/quick/qquickboundaryrule/data/dragHandler.qml
new file mode 100644
index 0000000000..c66fd76ff1
--- /dev/null
+++ b/tests/auto/quick/qquickboundaryrule/data/dragHandler.qml
@@ -0,0 +1,23 @@
+import QtQuick 2.14
+import Qt.labs.animation 1.0
+
+Rectangle {
+ id: root
+ width: 240; height: 120
+ color: "green"
+
+ DragHandler {
+ id: dragHandler
+ yAxis.minimum: -1000
+ xAxis.minimum: -1000
+ onActiveChanged: if (!active) xbr.returnToBounds();
+ }
+
+ BoundaryRule on x {
+ id: xbr
+ minimum: -50
+ maximum: 100
+ minimumOvershoot: 40
+ maximumOvershoot: 40
+ }
+}
diff --git a/tests/auto/quick/qquickboundaryrule/qquickboundaryrule.pro b/tests/auto/quick/qquickboundaryrule/qquickboundaryrule.pro
new file mode 100644
index 0000000000..ef43f4526a
--- /dev/null
+++ b/tests/auto/quick/qquickboundaryrule/qquickboundaryrule.pro
@@ -0,0 +1,12 @@
+CONFIG += testcase
+TARGET = tst_qquickboundaryrule
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_qquickboundaryrule.cpp
+
+include (../../shared/util.pri)
+include (../shared/util.pri)
+
+TESTDATA = data/*
+
+QT += core-private gui-private qml-private quick-private testlib
diff --git a/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp b/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp
new file mode 100644
index 0000000000..44f1c9a2f9
--- /dev/null
+++ b/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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 <QtTest/QtTest>
+#include <qsignalspy.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQuick/qquickview.h>
+#include <QtQuick/private/qquickboundaryrule_p.h>
+#include <QtQuick/private/qquickdraghandler_p.h>
+#include "../../shared/util.h"
+#include "../shared/viewtestutil.h"
+
+class tst_qquickboundaryrule : public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_qquickboundaryrule() {}
+
+private slots:
+ void init() { qApp->processEvents(); } //work around animation timer bug (QTBUG-22865)
+ void dragHandler();
+};
+
+void tst_qquickboundaryrule::dragHandler()
+{
+ QQuickView window;
+ QByteArray errorMessage;
+ QVERIFY2(QQuickTest::initView(window, testFileUrl("dragHandler.qml"), true, &errorMessage), errorMessage.constData());
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+ QQuickItem *target = window.rootObject();
+ QVERIFY(target);
+ QQuickDragHandler *dragHandler = target->findChild<QQuickDragHandler*>();
+ QVERIFY(dragHandler);
+ QQuickBoundaryRule *boundaryRule = target->findChild<QQuickBoundaryRule*>();
+ QVERIFY(boundaryRule);
+ QSignalSpy overshootChangedSpy(boundaryRule, SIGNAL(currentOvershootChanged()));
+
+ QPoint p1(10, 10);
+ QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p1);
+ // unrestricted drag
+ p1 += QPoint(100, 0);
+ QTest::mouseMove(&window, p1);
+ QTRY_VERIFY(dragHandler->active());
+ QCOMPARE(target->position().x(), 100);
+ QCOMPARE(boundaryRule->currentOvershoot(), 0);
+ QCOMPARE(boundaryRule->peakOvershoot(), 0);
+ QCOMPARE(overshootChangedSpy.count(), 0);
+ // restricted drag: halfway into overshoot
+ p1 += QPoint(20, 0);
+ QTest::mouseMove(&window, p1);
+ QCOMPARE(target->position().x(), 117.5);
+ QCOMPARE(boundaryRule->currentOvershoot(), 20);
+ QCOMPARE(boundaryRule->peakOvershoot(), 20);
+ QCOMPARE(overshootChangedSpy.count(), 1);
+ // restricted drag: maximum overshoot
+ p1 += QPoint(80, 0);
+ QTest::mouseMove(&window, p1);
+ QCOMPARE(target->position().x(), 140);
+ QCOMPARE(boundaryRule->currentOvershoot(), 100);
+ QCOMPARE(boundaryRule->peakOvershoot(), 100);
+ QCOMPARE(overshootChangedSpy.count(), 2);
+ // release and let it return to bounds
+ QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p1);
+ QTRY_COMPARE(dragHandler->active(), false);
+ QTRY_COMPARE(overshootChangedSpy.count(), 3);
+ QCOMPARE(boundaryRule->currentOvershoot(), 0);
+ QCOMPARE(boundaryRule->peakOvershoot(), 0);
+ QCOMPARE(target->position().x(), 100);
+}
+
+QTEST_MAIN(tst_qquickboundaryrule)
+
+#include "tst_qquickboundaryrule.moc"
diff --git a/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp b/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp
index d3d3505e85..f1288c2dbe 100644
--- a/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp
+++ b/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp
@@ -599,7 +599,7 @@ void tst_QQuickDrag::move()
QCoreApplication::processEvents();
QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&rightTarget));
QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&rightTarget));
- QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 0);
+ QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 1); QCOMPARE(outerTarget.moveEvents, 0);
QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 0); QCOMPARE(leftTarget .moveEvents, 0);
QCOMPARE(rightTarget.enterEvents, 1); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0);
QCOMPARE(rightTarget.position.x(), qreal(5)); QCOMPARE(rightTarget.position.y(), qreal(15));
@@ -621,10 +621,10 @@ void tst_QQuickDrag::move()
QCoreApplication::processEvents();
QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&leftTarget));
QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&leftTarget));
- QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 1);
+ QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 0);
QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 0); QCOMPARE(leftTarget .moveEvents, 1);
QCOMPARE(rightTarget.enterEvents, 0); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0);
- QCOMPARE(outerTarget.position.x(), qreal(25)); QCOMPARE(outerTarget.position.y(), qreal(40));
+ QCOMPARE(outerTarget.position.x(), qreal(60)); QCOMPARE(outerTarget.position.y(), qreal(50));
QCOMPARE(leftTarget.position.x(), qreal(25)); QCOMPARE(leftTarget.position.y(), qreal(5));
// Move out of all targets.
@@ -633,7 +633,7 @@ void tst_QQuickDrag::move()
QCoreApplication::processEvents();
QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(nullptr));
QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(nullptr));
- QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 1); QCOMPARE(outerTarget.moveEvents, 0);
+ QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 0);
QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 1); QCOMPARE(leftTarget .moveEvents, 0);
QCOMPARE(rightTarget.enterEvents, 0); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0);
diff --git a/tests/auto/quick/qquickdroparea/data/nested1.qml b/tests/auto/quick/qquickdroparea/data/nested1.qml
new file mode 100644
index 0000000000..de6ac70d08
--- /dev/null
+++ b/tests/auto/quick/qquickdroparea/data/nested1.qml
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.0
+
+Item {
+ width: 200; height: 200
+ property int outerEnterEvents: 0
+ property int outerExitEvents: 0
+ property int innerEnterEvents: 0
+ property int innerExitEvents: 0
+
+ DropArea {
+ objectName: "outerDropArea"
+ x: 75; y: 75
+ width: 100; height: 100
+ Rectangle {
+ anchors.fill: parent
+ color: "green"
+ }
+ onEntered: ++outerEnterEvents
+ onExited: ++outerExitEvents
+
+ DropArea {
+ objectName: "innerDropArea"
+ width: 50; height: 50
+ Rectangle {
+ anchors.fill: parent
+ color: "blue"
+ }
+ onEntered: ++innerEnterEvents
+ onExited: ++innerExitEvents
+ }
+ }
+
+ Rectangle {
+ width: 20; height: 20
+ color: dragArea.pressed ? "red" : "brown"
+ Drag.active: dragArea.drag.active
+ MouseArea {
+ id: dragArea
+ objectName: "dragArea"
+ anchors.fill: parent
+ drag.target: parent
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickdroparea/data/nested2.qml b/tests/auto/quick/qquickdroparea/data/nested2.qml
new file mode 100644
index 0000000000..93630c3779
--- /dev/null
+++ b/tests/auto/quick/qquickdroparea/data/nested2.qml
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.0
+
+Item {
+ width: 200; height: 200
+ property int outerEnterEvents: 0
+ property int outerExitEvents: 0
+ property int innerEnterEvents: 0
+ property int innerExitEvents: 0
+
+ Rectangle {
+ x: 75; y: 75
+ width: 100; height: 100
+ color: "green"
+ DropArea {
+ objectName: "outerDropArea"
+ anchors.fill: parent
+ onEntered: ++outerEnterEvents
+ onExited: ++outerExitEvents
+ }
+
+ Rectangle {
+ width: 50; height: 50
+ color: "blue"
+ DropArea {
+ objectName: "innerDropArea"
+ anchors.fill: parent
+ onEntered: ++innerEnterEvents
+ onExited: ++innerExitEvents
+ }
+ }
+ }
+
+ Rectangle {
+ width: 20; height: 20
+ color: dragArea.pressed ? "red" : "brown"
+ Drag.active: dragArea.drag.active
+ MouseArea {
+ id: dragArea
+ objectName: "dragArea"
+ anchors.fill: parent
+ drag.target: parent
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickdroparea/qquickdroparea.pro b/tests/auto/quick/qquickdroparea/qquickdroparea.pro
index a34d5ad009..7a8fdef7b9 100644
--- a/tests/auto/quick/qquickdroparea/qquickdroparea.pro
+++ b/tests/auto/quick/qquickdroparea/qquickdroparea.pro
@@ -4,4 +4,11 @@ macx:CONFIG -= app_bundle
SOURCES += tst_qquickdroparea.cpp
+OTHER_FILES += $$files(data/*.qml)
+
+include (../../shared/util.pri)
+include (../shared/util.pri)
+
+TESTDATA = data/*
+
QT += core-private gui-private qml-private quick-private network testlib
diff --git a/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp b/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp
index cf01cc927b..dcba4c872e 100644
--- a/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp
+++ b/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp
@@ -28,6 +28,7 @@
#include <QtTest/QtTest>
#include <QtTest/QSignalSpy>
+#include <QtGui/qstylehints.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickview.h>
#include <QtQml/qqmlcontext.h>
@@ -36,6 +37,8 @@
#include <qpa/qplatformdrag.h>
#include <qpa/qwindowsysteminterface.h>
+#include "../../shared/util.h"
+#include "../shared/viewtestutil.h"
template <typename T> static T evaluate(QObject *scope, const QString &expression)
{
@@ -54,13 +57,10 @@ template <> void evaluate<void>(QObject *scope, const QString &expression)
qWarning() << expr.error().toString();
}
-class tst_QQuickDropArea: public QObject
+class tst_QQuickDropArea: public QQmlDataTest
{
Q_OBJECT
private slots:
- void initTestCase();
- void cleanupTestCase();
-
void containsDrag_internal();
void containsDrag_external();
void keys_internal();
@@ -74,21 +74,13 @@ private slots:
void competingDrags();
void simultaneousDrags();
void dropStuff();
+ void nestedDropAreas_data();
+ void nestedDropAreas();
private:
QQmlEngine engine;
};
-void tst_QQuickDropArea::initTestCase()
-{
-
-}
-
-void tst_QQuickDropArea::cleanupTestCase()
-{
-
-}
-
void tst_QQuickDropArea::containsDrag_internal()
{
QQuickWindow window;
@@ -1224,6 +1216,74 @@ void tst_QQuickDropArea::dropStuff()
QCOMPARE(evaluate<QByteArray>(dropArea, "array"), QByteArray("red"));
}
+void tst_QQuickDropArea::nestedDropAreas_data()
+{
+ QTest::addColumn<QString>("qmlFile");
+
+ QTest::newRow("dropRectDropRect") << "nested1.qml";
+ QTest::newRow("rectDropRectDrop") << "nested2.qml";
+}
+
+void tst_QQuickDropArea::nestedDropAreas()
+{
+ QFETCH(QString, qmlFile);
+
+ const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+ QQuickView window;
+ QByteArray errorMessage;
+ QVERIFY2(QQuickTest::initView(window, testFileUrl(qmlFile.toLatin1().data()), true, &errorMessage), errorMessage.constData());
+
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+ QVERIFY(window.rootObject() != nullptr);
+
+ QQuickItem *dragArea = window.rootObject()->findChild<QQuickItem*>("dragArea");
+ QVERIFY(dragArea);
+ QQuickItem *outerDropArea = window.rootObject()->findChild<QQuickItem*>("outerDropArea");
+ QVERIFY(outerDropArea);
+ QQuickItem *innerDropArea = window.rootObject()->findChild<QQuickItem*>("innerDropArea");
+ QVERIFY(innerDropArea);
+
+ QPoint p = QPoint(10,10);
+ QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p);
+
+ // move the minimum distance to activate drag
+ p += QPoint(dragThreshold + 1, dragThreshold + 1);
+ QTest::mouseMove(&window, p);
+
+ // drag the red rectangle into the inner DropArea
+ p += QPoint(100, 100);
+ QTest::mouseMove(&window, p);
+ QCOMPARE(window.rootObject()->property("outerEnterEvents"), 0);
+ QCOMPARE(window.rootObject()->property("outerExitEvents"), 0);
+ QCOMPARE(window.rootObject()->property("innerEnterEvents"), 1);
+ QCOMPARE(window.rootObject()->property("innerExitEvents"), 0);
+
+ // drag the red rectangle into the outer DropArea
+ p += QPoint(0, 50);
+ QTest::mouseMove(&window, p);
+ QCOMPARE(window.rootObject()->property("outerEnterEvents"), 1);
+ QCOMPARE(window.rootObject()->property("outerExitEvents"), 0);
+ QCOMPARE(window.rootObject()->property("innerEnterEvents"), 1);
+ QCOMPARE(window.rootObject()->property("innerExitEvents"), 1);
+
+ // drag the red rectangle into the inner DropArea
+ p -= QPoint(0, 50);
+ QTest::mouseMove(&window, p);
+ QCOMPARE(window.rootObject()->property("outerEnterEvents"), 1);
+ QCOMPARE(window.rootObject()->property("outerExitEvents"), 1);
+ QCOMPARE(window.rootObject()->property("innerEnterEvents"), 2);
+ QCOMPARE(window.rootObject()->property("innerExitEvents"), 1);
+
+ // drag the red rectangle back out of both
+ p -= QPoint(100, 100);
+ QTest::mouseMove(&window, p);
+ QCOMPARE(window.rootObject()->property("outerEnterEvents"), 1);
+ QCOMPARE(window.rootObject()->property("outerExitEvents"), 1);
+ QCOMPARE(window.rootObject()->property("innerEnterEvents"), 2);
+ QCOMPARE(window.rootObject()->property("innerExitEvents"), 2);
+}
+
QTEST_MAIN(tst_QQuickDropArea)
#include "tst_qquickdroparea.moc"
diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
index 2314b82e8c..c104eecbcd 100644
--- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
+++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
@@ -874,7 +874,8 @@ void tst_qquickflickable::wheel()
// test a vertical flick
{
QPoint pos(200, 200);
- QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(0,-120), -120, Qt::Vertical, Qt::NoButton, Qt::NoModifier);
+ QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(0,-120),
+ Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
event.setAccepted(false);
QGuiApplication::sendEvent(window.data(), &event);
}
@@ -897,7 +898,8 @@ void tst_qquickflickable::wheel()
// test a horizontal flick
{
QPoint pos(200, 200);
- QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(-120,0), -120, Qt::Horizontal, Qt::NoButton, Qt::NoModifier);
+ QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(-120,0),
+ Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
event.setAccepted(false);
QGuiApplication::sendEvent(window.data(), &event);
@@ -926,7 +928,8 @@ void tst_qquickflickable::trackpad()
QPoint pos(200, 200);
{
- QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(0,-100), QPoint(0,-120), -120, Qt::Vertical, Qt::NoButton, Qt::NoModifier, Qt::ScrollBegin);
+ QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(0,-100), QPoint(0,-120),
+ Qt::NoButton, Qt::NoModifier, Qt::ScrollBegin, false);
event.setAccepted(false);
QGuiApplication::sendEvent(window.data(), &event);
}
@@ -938,7 +941,8 @@ void tst_qquickflickable::trackpad()
QCOMPARE(flick->contentY(), qreal(0));
{
- QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(-100,0), QPoint(-120,0), -120, Qt::Horizontal, Qt::NoButton, Qt::NoModifier, Qt::ScrollUpdate);
+ QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(-100,0), QPoint(-120,0),
+ Qt::NoButton, Qt::NoModifier, Qt::ScrollUpdate, false);
event.setAccepted(false);
QGuiApplication::sendEvent(window.data(), &event);
}
@@ -947,7 +951,8 @@ void tst_qquickflickable::trackpad()
QCOMPARE(flick->contentY(), qreal(0));
{
- QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(0,0), QPoint(0,0), 0, Qt::Horizontal, Qt::NoButton, Qt::NoModifier, Qt::ScrollEnd);
+ QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(0,0), QPoint(0,0),
+ Qt::NoButton, Qt::NoModifier, Qt::ScrollEnd, false);
event.setAccepted(false);
QGuiApplication::sendEvent(window.data(), &event);
}
diff --git a/tests/auto/quick/qquickfontloader_static/tst_qquickfontloader_static.cpp b/tests/auto/quick/qquickfontloader_static/tst_qquickfontloader_static.cpp
index c0f66bd709..7e848ef2fc 100644
--- a/tests/auto/quick/qquickfontloader_static/tst_qquickfontloader_static.cpp
+++ b/tests/auto/quick/qquickfontloader_static/tst_qquickfontloader_static.cpp
@@ -59,6 +59,8 @@ int main(int argc, char **argv)
component.setData(qmltemplate, current);
window.setContent(current, &component, component.create());
window.show();
- QTest::qWaitForWindowActive(&window);
+ if (!QTest::qWaitForWindowActive(&window))
+ return EXIT_FAILURE;
}
+ return 0;
}
diff --git a/tests/auto/quick/qquickgridview/qquickgridview.pro b/tests/auto/quick/qquickgridview/qquickgridview.pro
index 5051f8bc62..0390637058 100644
--- a/tests/auto/quick/qquickgridview/qquickgridview.pro
+++ b/tests/auto/quick/qquickgridview/qquickgridview.pro
@@ -10,5 +10,5 @@ include (../shared/util.pri)
TESTDATA = data/*
-QT += core-private gui-private qml-private quick-private testlib qmltest
+QT += core-private gui-private qml-private quick-private testlib qmltest qmlmodels-private
diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
index 448096720c..46e3457d6e 100644
--- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
+++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
@@ -40,7 +40,7 @@
#include <QtQuick/private/qquickitemview_p_p.h>
#include <QtQuick/private/qquickgridview_p.h>
#include <QtQuick/private/qquicktext_p.h>
-#include <QtQml/private/qqmllistmodel_p.h>
+#include <QtQmlModels/private/qqmllistmodel_p.h>
#include "../../shared/util.h"
#include "../shared/viewtestutil.h"
#include "../shared/visualtestutil.h"
@@ -6433,11 +6433,13 @@ QList<int> tst_QQuickGridView::toIntList(const QVariantList &list)
void tst_QQuickGridView::matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes)
{
+ const QSet<int> expectedIndexSet(expectedIndexes.cbegin(), expectedIndexes.cend());
for (int i=0; i<indexLists.count(); i++) {
- QSet<int> current = indexLists[i].value<QList<int> >().toSet();
- if (current != expectedIndexes.toSet())
+ const auto &currentList = indexLists[i].value<QList<int> >();
+ const QSet<int> current(currentList.cbegin(), currentList.cend());
+ if (current != expectedIndexSet)
qDebug() << "Cannot match actual targets" << current << "with expected" << expectedIndexes;
- QCOMPARE(current, expectedIndexes.toSet());
+ QCOMPARE(current, expectedIndexSet);
}
}
diff --git a/tests/auto/quick/qquickimage/data/multi.ico b/tests/auto/quick/qquickimage/data/multi.ico
new file mode 100644
index 0000000000..b748ceaa29
--- /dev/null
+++ b/tests/auto/quick/qquickimage/data/multi.ico
Binary files differ
diff --git a/tests/auto/quick/qquickimage/data/multiframe.qml b/tests/auto/quick/qquickimage/data/multiframe.qml
new file mode 100644
index 0000000000..df70bc784c
--- /dev/null
+++ b/tests/auto/quick/qquickimage/data/multiframe.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.14
+
+Image {
+ source: "multi.ico"
+}
diff --git a/tests/auto/quick/qquickimage/data/multiframeAsync.qml b/tests/auto/quick/qquickimage/data/multiframeAsync.qml
new file mode 100644
index 0000000000..167b4a3e57
--- /dev/null
+++ b/tests/auto/quick/qquickimage/data/multiframeAsync.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.14
+
+Image {
+ source: "multi.ico"
+ asynchronous: true
+}
diff --git a/tests/auto/quick/qquickimage/tst_qquickimage.cpp b/tests/auto/quick/qquickimage/tst_qquickimage.cpp
index d1f46a3912..abc7cd86bd 100644
--- a/tests/auto/quick/qquickimage/tst_qquickimage.cpp
+++ b/tests/auto/quick/qquickimage/tst_qquickimage.cpp
@@ -45,6 +45,7 @@
#include <QQuickWindow>
#include <QQuickView>
#include <QQuickImageProvider>
+#include <QQmlAbstractUrlInterceptor>
#include "../../shared/util.h"
#include "../../shared/testhttpserver.h"
@@ -95,6 +96,9 @@ private slots:
void highDpiFillModesAndSizes_data();
void highDpiFillModesAndSizes();
void hugeImages();
+ void urlInterceptor();
+ void multiFrame_data();
+ void multiFrame();
private:
QQmlEngine engine;
@@ -1100,6 +1104,92 @@ void tst_qquickimage::hugeImages()
QCOMPARE(contents.pixel(199, 99), qRgba(0, 0, 255, 255));
}
+
+class MyInterceptor : public QQmlAbstractUrlInterceptor
+{
+public:
+ MyInterceptor(QUrl url) : QQmlAbstractUrlInterceptor(), m_url(url) {}
+ QUrl intercept(const QUrl &url, QQmlAbstractUrlInterceptor::DataType)
+ {
+ if (url.scheme() == "interceptthis")
+ return m_url;
+ return url;
+ }
+
+ QUrl m_url;
+};
+
+void tst_qquickimage::urlInterceptor()
+{
+ QQmlEngine engine;
+ MyInterceptor interceptor {testFileUrl("colors.png")};
+ engine.setUrlInterceptor(&interceptor);
+
+ QQmlComponent c(&engine);
+
+ c.setData("import QtQuick 2.12; Image { objectName: \"item\"; source: width == 0 ? \"interceptthis:doesNotExist\" : \"interceptthis:doesNotExist\"}", QUrl{});
+ QScopedPointer<QQuickImage> object { qobject_cast<QQuickImage*>(c.create())};
+ QVERIFY(object);
+ QTRY_COMPARE(object->status(), QQuickImage::Ready);
+ QTRY_COMPARE(object->progress(), 1.0);
+}
+
+void tst_qquickimage::multiFrame_data()
+{
+ QTest::addColumn<QString>("qmlfile");
+ QTest::addColumn<bool>("asynchronous");
+
+ QTest::addRow("default") << "multiframe.qml" << false;
+ QTest::addRow("async") << "multiframeAsync.qml" << true;
+}
+
+void tst_qquickimage::multiFrame()
+{
+ if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
+ || (QGuiApplication::platformName() == QLatin1String("minimal")))
+ QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms");
+
+ QFETCH(QString, qmlfile);
+ QFETCH(bool, asynchronous);
+ Q_UNUSED(asynchronous)
+
+ QQuickView view(testFileUrl(qmlfile));
+ QQuickImage *image = qobject_cast<QQuickImage*>(view.rootObject());
+ QVERIFY(image);
+ QSignalSpy countSpy(image, SIGNAL(frameCountChanged()));
+ QSignalSpy currentSpy(image, SIGNAL(currentFrameChanged()));
+ if (asynchronous) {
+ QCOMPARE(image->frameCount(), 0);
+ QTRY_COMPARE(image->frameCount(), 4);
+ QCOMPARE(countSpy.count(), 1);
+ } else {
+ QCOMPARE(image->frameCount(), 4);
+ }
+ QCOMPARE(image->currentFrame(), 0);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ QImage contents = view.grabWindow();
+ if (contents.width() < 40)
+ QSKIP("Skipping due to grabWindow not functional");
+ // The first frame is a blue ball, approximately qRgba(0x33, 0x6d, 0xcc, 0xff)
+ QRgb color = contents.pixel(16, 16);
+ QVERIFY(qRed(color) < 0xc0);
+ QVERIFY(qGreen(color) < 0xc0);
+ QVERIFY(qBlue(color) > 0xc0);
+
+ image->setCurrentFrame(1);
+ QTRY_COMPARE(image->status(), QQuickImageBase::Ready);
+ QCOMPARE(currentSpy.count(), 1);
+ QCOMPARE(image->currentFrame(), 1);
+ contents = view.grabWindow();
+ // The second frame is a green ball, approximately qRgba(0x27, 0xc8, 0x22, 0xff)
+ color = contents.pixel(16, 16);
+ QVERIFY(qRed(color) < 0xc0);
+ QVERIFY(qGreen(color) > 0xc0);
+ QVERIFY(qBlue(color) < 0xc0);
+}
+
QTEST_MAIN(tst_qquickimage)
#include "tst_qquickimage.moc"
diff --git a/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp b/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp
index 4b75a7e008..9dc9ad53ea 100644
--- a/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp
+++ b/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp
@@ -33,6 +33,7 @@
#include <QImageReader>
#include <QWaitCondition>
#include <QThreadPool>
+#include <private/qqmlengine_p.h>
Q_DECLARE_METATYPE(QQuickImageProvider*);
@@ -67,6 +68,8 @@ private slots:
void asyncTextureTest();
void instantAsyncTextureTest();
+ void asyncImageThreadSafety();
+
private:
QString newImageFileName() const;
void fillRequestTestsData(const QString &id);
@@ -448,21 +451,56 @@ void tst_qquickimageprovider::threadTest()
foreach (QQuickImage *img, images) {
QCOMPARE(img->status(), QQuickImage::Loading);
}
- provider->ok = true;
- provider->cond.wakeAll();
+ {
+ QMutexLocker lock(&provider->mutex);
+ provider->ok = true;
+ provider->cond.wakeAll();
+ }
QTest::qWait(250);
foreach (QQuickImage *img, images) {
QTRY_COMPARE(img->status(), QQuickImage::Ready);
}
}
-class TestImageResponse : public QQuickImageResponse, public QRunnable
+class TestImageResponseRunner : public QObject, public QRunnable {
+
+ Q_OBJECT
+
+public:
+ Q_SIGNAL void finished(QQuickTextureFactory *texture);
+ TestImageResponseRunner(QMutex *lock, QWaitCondition *condition, bool *ok, const QString &id, const QSize &requestedSize)
+ : m_lock(lock), m_condition(condition), m_ok(ok), m_id(id), m_requestedSize(requestedSize) {}
+ void run()
+ {
+ m_lock->lock();
+ if (!(*m_ok)) {
+ m_condition->wait(m_lock);
+ }
+ m_lock->unlock();
+ QImage image(50, 50, QImage::Format_RGB32);
+ image.fill(QColor(m_id).rgb());
+ if (m_requestedSize.isValid())
+ image = image.scaled(m_requestedSize);
+ emit finished(QQuickTextureFactory::textureFactoryForImage(image));
+ }
+
+private:
+ QMutex *m_lock;
+ QWaitCondition *m_condition;
+ bool *m_ok;
+ QString m_id;
+ QSize m_requestedSize;
+};
+
+class TestImageResponse : public QQuickImageResponse
{
public:
- TestImageResponse(QMutex *lock, QWaitCondition *condition, bool *ok, const QString &id, const QSize &requestedSize)
+ TestImageResponse(QMutex *lock, QWaitCondition *condition, bool *ok, const QString &id, const QSize &requestedSize, QThreadPool *pool)
: m_lock(lock), m_condition(condition), m_ok(ok), m_id(id), m_requestedSize(requestedSize), m_texture(nullptr)
{
- setAutoDelete(false);
+ auto runnable = new TestImageResponseRunner(m_lock, m_condition, m_ok, m_id, m_requestedSize);
+ QObject::connect(runnable, &TestImageResponseRunner::finished, this, &TestImageResponse::handleResponse);
+ pool->start(runnable);
}
QQuickTextureFactory *textureFactory() const
@@ -470,18 +508,8 @@ class TestImageResponse : public QQuickImageResponse, public QRunnable
return m_texture;
}
- void run()
- {
- m_lock->lock();
- if (!(*m_ok)) {
- m_condition->wait(m_lock);
- }
- m_lock->unlock();
- QImage image(50, 50, QImage::Format_RGB32);
- image.fill(QColor(m_id).rgb());
- if (m_requestedSize.isValid())
- image = image.scaled(m_requestedSize);
- m_texture = QQuickTextureFactory::textureFactoryForImage(image);
+ void handleResponse(QQuickTextureFactory *factory) {
+ this->m_texture = factory;
emit finished();
}
@@ -505,8 +533,7 @@ class TestAsyncProvider : public QQuickAsyncImageProvider
QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize)
{
- TestImageResponse *response = new TestImageResponse(&lock, &condition, &ok, id, requestedSize);
- pool.start(response);
+ TestImageResponse *response = new TestImageResponse(&lock, &condition, &ok, id, requestedSize, &pool);
return response;
}
@@ -544,8 +571,11 @@ void tst_qquickimageprovider::asyncTextureTest()
foreach (QQuickImage *img, images) {
QTRY_COMPARE(img->status(), QQuickImage::Loading);
}
- provider->ok = true;
- provider->condition.wakeAll();
+ {
+ QMutexLocker lock(&provider->lock);
+ provider->ok = true;
+ provider->condition.wakeAll();
+ }
foreach (QQuickImage *img, images) {
QTRY_COMPARE(img->status(), QQuickImage::Ready);
}
@@ -616,6 +646,115 @@ void tst_qquickimageprovider::instantAsyncTextureTest()
}
+class WaitingAsyncImageResponse : public QQuickImageResponse, public QRunnable
+{
+public:
+ WaitingAsyncImageResponse(QMutex *providerRemovedMutex, QWaitCondition *providerRemovedCond, bool *providerRemoved, QMutex *imageRequestedMutex, QWaitCondition *imageRequestedCond, bool *imageRequested)
+ : m_providerRemovedMutex(providerRemovedMutex), m_providerRemovedCond(providerRemovedCond), m_providerRemoved(providerRemoved),
+ m_imageRequestedMutex(imageRequestedMutex), m_imageRequestedCondition(imageRequestedCond), m_imageRequested(imageRequested)
+ {
+ setAutoDelete(false);
+ }
+
+ void run() override
+ {
+ m_imageRequestedMutex->lock();
+ *m_imageRequested = true;
+ m_imageRequestedCondition->wakeAll();
+ m_imageRequestedMutex->unlock();
+ m_providerRemovedMutex->lock();
+ while (!*m_providerRemoved)
+ m_providerRemovedCond->wait(m_providerRemovedMutex);
+ m_providerRemovedMutex->unlock();
+ emit finished();
+ }
+
+ QQuickTextureFactory *textureFactory() const override
+ {
+ QImage image(50, 50, QImage::Format_RGB32);
+ auto texture = QQuickTextureFactory::textureFactoryForImage(image);
+ return texture;
+ }
+
+ QMutex *m_providerRemovedMutex;
+ QWaitCondition *m_providerRemovedCond;
+ bool *m_providerRemoved;
+ QMutex *m_imageRequestedMutex;
+ QWaitCondition *m_imageRequestedCondition;
+ bool *m_imageRequested;
+
+};
+
+class WaitingAsyncProvider : public QQuickAsyncImageProvider
+{
+public:
+ WaitingAsyncProvider(QMutex *providerRemovedMutex, QWaitCondition *providerRemovedCond, bool *providerRemoved, QMutex *imageRequestedMutex, QWaitCondition *imageRequestedCond, bool *imageRequested)
+ : m_providerRemovedMutex(providerRemovedMutex), m_providerRemovedCond(providerRemovedCond), m_providerRemoved(providerRemoved),
+ m_imageRequestedMutex(imageRequestedMutex), m_imageRequestedCondition(imageRequestedCond), m_imageRequested(imageRequested)
+ {
+ }
+
+ ~WaitingAsyncProvider() {}
+
+ QQuickImageResponse *requestImageResponse(const QString & /* id */, const QSize & /* requestedSize */)
+ {
+ auto response = new WaitingAsyncImageResponse(m_providerRemovedMutex, m_providerRemovedCond, m_providerRemoved, m_imageRequestedMutex, m_imageRequestedCondition, m_imageRequested);
+ pool.start(response);
+ return response;
+ }
+
+ QMutex *m_providerRemovedMutex;
+ QWaitCondition *m_providerRemovedCond;
+ bool *m_providerRemoved;
+ QMutex *m_imageRequestedMutex;
+ QWaitCondition *m_imageRequestedCondition;
+ bool *m_imageRequested;
+ QThreadPool pool;
+};
+
+
+// QTBUG-76527
+void tst_qquickimageprovider::asyncImageThreadSafety()
+{
+ QQmlEngine engine;
+ QMutex providerRemovedMutex;
+ bool providerRemoved = false;
+ QWaitCondition providerRemovedCond;
+ QMutex imageRequestedMutex;
+ bool imageRequested = false;
+ QWaitCondition imageRequestedCond;
+ auto imageProvider = new WaitingAsyncProvider(&providerRemovedMutex, &providerRemovedCond, &providerRemoved, &imageRequestedMutex, &imageRequestedCond, &imageRequested);
+ engine.addImageProvider("test_waiting", imageProvider);
+ QVERIFY(engine.imageProvider("test_waiting") != nullptr);
+ auto privateEngine = QQmlEnginePrivate::get(&engine);
+
+ QString componentStr = "import QtQuick 2.0\nItem { \n"
+ "Image { source: \"image://test_waiting/blue\"; }\n"
+ " }";
+ QQmlComponent component(&engine);
+ component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ QWeakPointer<QQmlImageProviderBase> observer = privateEngine->imageProvider("test_waiting").toWeakRef();
+ QVERIFY(!observer.isNull()); // engine still own the object
+ imageRequestedMutex.lock();
+ while (!imageRequested)
+ imageRequestedCond.wait(&imageRequestedMutex);
+ imageRequestedMutex.unlock();
+ engine.removeImageProvider("test_waiting");
+
+ QVERIFY(engine.imageProvider("test_waiting") == nullptr);
+ QVERIFY(!observer.isNull()); // lifetime has been extended
+
+ providerRemovedMutex.lock();
+ providerRemoved = true;
+ providerRemovedCond.wakeAll();
+ providerRemovedMutex.unlock();
+
+ QTRY_VERIFY(observer.isNull()); // once the reply has finished, the imageprovider gets deleted
+}
+
+
QTEST_MAIN(tst_qquickimageprovider)
#include "tst_qquickimageprovider.moc"
diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp
index 9ce9766c92..8aab13e095 100644
--- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp
@@ -83,8 +83,8 @@ protected:
event->accept();
++wheelCount;
timestamp = event->timestamp();
- lastWheelEventPos = event->pos();
- lastWheelEventGlobalPos = event->globalPos();
+ lastWheelEventPos = event->position().toPoint();
+ lastWheelEventGlobalPos = event->globalPosition().toPoint();
}
};
@@ -1464,7 +1464,8 @@ void tst_qquickitem::wheelEvent()
QPoint localPoint(width / 2, height / 2);
QPoint globalPoint = window.mapToGlobal(localPoint);
- QWheelEvent event(localPoint, globalPoint, -120, Qt::NoButton, Qt::NoModifier, Qt::Vertical);
+ QWheelEvent event(localPoint, globalPoint, QPoint(0, 0), QPoint(0, -120),
+ Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
event.setTimestamp(123456UL);
event.setAccepted(false);
QGuiApplication::sendEvent(&window, &event);
diff --git a/tests/auto/quick/qquicklistview/BLACKLIST b/tests/auto/quick/qquicklistview/BLACKLIST
index 42658dca7a..1f3736328a 100644
--- a/tests/auto/quick/qquicklistview/BLACKLIST
+++ b/tests/auto/quick/qquicklistview/BLACKLIST
@@ -1,15 +1,11 @@
[enforceRange_withoutHighlight]
-osx
opensuse-42.3
opensuse-leap
#QTBUG-53863
[populateTransitions]
opensuse-42.1
-#QTBUG-65964
-
[contentHeightWithDelayRemove]
osx-10.12
-
#QTBUG-75960
#QTBUG-76652
[currentIndex]
diff --git a/tests/auto/quick/qquicklistview/qquicklistview.pro b/tests/auto/quick/qquicklistview/qquicklistview.pro
index fd96c269a2..b08fca2b1d 100644
--- a/tests/auto/quick/qquicklistview/qquicklistview.pro
+++ b/tests/auto/quick/qquicklistview/qquicklistview.pro
@@ -18,5 +18,5 @@ include (../shared/util.pri)
TESTDATA = data/*
DISTFILES += data/*
-QT += core-private gui-private qml-private quick-private testlib qmltest
+QT += core-private gui-private qml-private quick-private testlib qmltest qmlmodels-private
diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
index 9419dae362..ca438a9cd5 100644
--- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
+++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
@@ -40,9 +40,9 @@
#include <QtQuick/private/qquicklistview_p.h>
#include <QtQuick/private/qquickmousearea_p.h>
#include <QtQuick/private/qquicktext_p.h>
-#include <QtQml/private/qqmlobjectmodel_p.h>
-#include <QtQml/private/qqmllistmodel_p.h>
-#include <QtQml/private/qqmldelegatemodel_p.h>
+#include <QtQmlModels/private/qqmlobjectmodel_p.h>
+#include <QtQmlModels/private/qqmllistmodel_p.h>
+#include <QtQmlModels/private/qqmldelegatemodel_p.h>
#include "../../shared/util.h"
#include "../shared/viewtestutil.h"
#include "../shared/visualtestutil.h"
@@ -7307,11 +7307,13 @@ QList<int> tst_QQuickListView::toIntList(const QVariantList &list)
void tst_QQuickListView::matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes)
{
+ const QSet<int> expectedIndexSet(expectedIndexes.cbegin(), expectedIndexes.cend());
for (int i=0; i<indexLists.count(); i++) {
- QSet<int> current = indexLists[i].value<QList<int> >().toSet();
- if (current != expectedIndexes.toSet())
+ const auto &currentList = indexLists[i].value<QList<int> >();
+ const QSet<int> current(currentList.cbegin(), currentList.cend());
+ if (current != expectedIndexSet)
qDebug() << "Cannot match actual targets" << current << "with expected" << expectedIndexes;
- QCOMPARE(current, expectedIndexes.toSet());
+ QCOMPARE(current, expectedIndexSet);
}
}
diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
index 52d1458a53..17553ee6c4 100644
--- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
+++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
@@ -393,10 +393,20 @@ void tst_QQuickMouseArea::dragging()
QTRY_COMPARE(blackRect->x(), 61.0);
QCOMPARE(blackRect->y(), 61.0);
+ qreal relativeX = mouseRegion->mouseX();
+ qreal relativeY = mouseRegion->mouseY();
+ for (int i = 0; i < 20; i++) {
+ p += QPoint(1, 1);
+ QTest::mouseMove(&window, p);
+ }
+ QTRY_VERIFY(drag->active());
+ QTRY_COMPARE(mouseRegion->mouseX(), relativeX);
+ QCOMPARE(mouseRegion->mouseY(), relativeY);
+
QTest::mouseRelease(&window, button, Qt::NoModifier, p);
QTRY_VERIFY(!drag->active());
- QTRY_COMPARE(blackRect->x(), 61.0);
- QCOMPARE(blackRect->y(), 61.0);
+ QTRY_COMPARE(blackRect->x(), 81.0);
+ QCOMPARE(blackRect->y(), 81.0);
}
void tst_QQuickMouseArea::dragSmoothed()
@@ -1349,6 +1359,34 @@ void tst_QQuickMouseArea::hoverVisible()
QCOMPARE(enteredSpy.count(), 1);
QCOMPARE(QPointF(mouseTracker->mouseX(), mouseTracker->mouseY()), QPointF(11,33));
+
+ // QTBUG-77983
+ mouseTracker->setVisible(false);
+ mouseTracker->setEnabled(false);
+
+ QCOMPARE(mouseTracker->hovered(), false);
+ mouseTracker->setVisible(true);
+ // if the enabled property is false, the containsMouse property shouldn't become true
+ // when an invisible mousearea become visible
+ QCOMPARE(mouseTracker->hovered(), false);
+
+ mouseTracker->parentItem()->setEnabled(false);
+ mouseTracker->setVisible(false);
+ mouseTracker->setEnabled(true);
+
+ QCOMPARE(mouseTracker->hovered(), false);
+ mouseTracker->setVisible(true);
+ // if the parent item is not enabled, the containsMouse property will be false, even if
+ // the mousearea is enabled
+ QCOMPARE(mouseTracker->hovered(), false);
+
+ mouseTracker->parentItem()->setEnabled(true);
+ mouseTracker->setVisible(false);
+ mouseTracker->setEnabled(true);
+
+ QCOMPARE(mouseTracker->hovered(), false);
+ mouseTracker->setVisible(true);
+ QCOMPARE(mouseTracker->hovered(), true);
}
void tst_QQuickMouseArea::hoverAfterPress()
@@ -1509,7 +1547,7 @@ void tst_QQuickMouseArea::onWheel()
QVERIFY(root != nullptr);
QWheelEvent wheelEvent(QPoint(10, 32), QPoint(10, 32), QPoint(60, 20), QPoint(0, 120),
- 0, Qt::Vertical,Qt::NoButton, Qt::ControlModifier);
+ Qt::NoButton, Qt::ControlModifier, Qt::NoScrollPhase, false);
QGuiApplication::sendEvent(&window, &wheelEvent);
QCOMPARE(root->property("angleDeltaY").toInt(), 120);
diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
index cd66fc4ede..e96b892b54 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
+++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
@@ -72,6 +72,7 @@ private slots:
void mouseGestureStarted_data();
void mouseGestureStarted();
void cancel();
+ void stationaryTouchWithChangingPressure();
private:
QQuickView *createAndShowView(const QString &file);
@@ -1305,6 +1306,42 @@ void tst_QQuickMultiPointTouchArea::cancel()
}
+void tst_QQuickMultiPointTouchArea::stationaryTouchWithChangingPressure() // QTBUG-77142
+{
+ QScopedPointer<QQuickView> window(createAndShowView("basic.qml"));
+ QVERIFY(window->rootObject() != nullptr);
+
+ QQuickTouchPoint *point1 = window->rootObject()->findChild<QQuickTouchPoint*>("point1");
+ QCOMPARE(point1->pressed(), false);
+
+ QPoint p1(20,100);
+ QTouchEvent::TouchPoint tp1(1);
+
+ tp1.setScreenPos(window->mapToGlobal(p1));
+ tp1.setState(Qt::TouchPointPressed);
+ tp1.setPressure(0.5);
+ qt_handleTouchEvent(window.data(), device, {tp1});
+ QQuickTouchUtils::flush(window.data());
+
+ QCOMPARE(point1->pressed(), true);
+ QCOMPARE(point1->pressure(), 0.5);
+
+ tp1.setState(Qt::TouchPointStationary);
+ tp1.setPressure(0.6);
+ qt_handleTouchEvent(window.data(), device, {tp1});
+ QQuickTouchUtils::flush(window.data());
+
+ QCOMPARE(point1->pressure(), 0.6);
+
+ tp1.setState(Qt::TouchPointReleased);
+ tp1.setPressure(0);
+ qt_handleTouchEvent(window.data(), device, {tp1});
+ QQuickTouchUtils::flush(window.data());
+
+ QCOMPARE(point1->pressed(), false);
+ QCOMPARE(point1->pressure(), 0);
+}
+
QTEST_MAIN(tst_QQuickMultiPointTouchArea)
diff --git a/tests/auto/quick/qquickpath/qquickpath.pro b/tests/auto/quick/qquickpath/qquickpath.pro
index 492f82f53d..ef110a8331 100644
--- a/tests/auto/quick/qquickpath/qquickpath.pro
+++ b/tests/auto/quick/qquickpath/qquickpath.pro
@@ -7,5 +7,6 @@ SOURCES += tst_qquickpath.cpp
include (../../shared/util.pri)
TESTDATA = data/*
+DISTFILES = data/*
QT += core-private gui-private qml-private quick-private testlib
diff --git a/tests/auto/quick/qquickpath/tst_qquickpath.cpp b/tests/auto/quick/qquickpath/tst_qquickpath.cpp
index 12a8c673b0..c89ce730a8 100644
--- a/tests/auto/quick/qquickpath/tst_qquickpath.cpp
+++ b/tests/auto/quick/qquickpath/tst_qquickpath.cpp
@@ -42,18 +42,44 @@ public:
private slots:
void arc();
void angleArc();
- void catmullromCurve();
- void closedCatmullromCurve();
+ void catmullRomCurve();
+ void closedCatmullRomCurve();
void svg();
void line();
+
+private:
+ void arc(QSizeF scale);
+ void angleArc(QSizeF scale);
+ void catmullRomCurve(QSizeF scale, const QVector<QPointF> &points);
+ void closedCatmullRomCurve(QSizeF scale, const QVector<QPointF> &points);
+ void svg(QSizeF scale);
+ void line(QSizeF scale);
};
-void tst_QuickPath::arc()
+static void compare(const QPointF &point, const QSizeF &scale, int line, double x, double y)
+{
+ QVERIFY2(qFuzzyCompare(float(point.x()), float(x * scale.width())),
+ (QStringLiteral("Actual: ") + QString::number(point.x(),'g',14)
+ + QStringLiteral(" Expected: ") + QString::number(x * scale.width(),'g',14)
+ + QStringLiteral(" At: ") + QString::number(line)).toLatin1().data());
+ QVERIFY2(qFuzzyCompare(float(point.y()), float(y * scale.height())),
+ (QStringLiteral("Actual: ") + QString::number(point.y(),'g',14)
+ + QStringLiteral(" Expected: ") + QString::number(y * scale.height(),'g',14)
+ + QStringLiteral(" At: ") + QString::number(line)).toLatin1().data());
+}
+static void compare(const QPointF &point, int line, const QPointF &pt)
+{
+ return compare(point, QSizeF(1,1), line, pt.x(), pt.y());
+}
+
+void tst_QuickPath::arc(QSizeF scale)
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("arc.qml"));
QQuickPath *obj = qobject_cast<QQuickPath*>(c.create());
QVERIFY(obj != nullptr);
+ if (scale != QSizeF(1,1))
+ obj->setProperty("scale", scale);
QCOMPARE(obj->startX(), 0.);
QCOMPARE(obj->startY(), 0.);
@@ -73,22 +99,30 @@ void tst_QuickPath::arc()
QPainterPath path = obj->path();
QVERIFY(path != QPainterPath());
- QPointF pos = obj->pointAt(0);
+ QPointF pos = obj->pointAtPercent(0);
QCOMPARE(pos, QPointF(0,0));
- pos = obj->pointAt(.25);
- QCOMPARE(pos.toPoint(), QPoint(39,8)); //fuzzy compare
- pos = obj->pointAt(.75);
- QCOMPARE(pos.toPoint(), QPoint(92,61)); //fuzzy compare
- pos = obj->pointAt(1);
- QCOMPARE(pos, QPointF(100,100));
+ pos = obj->pointAtPercent(.25);
+ compare(pos, scale, __LINE__, 38.9244897744, 7.85853964341);
+ pos = obj->pointAtPercent(.75);
+ compare(pos, scale, __LINE__, 92.141460356592, 61.07551022559);
+ pos = obj->pointAtPercent(1);
+ QCOMPARE(pos, QPointF(100 * scale.width(), 100 * scale.height()));
}
-void tst_QuickPath::angleArc()
+void tst_QuickPath::arc()
+{
+ arc(QSizeF(1,1));
+ arc(QSizeF(2.2,3.4));
+}
+
+void tst_QuickPath::angleArc(QSizeF scale)
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("anglearc.qml"));
QQuickPath *obj = qobject_cast<QQuickPath*>(c.create());
QVERIFY(obj != nullptr);
+ if (scale != QSizeF(1,1))
+ obj->setProperty("scale", scale);
QQmlListReference list(obj, "pathElements");
QCOMPARE(list.count(), 1);
@@ -106,28 +140,35 @@ void tst_QuickPath::angleArc()
QPainterPath path = obj->path();
QVERIFY(path != QPainterPath());
- // using QPoint to do fuzzy compare
- QPointF pos = obj->pointAt(0);
- QCOMPARE(pos.toPoint(), QPoint(135,135));
- pos = obj->pointAt(.25);
- QCOMPARE(pos.toPoint(), QPoint(119,146));
- pos = obj->pointAt(.75);
- QCOMPARE(pos.toPoint(), QPoint(81,146));
- pos = obj->pointAt(1);
- QCOMPARE(pos.toPoint(), QPoint(65,135));
+ QPointF pos = obj->pointAtPercent(0);
+ compare(pos, scale, __LINE__, 135.35533905867, 135.35533905867);
+ pos = obj->pointAtPercent(.25);
+ compare(pos, scale, __LINE__, 119.46222180396, 146.07068621369);
+ pos = obj->pointAtPercent(.75);
+ compare(pos, scale, __LINE__, 80.537778196007, 146.07068621366);
+ pos = obj->pointAtPercent(1);
+ compare(pos, scale, __LINE__, 64.644660941173, 135.35533905867);
// if moveToStart is false, we should have a line starting from startX/Y
arc->setMoveToStart(false);
- pos = obj->pointAt(0);
+ pos = obj->pointAtPercent(0);
QCOMPARE(pos, QPointF(0,0));
}
-void tst_QuickPath::catmullromCurve()
+void tst_QuickPath::angleArc()
+{
+ angleArc(QSizeF(1,1));
+ angleArc(QSizeF(2.7,0.92));
+}
+
+void tst_QuickPath::catmullRomCurve(QSizeF scale, const QVector<QPointF> &points)
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("curve.qml"));
QQuickPath *obj = qobject_cast<QQuickPath*>(c.create());
QVERIFY(obj != nullptr);
+ if (scale != QSizeF(1,1))
+ obj->setProperty("scale", scale);
QCOMPARE(obj->startX(), 0.);
QCOMPARE(obj->startY(), 0.);
@@ -148,22 +189,36 @@ void tst_QuickPath::catmullromCurve()
QPainterPath path = obj->path();
QVERIFY(path != QPainterPath());
- QPointF pos = obj->pointAt(0);
- QCOMPARE(pos, QPointF(0,0));
- pos = obj->pointAt(.25);
- QCOMPARE(pos.toPoint(), QPoint(63,26)); //fuzzy compare
- pos = obj->pointAt(.75);
- QCOMPARE(pos.toPoint(), QPoint(51,105)); //fuzzy compare
- pos = obj->pointAt(1);
- QCOMPARE(pos.toPoint(), QPoint(100,150));
+ QPointF pos = path.pointAtPercent(0);
+ QCOMPARE(pos, points.at(0));
+ pos = path.pointAtPercent(.25);
+ compare(pos, __LINE__, points.at(1));
+ pos = path.pointAtPercent(.75);
+ compare(pos, __LINE__, points.at(2));
+ pos = path.pointAtPercent(1);
+ compare(pos, __LINE__, points.at(3));
+}
+
+void tst_QuickPath::catmullRomCurve()
+{
+ catmullRomCurve(QSizeF(1,1), { QPointF(0,0),
+ QPointF(62.917022919131, 26.175485291549),
+ QPointF(51.194527196674 , 105.27985623074),
+ QPointF(100, 150) });
+ catmullRomCurve(QSizeF(2,5.3), { QPointF(0,0),
+ QPointF(150.80562419914, 170.34065984615),
+ QPointF(109.08400252853 , 588.35165918579),
+ QPointF(200, 795) });
}
-void tst_QuickPath::closedCatmullromCurve()
+void tst_QuickPath::closedCatmullRomCurve(QSizeF scale, const QVector<QPointF> &points)
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("closedcurve.qml"));
QQuickPath *obj = qobject_cast<QQuickPath*>(c.create());
QVERIFY(obj != nullptr);
+ if (scale != QSizeF(1,1))
+ obj->setProperty("scale", scale);
QCOMPARE(obj->startX(), 50.);
QCOMPARE(obj->startY(), 50.);
@@ -181,22 +236,36 @@ void tst_QuickPath::closedCatmullromCurve()
QPainterPath path = obj->path();
QVERIFY(path != QPainterPath());
- QPointF pos = obj->pointAt(0);
- QCOMPARE(pos, QPointF(50,50));
- pos = obj->pointAt(.1);
- QCOMPARE(pos.toPoint(), QPoint(67,56)); //fuzzy compare
- pos = obj->pointAt(.75);
- QCOMPARE(pos.toPoint(), QPoint(44,116)); //fuzzy compare
- pos = obj->pointAt(1);
- QCOMPARE(pos, QPointF(50,50));
+ QPointF pos = path.pointAtPercent(0);
+ QCOMPARE(pos, points.at(0));
+ pos = path.pointAtPercent(.1);
+ compare(pos, __LINE__, points.at(1));
+ pos = path.pointAtPercent(.75);
+ compare(pos, __LINE__, points.at(2));
+ pos = path.pointAtPercent(1);
+ compare(pos, __LINE__, points.at(3));
}
-void tst_QuickPath::svg()
+void tst_QuickPath::closedCatmullRomCurve()
+{
+ closedCatmullRomCurve(QSizeF(1,1), { QPointF(50,50),
+ QPointF(66.776225481812, 55.617435304145),
+ QPointF(44.10269379731 , 116.33512508175),
+ QPointF(50, 50) });
+ closedCatmullRomCurve(QSizeF(2,3), { QPointF(100,150),
+ QPointF(136.49725836178, 170.25466686363),
+ QPointF(87.713232151943 , 328.29232737977),
+ QPointF(100, 150) });
+}
+
+void tst_QuickPath::svg(QSizeF scale)
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("svg.qml"));
QQuickPath *obj = qobject_cast<QQuickPath*>(c.create());
QVERIFY(obj != nullptr);
+ if (scale != QSizeF(1,1))
+ obj->setProperty("scale", scale);
QCOMPARE(obj->startX(), 0.);
QCOMPARE(obj->startY(), 0.);
@@ -211,17 +280,23 @@ void tst_QuickPath::svg()
QPainterPath path = obj->path();
QVERIFY(path != QPainterPath());
- QPointF pos = obj->pointAt(0);
- QCOMPARE(pos, QPointF(200,300));
- pos = obj->pointAt(.25);
- QCOMPARE(pos.toPoint(), QPoint(400,175)); //fuzzy compare
- pos = obj->pointAt(.75);
- QCOMPARE(pos.toPoint(), QPoint(800,425)); //fuzzy compare
- pos = obj->pointAt(1);
- QCOMPARE(pos, QPointF(1000,300));
+ QPointF pos = obj->pointAtPercent(0);
+ QCOMPARE(pos, QPointF(200 * scale.width(),300 * scale.height()));
+ pos = obj->pointAtPercent(.25);
+ QCOMPARE(pos.toPoint(), QPoint(400 * scale.width(),175 * scale.height())); //fuzzy compare
+ pos = obj->pointAtPercent(.75);
+ QCOMPARE(pos.toPoint(), QPoint(800 * scale.width(),425 * scale.height())); //fuzzy compare
+ pos = obj->pointAtPercent(1);
+ QCOMPARE(pos, QPointF(1000 * scale.width(),300 * scale.height()));
}
-void tst_QuickPath::line()
+void tst_QuickPath::svg()
+{
+ svg(QSizeF(1,1));
+ svg(QSizeF(5,3));
+}
+
+void tst_QuickPath::line(QSizeF scale)
{
QQmlEngine engine;
QQmlComponent c1(&engine);
@@ -234,6 +309,8 @@ void tst_QuickPath::line()
QScopedPointer<QObject> o1(c1.create());
QQuickPath *path1 = qobject_cast<QQuickPath *>(o1.data());
QVERIFY(path1);
+ if (scale != QSizeF(1,1))
+ path1->setProperty("scale", scale);
QQmlComponent c2(&engine);
c2.setData(
@@ -246,18 +323,25 @@ void tst_QuickPath::line()
QScopedPointer<QObject> o2(c2.create());
QQuickPath *path2 = qobject_cast<QQuickPath *>(o2.data());
QVERIFY(path2);
+ if (scale != QSizeF(1,1))
+ path2->setProperty("scale", scale);
for (int i = 0; i < 167; ++i) {
qreal t = i / 167.0;
- QPointF p1 = path1->pointAt(t);
+ QPointF p1 = path1->pointAtPercent(t);
QCOMPARE(p1.x(), p1.y());
- QPointF p2 = path2->pointAt(t);
+ QPointF p2 = path2->pointAtPercent(t);
QCOMPARE(p1.toPoint(), p2.toPoint());
}
}
+void tst_QuickPath::line()
+{
+ line(QSizeF(1,1));
+ line(QSizeF(7.23,7.23));
+}
QTEST_MAIN(tst_QuickPath)
diff --git a/tests/auto/quick/qquickpathview/qquickpathview.pro b/tests/auto/quick/qquickpathview/qquickpathview.pro
index f21fb64fa4..5eb24b89bd 100644
--- a/tests/auto/quick/qquickpathview/qquickpathview.pro
+++ b/tests/auto/quick/qquickpathview/qquickpathview.pro
@@ -9,5 +9,5 @@ include (../shared/util.pri)
TESTDATA = data/*
-QT += core-private gui-private qml-private quick-private testlib qmltest
+QT += core-private gui-private qml-private quick-private testlib qmltest qmlmodels-private
qtHaveModule(widgets): QT += widgets
diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
index e2d3253e44..4498548d45 100644
--- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
+++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
@@ -39,7 +39,7 @@
#include <QtQuick/private/qquicktext_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuickTest/QtQuickTest>
-#include <QtQml/private/qqmllistmodel_p.h>
+#include <QtQmlModels/private/qqmllistmodel_p.h>
#include <QtQml/private/qqmlvaluetype_p.h>
#include <QtGui/qstandarditemmodel.h>
#include <QStringListModel>
@@ -242,7 +242,7 @@ void tst_QQuickPathView::items()
QVERIFY(path);
QVERIFY(pathview->highlightItem());
- QPointF start = path->pointAt(0.0);
+ QPointF start = path->pointAtPercent(0.0);
QPointF offset;
offset.setX(pathview->highlightItem()->width()/2);
offset.setY(pathview->highlightItem()->height()/2);
@@ -920,7 +920,7 @@ void tst_QQuickPathView::pathMoved()
QVERIFY(firstItem);
QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path());
QVERIFY(path);
- QPointF start = path->pointAt(0.0);
+ QPointF start = path->pointAtPercent(0.0);
QPointF offset;//Center of item is at point, but pos is from corner
offset.setX(firstItem->width()/2);
offset.setY(firstItem->height()/2);
@@ -929,7 +929,7 @@ void tst_QQuickPathView::pathMoved()
for (int i=0; i<model.count(); i++) {
QQuickRectangle *curItem = findItem<QQuickRectangle>(pathview, "wrapper", i);
- QPointF itemPos(path->pointAt(0.25 + i*0.25));
+ QPointF itemPos(path->pointAtPercent(0.25 + i*0.25));
QCOMPARE(curItem->position() + offset, QPointF(itemPos.x(), itemPos.y()));
}
@@ -1008,7 +1008,7 @@ void tst_QQuickPathView::setCurrentIndex()
QVERIFY(firstItem);
QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path());
QVERIFY(path);
- QPointF start = path->pointAt(0.0);
+ QPointF start = path->pointAtPercent(0.0);
QPointF offset;//Center of item is at point, but pos is from corner
offset.setX(firstItem->width()/2);
offset.setY(firstItem->height()/2);
@@ -1714,7 +1714,7 @@ void tst_QQuickPathView::changePreferredHighlight()
QVERIFY(firstItem);
QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path());
QVERIFY(path);
- QPointF start = path->pointAt(0.5);
+ QPointF start = path->pointAtPercent(0.5);
QPointF offset;//Center of item is at point, but pos is from corner
offset.setX(firstItem->width()/2);
offset.setY(firstItem->height()/2);
@@ -1722,7 +1722,7 @@ void tst_QQuickPathView::changePreferredHighlight()
pathview->setPreferredHighlightBegin(0.8);
pathview->setPreferredHighlightEnd(0.8);
- start = path->pointAt(0.8);
+ start = path->pointAtPercent(0.8);
QTRY_COMPARE(firstItem->position() + offset, start);
QCOMPARE(pathview->currentIndex(), 0);
@@ -1775,7 +1775,7 @@ void tst_QQuickPathView::currentOffsetOnInsertion()
QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path());
QVERIFY(path);
- QPointF start = path->pointAt(0.5);
+ QPointF start = path->pointAtPercent(0.5);
QPointF offset;//Center of item is at point, but pos is from corner
offset.setX(item->width()/2);
offset.setY(item->height()/2);
@@ -1864,7 +1864,7 @@ void tst_QQuickPathView::asynchronous()
QVERIFY(firstItem);
QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path());
QVERIFY(path);
- QPointF start = path->pointAt(0.0);
+ QPointF start = path->pointAtPercent(0.0);
QPointF offset;//Center of item is at point, but pos is from corner
offset.setX(firstItem->width()/2);
offset.setY(firstItem->height()/2);
@@ -1873,7 +1873,7 @@ void tst_QQuickPathView::asynchronous()
for (int i=0; i<5; i++) {
QQuickItem *curItem = findItem<QQuickItem>(pathview, "wrapper", i);
- QPointF itemPos(path->pointAt(0.2 + i*0.2));
+ QPointF itemPos(path->pointAtPercent(0.2 + i*0.2));
QCOMPARE(curItem->position() + offset, itemPos);
}
diff --git a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp
index 80be25d1b0..d3c0f345b9 100644
--- a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp
+++ b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp
@@ -4029,11 +4029,13 @@ QQuickView *tst_qquickpositioners::createView(const QString &filename, bool wait
void tst_qquickpositioners::matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes)
{
+ const QSet<int> expectedIndexSet(expectedIndexes.cbegin(), expectedIndexes.cend());
for (int i=0; i<indexLists.count(); i++) {
- QSet<int> current = indexLists[i].value<QList<int> >().toSet();
- if (current != expectedIndexes.toSet())
+ const auto &currentList = indexLists[i].value<QList<int> >();
+ const QSet<int> current(currentList.cbegin(), currentList.cend());
+ if (current != expectedIndexSet)
qDebug() << "Cannot match actual targets" << current << "with expected" << expectedIndexes;
- QCOMPARE(current, expectedIndexes.toSet());
+ QCOMPARE(current, expectedIndexSet);
}
}
diff --git a/tests/auto/quick/qquickrectangle/data/gradient-preset.qml b/tests/auto/quick/qquickrectangle/data/gradient-preset.qml
index b740bdd610..c046dc4c05 100644
--- a/tests/auto/quick/qquickrectangle/data/gradient-preset.qml
+++ b/tests/auto/quick/qquickrectangle/data/gradient-preset.qml
@@ -10,7 +10,23 @@ Item {
gradient: "NightFade"
}
Rectangle {
- objectName: "invalid"
+ objectName: "invalid1"
gradient: -1
}
+ Rectangle {
+ objectName: "invalid2"
+ gradient: 123456789
+ }
+ Rectangle {
+ objectName: "invalid3"
+ gradient: "NOT_EXISTING"
+ }
+ Rectangle {
+ objectName: "invalid4"
+ gradient: "NumPresets"
+ }
+ Rectangle {
+ objectName: "invalid5"
+ gradient: Gradient.NumPresets
+ }
}
diff --git a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp
index 710caaa734..e4d790f466 100644
--- a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp
+++ b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp
@@ -189,9 +189,11 @@ void tst_qquickrectangle::gradient_preset()
QVERIFY(stringRect->gradient().isString());
QCOMPARE(stringRect->gradient().toString(), QLatin1String("NightFade"));
- QQuickRectangle *invalidRect = view.rootObject()->findChild<QQuickRectangle *>("invalid");
- QVERIFY(invalidRect);
- QVERIFY(invalidRect->gradient().isUndefined());
+ for (int i = 1; i <= 5; ++i) {
+ QQuickRectangle *invalidRect = view.rootObject()->findChild<QQuickRectangle *>(qPrintable(QString("invalid%1").arg(i)));
+ QVERIFY(invalidRect);
+ QVERIFY(invalidRect->gradient().isUndefined());
+ }
}
void tst_qquickrectangle::antialiasing()
diff --git a/tests/auto/quick/qquickrepeater/qquickrepeater.pro b/tests/auto/quick/qquickrepeater/qquickrepeater.pro
index 5554342943..aed5702266 100644
--- a/tests/auto/quick/qquickrepeater/qquickrepeater.pro
+++ b/tests/auto/quick/qquickrepeater/qquickrepeater.pro
@@ -9,4 +9,4 @@ include (../shared/util.pri)
TESTDATA = data/*
-QT += core-private gui-private qml-private quick-private testlib
+QT += core-private gui-private qml-private quick-private testlib qmlmodels-private
diff --git a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp
index e4b427f6ec..65e7d29595 100644
--- a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp
+++ b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp
@@ -35,8 +35,8 @@
#include <QtQml/qqmlincubator.h>
#include <private/qquickrepeater_p.h>
#include <QtQuick/private/qquicktext_p.h>
-#include <QtQml/private/qqmllistmodel_p.h>
-#include <QtQml/private/qqmlobjectmodel_p.h>
+#include <QtQmlModels/private/qqmllistmodel_p.h>
+#include <QtQmlModels/private/qqmlobjectmodel_p.h>
#include <QtGui/qstandarditemmodel.h>
#include "../../shared/util.h"
@@ -899,15 +899,15 @@ void tst_QQuickRepeater::destroyCount()
QQuickRepeater *repeater = findItem<QQuickRepeater>(rootObject, "repeater");
QVERIFY(repeater);
- repeater->setProperty("model", qVariantFromValue<int>(3));
+ repeater->setProperty("model", QVariant::fromValue<int>(3));
QCOMPARE(repeater->property("componentCount").toInt(), 3);
- repeater->setProperty("model", qVariantFromValue<int>(0));
+ repeater->setProperty("model", QVariant::fromValue<int>(0));
QCOMPARE(repeater->property("componentCount").toInt(), 0);
- repeater->setProperty("model", qVariantFromValue<int>(4));
+ repeater->setProperty("model", QVariant::fromValue<int>(4));
QCOMPARE(repeater->property("componentCount").toInt(), 4);
QStringListModel model;
- repeater->setProperty("model", qVariantFromValue<QStringListModel *>(&model));
+ repeater->setProperty("model", QVariant::fromValue<QStringListModel *>(&model));
QCOMPARE(repeater->property("componentCount").toInt(), 0);
QStringList list;
list << "1" << "2" << "3" << "4";
@@ -915,7 +915,7 @@ void tst_QQuickRepeater::destroyCount()
QCOMPARE(repeater->property("componentCount").toInt(), 4);
model.insertRows(2,1);
QModelIndex index = model.index(2);
- model.setData(index, qVariantFromValue<QString>(QStringLiteral("foobar")));
+ model.setData(index, QVariant::fromValue<QString>(QStringLiteral("foobar")));
QCOMPARE(repeater->property("componentCount").toInt(), 5);
model.removeRows(2,1);
diff --git a/tests/auto/quick/qquickshape/BLACKLIST b/tests/auto/quick/qquickshape/BLACKLIST
deleted file mode 100644
index d0ebc2f505..0000000000
--- a/tests/auto/quick/qquickshape/BLACKLIST
+++ /dev/null
@@ -1,8 +0,0 @@
-[render]
-osx ci
-[renderWithMultipleSp]
-osx ci
-[radialGrad]
-osx ci
-[conicalGrad]
-osx ci
diff --git a/tests/auto/quick/qquickshape/data/multiline.png b/tests/auto/quick/qquickshape/data/multiline.png
new file mode 100644
index 0000000000..ae25d111df
--- /dev/null
+++ b/tests/auto/quick/qquickshape/data/multiline.png
Binary files differ
diff --git a/tests/auto/quick/qquickshape/data/multiline.qml b/tests/auto/quick/qquickshape/data/multiline.qml
new file mode 100644
index 0000000000..ad07506972
--- /dev/null
+++ b/tests/auto/quick/qquickshape/data/multiline.qml
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+import QtQuick.Shapes 1.14
+
+Shape {
+ width: 200
+ height: 150
+ vendorExtensionsEnabled: false
+ objectName: "shape"
+ id: shape
+ property alias paths: multiline.paths
+ property point vertexBeingChecked;
+
+ function checkVertexAt(i, j) {
+ vertexBeingChecked = multiline.paths[i][j]
+ }
+
+ ShapePath {
+ strokeWidth: 4
+ strokeColor: "green"
+ PathMultiline {
+ id: multiline
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickshape/data/multilineStronglyTyped.qml b/tests/auto/quick/qquickshape/data/multilineStronglyTyped.qml
new file mode 100644
index 0000000000..46d650e053
--- /dev/null
+++ b/tests/auto/quick/qquickshape/data/multilineStronglyTyped.qml
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+import QtQuick.Shapes 1.14
+import Qt.test 1.0
+
+Shape {
+ width: 200
+ height: 150
+ vendorExtensionsEnabled: false
+ objectName: "shape"
+ id: shape
+ property alias paths: multiline.paths
+ property point vertexBeingChecked;
+
+ function checkVertexAt(i, j) {
+ vertexBeingChecked = multiline.paths[i][j]
+ }
+
+ PolygonProvider {
+ id: provider
+ objectName: "provider"
+ }
+
+ ShapePath {
+ strokeWidth: 4
+ strokeColor: "green"
+ PathMultiline {
+ id: multiline
+ paths: provider.paths
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickshape/data/pathitem7.qml b/tests/auto/quick/qquickshape/data/pathitem7.qml
new file mode 100644
index 0000000000..b3ef47a4dd
--- /dev/null
+++ b/tests/auto/quick/qquickshape/data/pathitem7.qml
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+import tst_qquickpathitem 1.0
+
+Item {
+ id: item
+ width: 200
+ height: 150
+
+ Shape {
+ vendorExtensionsEnabled: false
+ objectName: "shape"
+ id: shape
+ anchors.fill: parent
+
+ ShapePath {
+ strokeWidth: 4
+ strokeColor: "red"
+ scale: Qt.size(shape.width - 1, shape.height - 1)
+ fillGradient: LinearGradient {
+ x1: 20; y1: 20
+ x2: 180; y2: 130
+ GradientStop { position: 0; color: "blue" }
+ GradientStop { position: 0.2; color: "green" }
+ GradientStop { position: 0.4; color: "red" }
+ GradientStop { position: 0.6; color: "yellow" }
+ GradientStop { position: 1; color: "cyan" }
+ }
+ strokeStyle: ShapePath.DashLine
+ dashPattern: [ 1, 4 ]
+ startX: 20; startY: 20 // unnecessary, PathPolyline moves to the first vertex.
+ PathPolyline {
+ path: [ Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)),
+ Qt.point(180.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)),
+ Qt.point(20.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)),
+ Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)) ]
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickshape/data/pathitem8.png b/tests/auto/quick/qquickshape/data/pathitem8.png
new file mode 100644
index 0000000000..ee585958ec
--- /dev/null
+++ b/tests/auto/quick/qquickshape/data/pathitem8.png
Binary files differ
diff --git a/tests/auto/quick/qquickshape/data/pathitem8.qml b/tests/auto/quick/qquickshape/data/pathitem8.qml
new file mode 100644
index 0000000000..9789ff90e0
--- /dev/null
+++ b/tests/auto/quick/qquickshape/data/pathitem8.qml
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+import QtQuick.Shapes 1.14
+
+Item {
+ id: item
+ width: 200
+ height: 150
+
+ Shape {
+ vendorExtensionsEnabled: false
+ objectName: "shape"
+ id: shape
+ anchors.fill: parent
+
+ ShapePath {
+ strokeWidth: 4
+ strokeColor: "red"
+ scale: Qt.size(shape.width - 1, shape.height - 1)
+ fillGradient: LinearGradient {
+ x1: 20; y1: 20
+ x2: 180; y2: 130
+ GradientStop { position: 0; color: "blue" }
+ GradientStop { position: 0.2; color: "green" }
+ GradientStop { position: 0.4; color: "red" }
+ GradientStop { position: 0.6; color: "yellow" }
+ GradientStop { position: 1; color: "cyan" }
+ }
+ strokeStyle: ShapePath.DashLine
+ dashPattern: [ 1, 4 ]
+ PathMultiline {
+ paths: [[Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)),
+ Qt.point(180.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)),
+ Qt.point(20.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)),
+ Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)) ],
+ [Qt.point( 0.45 , 0.67 ),
+ Qt.point( 0.414906666468 , 0.573581858547 ),
+ Qt.point( 0.32604722665 , 0.522278837048 ),
+ Qt.point( 0.225 , 0.540096189432 ),
+ Qt.point( 0.159046106882 , 0.618696978501 ),
+ Qt.point( 0.159046106882 , 0.721303021499 ),
+ Qt.point( 0.225 , 0.799903810568 ),
+ Qt.point( 0.32604722665 , 0.817721162952 ),
+ Qt.point( 0.414906666468 , 0.766418141453 ),
+ Qt.point( 0.45 , 0.67 ),
+ ],
+ [Qt.point( 0.69 , 0.75 ),
+ Qt.point( 0.668943999881 , 0.692149115128 ),
+ Qt.point( 0.61562833599 , 0.661367302229 ),
+ Qt.point( 0.555 , 0.672057713659 ),
+ Qt.point( 0.515427664129 , 0.719218187101 ),
+ Qt.point( 0.515427664129 , 0.780781812899 ),
+ Qt.point( 0.555 , 0.827942286341 ),
+ Qt.point( 0.61562833599 , 0.838632697771 ),
+ Qt.point( 0.668943999881 , 0.807850884872 ),
+ Qt.point( 0.69 , 0.75 ),
+ ]]
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickshape/data/polyline.png b/tests/auto/quick/qquickshape/data/polyline.png
new file mode 100644
index 0000000000..00123fba44
--- /dev/null
+++ b/tests/auto/quick/qquickshape/data/polyline.png
Binary files differ
diff --git a/tests/auto/quick/qquickshape/data/polyline.qml b/tests/auto/quick/qquickshape/data/polyline.qml
new file mode 100644
index 0000000000..e62d952ae7
--- /dev/null
+++ b/tests/auto/quick/qquickshape/data/polyline.qml
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+import QtQuick.Shapes 1.14
+
+Shape {
+ width: 200
+ height: 150
+ vendorExtensionsEnabled: false
+ objectName: "shape"
+ id: shape
+ property alias path: polyline.path
+ property point vertexBeingChecked;
+
+ function checkVertexAt(i) {
+ vertexBeingChecked = polyline.path[i]
+ }
+
+ ShapePath {
+ strokeWidth: 4
+ strokeColor: "green"
+ PathPolyline {
+ id: polyline
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickshape/qquickshape.pro b/tests/auto/quick/qquickshape/qquickshape.pro
index a0e5c002e0..3cf79426c5 100644
--- a/tests/auto/quick/qquickshape/qquickshape.pro
+++ b/tests/auto/quick/qquickshape/qquickshape.pro
@@ -8,6 +8,7 @@ include (../../shared/util.pri)
include (../shared/util.pri)
TESTDATA = data/*
+DISTFILES = data/*
QT += core-private gui-private qml-private quick-private testlib quickshapes-private
qtHaveModule(widgets): QT += widgets
diff --git a/tests/auto/quick/qquickshape/tst_qquickshape.cpp b/tests/auto/quick/qquickshape/tst_qquickshape.cpp
index 61fb260612..851475e2cd 100644
--- a/tests/auto/quick/qquickshape/tst_qquickshape.cpp
+++ b/tests/auto/quick/qquickshape/tst_qquickshape.cpp
@@ -1,6 +1,6 @@
-/****************************************************************************
+/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
@@ -34,6 +34,7 @@
#include <QtQml/qqmlexpression.h>
#include <QtQml/qqmlincubator.h>
#include <QtQuickShapes/private/qquickshape_p.h>
+#include <QStandardPaths>
#include "../../shared/util.h"
#include "../shared/viewtestutil.h"
@@ -42,6 +43,28 @@
using namespace QQuickViewTestUtil;
using namespace QQuickVisualTestUtil;
+class PolygonProvider : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QVector<QPolygonF> paths READ paths WRITE setPaths NOTIFY pathsChanged)
+
+public:
+ QVector<QPolygonF> paths() const { return m_paths; }
+ void setPaths(QVector<QPolygonF> paths)
+ {
+ if (m_paths == paths)
+ return;
+ m_paths = paths;
+ emit pathsChanged();
+ }
+
+signals:
+ void pathsChanged();
+
+private:
+ QVector<QPolygonF> m_paths;
+};
+
class tst_QQuickShape : public QQmlDataTest
{
Q_OBJECT
@@ -57,6 +80,16 @@ private slots:
void renderWithMultipleSp();
void radialGrad();
void conicalGrad();
+ void renderPolyline();
+ void renderMultiline();
+ void polylineDataTypes_data();
+ void polylineDataTypes();
+ void multilineDataTypes_data();
+ void multilineDataTypes();
+ void multilineStronglyTyped();
+
+private:
+ QVector<QPolygonF> m_lowPolyLogo;
};
tst_QQuickShape::tst_QQuickShape()
@@ -66,11 +99,36 @@ tst_QQuickShape::tst_QQuickShape()
const char *uri = "tst_qquickpathitem";
qmlRegisterType<QQuickShape>(uri, 1, 0, "Shape");
- qmlRegisterType<QQuickShapePath>(uri, 1, 0, "ShapePath");
+ qmlRegisterType<QQuickShapePath, 14>(uri, 1, 0, "ShapePath");
qmlRegisterUncreatableType<QQuickShapeGradient>(uri, 1, 0, "ShapeGradient", QQuickShapeGradient::tr("ShapeGradient is an abstract base class"));
qmlRegisterType<QQuickShapeLinearGradient>(uri, 1, 0, "LinearGradient");
qmlRegisterType<QQuickShapeRadialGradient>(uri, 1, 0, "RadialGradient");
qmlRegisterType<QQuickShapeConicalGradient>(uri, 1, 0, "ConicalGradient");
+ qmlRegisterType<QQuickPathPolyline>(uri, 1, 0, "PathPolyline");
+ qmlRegisterType<PolygonProvider>("Qt.test", 1, 0, "PolygonProvider");
+
+ m_lowPolyLogo << (QPolygonF() <<
+ QPointF(20, 0) <<
+ QPointF(140, 0) <<
+ QPointF(140, 80) <<
+ QPointF(120, 100) <<
+ QPointF(0, 100) <<
+ QPointF(0, 20) <<
+ QPointF(20, 0) )
+ << (QPolygonF() << QPointF(20, 80) <<
+ QPointF(60, 80) <<
+ QPointF(80, 60) <<
+ QPointF(80, 20) <<
+ QPointF(40, 20) <<
+ QPointF(20, 40) <<
+ QPointF(20, 80) )
+ << (QPolygonF() << QPointF(80, 80) <<
+ QPointF(70, 70) )
+ << (QPolygonF() << QPointF(120, 80) <<
+ QPointF(100, 60) <<
+ QPointF(100, 20) )
+ << (QPolygonF() << QPointF(100, 40) <<
+ QPointF(120, 40) );
}
void tst_QQuickShape::initValues()
@@ -311,6 +369,344 @@ void tst_QQuickShape::conicalGrad()
qPrintable(errorMessage));
}
+void tst_QQuickShape::renderPolyline()
+{
+ QScopedPointer<QQuickView> window(createView());
+
+ window->setSource(testFileUrl("pathitem7.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
+ || (QGuiApplication::platformName() == QLatin1String("minimal")))
+ QEXPECT_FAIL("", "Failure due to grabWindow not functional on offscreen/minimimal platforms", Abort);
+
+ QImage img = window->grabWindow();
+ QVERIFY(!img.isNull());
+
+ QImage refImg(testFileUrl("pathitem3.png").toLocalFile()); // It's a recreation of pathitem3 using PathPolyline
+ QVERIFY(!refImg.isNull());
+
+ QString errorMessage;
+ const QImage actualImg = img.convertToFormat(refImg.format());
+ const bool res = QQuickVisualTestUtil::compareImages(actualImg, refImg, &errorMessage);
+ if (!res) { // For visual inspection purposes.
+ QTest::qWait(5000);
+ const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
+ actualImg.save(tempLocation + QLatin1String("/pathitem7.png"));
+ }
+ QVERIFY2(res, qPrintable(errorMessage));
+}
+
+void tst_QQuickShape::renderMultiline()
+{
+ QScopedPointer<QQuickView> window(createView());
+
+ window->setSource(testFileUrl("pathitem8.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
+ || (QGuiApplication::platformName() == QLatin1String("minimal")))
+ QEXPECT_FAIL("", "Failure due to grabWindow not functional on offscreen/minimimal platforms", Abort);
+
+ QImage img = window->grabWindow();
+ QVERIFY(!img.isNull());
+
+ QImage refImg(testFileUrl("pathitem8.png").toLocalFile());
+ QVERIFY(!refImg.isNull());
+
+ QString errorMessage;
+ const QImage actualImg = img.convertToFormat(refImg.format());
+ const bool res = QQuickVisualTestUtil::compareImages(actualImg, refImg, &errorMessage);
+ if (!res) { // For visual inspection purposes.
+ QTest::qWait(5000);
+ const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
+ actualImg.save(tempLocation + QLatin1String("/pathitem8.png"));
+ }
+ QVERIFY2(res, qPrintable(errorMessage));
+}
+
+void tst_QQuickShape::polylineDataTypes_data()
+{
+ QTest::addColumn<QVariant>("path");
+
+ QTest::newRow("polygon") << QVariant::fromValue(m_lowPolyLogo.first());
+ {
+ QVector<QPointF> points;
+ points << m_lowPolyLogo.first();
+ QTest::newRow("vector of points") << QVariant::fromValue(points);
+ }
+ {
+ QList<QPointF> points;
+ for (const auto &point : m_lowPolyLogo.first())
+ points << point;
+ QTest::newRow("list of points") << QVariant::fromValue(points);
+ }
+ {
+ QVariantList points;
+ for (const auto &point : m_lowPolyLogo.first())
+ points << point;
+ QTest::newRow("QVariantList of points") << QVariant::fromValue(points);
+ }
+ {
+ QVector<QPoint> points;
+ for (const auto &point : m_lowPolyLogo.first())
+ points << point.toPoint();
+ QTest::newRow("vector of QPoint (integer points)") << QVariant::fromValue(points);
+ }
+ // Oddly, QPolygon is not supported, even though it's really QVector<QPoint>.
+ // We don't want to have a special case for it in QQuickPathPolyline::setPath(),
+ // but it could potentially be supported by fixing one of the QVariant conversions.
+}
+
+void tst_QQuickShape::polylineDataTypes()
+{
+ QFETCH(QVariant, path);
+
+ QScopedPointer<QQuickView> window(createView());
+ window->setSource(testFileUrl("polyline.qml"));
+ QQuickShape *shape = qobject_cast<QQuickShape *>(window->rootObject());
+ QVERIFY(shape);
+ shape->setProperty("path", path);
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
+ || (QGuiApplication::platformName() == QLatin1String("minimal")))
+ QEXPECT_FAIL("", "Failure due to grabWindow not functional on offscreen/minimimal platforms", Abort);
+
+ QImage img = window->grabWindow();
+ QVERIFY(!img.isNull());
+
+ QImage refImg(testFileUrl("polyline.png").toLocalFile());
+ QVERIFY(!refImg.isNull());
+
+ QString errorMessage;
+ const QImage actualImg = img.convertToFormat(refImg.format());
+ const bool res = QQuickVisualTestUtil::compareImages(actualImg, refImg, &errorMessage);
+ if (!res) { // For visual inspection purposes.
+ QTest::qWait(5000);
+ const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
+ actualImg.save(tempLocation + QLatin1String("/polyline.png"));
+ }
+ QVERIFY2(res, qPrintable(errorMessage));
+
+ QCOMPARE(shape->property("path").value<QVector<QPointF>>(), m_lowPolyLogo.first());
+ // Verify that QML sees it as an array of points
+ int i = 0;
+ for (QPointF p : m_lowPolyLogo.first()) {
+ QMetaObject::invokeMethod(shape, "checkVertexAt", Q_ARG(QVariant, QVariant::fromValue<int>(i++)));
+ QCOMPARE(shape->property("vertexBeingChecked").toPointF(), p);
+ }
+}
+
+void tst_QQuickShape::multilineDataTypes_data()
+{
+ QTest::addColumn<QVariant>("paths");
+
+ QTest::newRow("vector of polygons") << QVariant::fromValue(m_lowPolyLogo);
+ {
+ QVector<QVector<QPointF>> paths;
+ for (const auto &poly : m_lowPolyLogo) {
+ QVector<QPointF> points;
+ points << poly;
+ paths << points;
+ }
+ QTest::newRow("vector of point vectors") << QVariant::fromValue(paths);
+ }
+ {
+ QList<QVector<QPointF>> paths;
+ for (const auto &poly : m_lowPolyLogo) {
+ QVector<QPointF> points;
+ points << poly;
+ paths << points;
+ }
+ QTest::newRow("list of point vectors") << QVariant::fromValue(paths);
+ }
+ {
+ QList<QList<QPointF>> paths;
+ for (const auto &poly : m_lowPolyLogo) {
+ QList<QPointF> points;
+ for (const auto &point : poly)
+ points << point;
+ paths << points;
+ }
+ QTest::newRow("list of point lists") << QVariant::fromValue(paths);
+ }
+ {
+ QVariantList paths;
+ for (const auto &poly : m_lowPolyLogo) {
+ QVector<QPointF> points;
+ points << poly;
+ paths << QVariant::fromValue(points);
+ }
+ QTest::newRow("QVariantList of point vectors") << QVariant::fromValue(paths);
+ }
+ {
+ QVariantList paths;
+ for (const auto &poly : m_lowPolyLogo) {
+ QList<QPointF> points;
+ for (const auto &point : poly)
+ points << point;
+ paths << QVariant::fromValue(points);
+ }
+ QTest::newRow("QVariantList of point lists") << QVariant::fromValue(paths);
+ }
+ {
+ QVariantList paths;
+ for (const auto &poly : m_lowPolyLogo) {
+ QVariantList points;
+ for (const auto &point : poly)
+ points << point;
+ paths << QVariant::fromValue(points);
+ }
+ QTest::newRow("QVariantList of QVariantLists") << QVariant::fromValue(paths);
+ }
+ /* These could be supported if QVariant knew how to convert lists and vectors of QPolygon to QPolygonF.
+ But they are omitted for now because we don't want to have special cases for them
+ in QQuickPathMultiline::setPaths(). Floating point is preferred for geometry in Qt Quick.
+ {
+ QList<QPolygon> paths;
+ for (const auto &poly : m_lowPolyLogo)
+ paths << poly.toPolygon();
+ QTest::newRow("list of QPolygon (integer points)") << QVariant::fromValue(paths);
+ }
+ {
+ QVector<QPolygon> paths;
+ for (const auto &poly : m_lowPolyLogo)
+ paths << poly.toPolygon();
+ QTest::newRow("vector of QPolygon (integer points)") << QVariant::fromValue(paths);
+ }
+ */
+ {
+ QList<QList<QPoint>> paths;
+ for (const auto &poly : m_lowPolyLogo) {
+ QList<QPoint> points;
+ for (const auto &point : poly)
+ points << point.toPoint();
+ paths << points;
+ }
+ QTest::newRow("list of integer point lists") << QVariant::fromValue(paths);
+ }
+ {
+ QVector<QList<QPoint>> paths;
+ for (const auto &poly : m_lowPolyLogo) {
+ QList<QPoint> points;
+ for (const auto &point : poly)
+ points << point.toPoint();
+ paths << points;
+ }
+ QTest::newRow("vector of integer point lists") << QVariant::fromValue(paths);
+ }
+ {
+ QList<QVector<QPoint>> paths;
+ for (const auto &poly : m_lowPolyLogo) {
+ QVector<QPoint> points;
+ for (const auto &point : poly)
+ points << point.toPoint();
+ paths << points;
+ }
+ QTest::newRow("list of integer point vectors") << QVariant::fromValue(paths);
+ }
+}
+
+void tst_QQuickShape::multilineDataTypes()
+{
+ QFETCH(QVariant, paths);
+
+ QScopedPointer<QQuickView> window(createView());
+ window->setSource(testFileUrl("multiline.qml"));
+ QQuickShape *shape = qobject_cast<QQuickShape *>(window->rootObject());
+ QVERIFY(shape);
+ shape->setProperty("paths", paths);
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
+ || (QGuiApplication::platformName() == QLatin1String("minimal")))
+ QEXPECT_FAIL("", "Failure due to grabWindow not functional on offscreen/minimimal platforms", Abort);
+
+ QImage img = window->grabWindow();
+ QVERIFY(!img.isNull());
+
+ QImage refImg(testFileUrl("multiline.png").toLocalFile());
+ QVERIFY(!refImg.isNull());
+
+ QString errorMessage;
+ const QImage actualImg = img.convertToFormat(refImg.format());
+ const bool res = QQuickVisualTestUtil::compareImages(actualImg, refImg, &errorMessage);
+ if (!res) { // For visual inspection purposes.
+ QTest::qWait(5000);
+ const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
+ actualImg.save(tempLocation + QLatin1String("/multiline.png"));
+ }
+ QVERIFY2(res, qPrintable(errorMessage));
+
+ QVector<QVector<QPointF>> pointVectors;
+ for (auto v : m_lowPolyLogo)
+ pointVectors << v;
+ QCOMPARE(shape->property("paths").value<QVector<QVector<QPointF>>>(), pointVectors);
+ // Verify that QML sees it as an array of arrays of points
+ int i = 0;
+ for (auto pv : m_lowPolyLogo) {
+ int j = 0;
+ for (QPointF p : pv) {
+ QMetaObject::invokeMethod(shape, "checkVertexAt", Q_ARG(QVariant, QVariant::fromValue<int>(i)), Q_ARG(QVariant, QVariant::fromValue<int>(j++)));
+ QCOMPARE(shape->property("vertexBeingChecked").toPointF(), p);
+ }
+ ++i;
+ }
+}
+
+void tst_QQuickShape::multilineStronglyTyped()
+{
+ QScopedPointer<QQuickView> window(createView());
+ window->setSource(testFileUrl("multilineStronglyTyped.qml"));
+ QQuickShape *shape = qobject_cast<QQuickShape *>(window->rootObject());
+ QVERIFY(shape);
+ PolygonProvider *provider = shape->findChild<PolygonProvider*>("provider");
+ QVERIFY(provider);
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+ provider->setPaths(m_lowPolyLogo);
+
+ if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
+ || (QGuiApplication::platformName() == QLatin1String("minimal")))
+ QEXPECT_FAIL("", "Failure due to grabWindow not functional on offscreen/minimimal platforms", Abort);
+
+ QImage img = window->grabWindow();
+ QVERIFY(!img.isNull());
+
+ QImage refImg(testFileUrl("multiline.png").toLocalFile());
+ QVERIFY(!refImg.isNull());
+
+ QString errorMessage;
+ const QImage actualImg = img.convertToFormat(refImg.format());
+ const bool res = QQuickVisualTestUtil::compareImages(actualImg, refImg, &errorMessage);
+ if (!res) { // For visual inspection purposes.
+ QTest::qWait(5000);
+ const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
+ actualImg.save(tempLocation + QLatin1String("/multilineStronglyTyped.png"));
+ }
+ QVERIFY2(res, qPrintable(errorMessage));
+
+ QVector<QVector<QPointF>> pointVectors;
+ for (auto v : m_lowPolyLogo)
+ pointVectors << v;
+ QCOMPARE(shape->property("paths").value<QVector<QVector<QPointF>>>(), pointVectors);
+ // Verify that QML sees it as an array of arrays of points
+ int i = 0;
+ for (auto pv : m_lowPolyLogo) {
+ int j = 0;
+ for (QPointF p : pv) {
+ QMetaObject::invokeMethod(shape, "checkVertexAt", Q_ARG(QVariant, QVariant::fromValue<int>(i)), Q_ARG(QVariant, QVariant::fromValue<int>(j++)));
+ QCOMPARE(shape->property("vertexBeingChecked").toPointF(), p);
+ }
+ ++i;
+ }
+}
+
QTEST_MAIN(tst_QQuickShape)
#include "tst_qquickshape.moc"
diff --git a/tests/auto/quick/qquickstates/data/trivialWhen.qml b/tests/auto/quick/qquickstates/data/trivialWhen.qml
new file mode 100644
index 0000000000..9f7f3161e9
--- /dev/null
+++ b/tests/auto/quick/qquickstates/data/trivialWhen.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.12
+
+State {
+ when: true
+}
diff --git a/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
index 50554f6333..1eb797f54f 100644
--- a/tests/auto/quick/qquickstates/tst_qquickstates.cpp
+++ b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
@@ -138,6 +138,7 @@ private slots:
void QTBUG_38492();
void revertListMemoryLeak();
void duplicateStateName();
+ void trivialWhen();
};
void tst_qquickstates::initTestCase()
@@ -1665,6 +1666,15 @@ void tst_qquickstates::duplicateStateName()
QVERIFY(!item.isNull());
}
+// QTBUG-76838
+void tst_qquickstates::trivialWhen()
+{
+ QQmlEngine engine;
+
+ QQmlComponent c(&engine, testFileUrl("trivialWhen.qml"));
+ QVERIFY(c.create());
+}
+
QTEST_MAIN(tst_qquickstates)
diff --git a/tests/auto/quick/qquicktableview/data/syncviewsimple.qml b/tests/auto/quick/qquicktableview/data/syncviewsimple.qml
new file mode 100644
index 0000000000..012cceaa9d
--- /dev/null
+++ b/tests/auto/quick/qquicktableview/data/syncviewsimple.qml
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+import QtQuick.Window 2.3
+
+Item {
+ width: 640
+ height: 450
+
+ property alias tableView: mainTableView
+ property alias tableViewH: tableViewH
+ property alias tableViewV: tableViewV
+ property alias tableViewHV: tableViewHV
+
+ Column {
+ spacing: 10
+ TableView {
+ id: tableViewH
+ objectName: "Hor"
+ width: 600
+ height: 100
+ anchors.margins: 1
+ clip: true
+ delegate: tableViewDelegate
+ syncView: mainTableView
+ syncDirection: Qt.Horizontal
+ }
+
+ TableView {
+ id: tableViewV
+ objectName: "Ver"
+ width: 600
+ height: 100
+ anchors.margins: 1
+ clip: true
+ delegate: tableViewDelegate
+ syncView: mainTableView
+ syncDirection: Qt.Vertical
+ }
+
+ TableView {
+ id: tableViewHV
+ objectName: "HorVer"
+ width: 600
+ height: 100
+ anchors.margins: 1
+ clip: true
+ delegate: tableViewDelegate
+ syncView: mainTableView
+ }
+
+ TableView {
+ id: mainTableView
+ objectName: "root"
+ width: 600
+ height: 100
+ anchors.margins: 1
+ clip: true
+ delegate: tableViewDelegate
+ columnSpacing: 1
+ rowSpacing: 1
+
+ columnWidthProvider: function(c) { return 50 + c }
+ rowHeightProvider: function(r) { return 25 + r }
+ }
+ }
+
+ Component {
+ id: tableViewDelegate
+ Rectangle {
+ objectName: "tableViewDelegate"
+ color: "lightgray"
+ border.width: 1
+ implicitWidth: 30
+ implicitHeight: 60
+
+ Text {
+ anchors.centerIn: parent
+ font.pixelSize: 10
+ text: parent.TableView.view.objectName + "\n" + column + ", " + row
+ }
+ }
+ }
+
+}
diff --git a/tests/auto/quick/qquicktableview/qquicktableview.pro b/tests/auto/quick/qquicktableview/qquicktableview.pro
index cf831ed5b5..da0c0b01d0 100644
--- a/tests/auto/quick/qquicktableview/qquicktableview.pro
+++ b/tests/auto/quick/qquicktableview/qquicktableview.pro
@@ -11,5 +11,5 @@ include (../shared/util.pri)
TESTDATA = data/*
-QT += core-private gui-private qml-private quick-private testlib
+QT += core-private gui-private qml-private quick-private testlib qmlmodels-private
diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
index acb475b1c5..889175a228 100644
--- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
+++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
@@ -38,8 +38,8 @@
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlexpression.h>
#include <QtQml/qqmlincubator.h>
-#include <QtQml/private/qqmlobjectmodel_p.h>
-#include <QtQml/private/qqmllistmodel_p.h>
+#include <QtQmlModels/private/qqmlobjectmodel_p.h>
+#include <QtQmlModels/private/qqmllistmodel_p.h>
#include "testmodel.h"
@@ -50,24 +50,23 @@
using namespace QQuickViewTestUtil;
using namespace QQuickVisualTestUtil;
-static const char* kTableViewPropName = "tableView";
static const char* kDelegateObjectName = "tableViewDelegate";
static const char *kDelegatesCreatedCountProp = "delegatesCreatedCount";
static const char *kModelDataBindingProp = "modelDataBinding";
Q_DECLARE_METATYPE(QMarginsF);
-#define DECLARE_TABLEVIEW_VARIABLES \
- auto tableView = view->rootObject()->property(kTableViewPropName).value<QQuickTableView *>(); \
- QVERIFY(tableView); \
- auto tableViewPrivate = QQuickTableViewPrivate::get(tableView); \
- Q_UNUSED(tableViewPrivate)
+#define GET_QML_TABLEVIEW(PROPNAME) \
+ auto PROPNAME = view->rootObject()->property(#PROPNAME).value<QQuickTableView *>(); \
+ QVERIFY(PROPNAME); \
+ auto PROPNAME ## Private = QQuickTableViewPrivate::get(PROPNAME); \
+ Q_UNUSED(PROPNAME ## Private)
#define LOAD_TABLEVIEW(fileName) \
view->setSource(testFileUrl(fileName)); \
view->show(); \
QVERIFY(QTest::qWaitForWindowActive(view)); \
- DECLARE_TABLEVIEW_VARIABLES
+ GET_QML_TABLEVIEW(tableView)
#define LOAD_TABLEVIEW_ASYNC(fileName) \
view->setSource(testFileUrl("asyncloader.qml")); \
@@ -77,11 +76,12 @@ Q_DECLARE_METATYPE(QMarginsF);
loader->setSource(QUrl::fromLocalFile("data/" fileName)); \
QTRY_VERIFY(loader->item()); \
QCOMPARE(loader->status(), QQuickLoader::Status::Ready); \
- DECLARE_TABLEVIEW_VARIABLES
+ GET_QML_TABLEVIEW(tableView)
-#define WAIT_UNTIL_POLISHED \
- QVERIFY(QQuickTest::qIsPolishScheduled(tableView)); \
- QVERIFY(QQuickTest::qWaitForItemPolished(tableView))
+#define WAIT_UNTIL_POLISHED_ARG(item) \
+ QVERIFY(QQuickTest::qIsPolishScheduled(item)); \
+ QVERIFY(QQuickTest::qWaitForItemPolished(item))
+#define WAIT_UNTIL_POLISHED WAIT_UNTIL_POLISHED_ARG(tableView)
class tst_QQuickTableView : public QQmlDataTest
{
@@ -123,6 +123,9 @@ private slots:
void checkContentWidthAndHeight();
void checkPageFlicking();
void checkExplicitContentWidthAndHeight();
+ void checkExtents_origin();
+ void checkExtents_endExtent();
+ void checkExtents_moveTableToEdge();
void checkContentXY();
void noDelegate();
void changeDelegateDuringUpdate();
@@ -163,6 +166,13 @@ private slots:
void hideRowsAndColumns_data();
void hideRowsAndColumns();
void checkThatRevisionedPropertiesCannotBeUsedInOldImports();
+ void checkSyncView_rootView_data();
+ void checkSyncView_rootView();
+ void checkSyncView_childViews_data();
+ void checkSyncView_childViews();
+ void checkSyncView_differentSizedModels();
+ void checkSyncView_connect_late_data();
+ void checkSyncView_connect_late();
};
tst_QQuickTableView::tst_QQuickTableView()
@@ -579,7 +589,7 @@ void tst_QQuickTableView::checkForceLayoutEndUpDoingALayout()
void tst_QQuickTableView::checkContentWidthAndHeight()
{
- // Check that contentWidth/Height reports the correct size of the the
+ // Check that contentWidth/Height reports the correct size of the
// table, based on knowledge of the rows and columns that has been loaded.
LOAD_TABLEVIEW("contentwidthheight.qml");
@@ -590,11 +600,7 @@ void tst_QQuickTableView::checkContentWidthAndHeight()
const int tableSize = 100;
const int cellSizeSmall = 100;
- const int cellSizeLarge = 200;
const int spacing = 1;
- const int smallCellCount = 20;
- const int largeCellCount = tableSize - smallCellCount;
- const qreal accumulatedSpacing = ((tableSize - 1) * spacing);
auto model = TestModelAsVariant(tableSize, tableSize);
tableView->setModel(model);
@@ -607,72 +613,12 @@ void tst_QQuickTableView::checkContentWidthAndHeight()
QCOMPARE(tableViewPrivate->averageEdgeSize.width(), cellSizeSmall);
QCOMPARE(tableViewPrivate->averageEdgeSize.height(), cellSizeSmall);
- // Flick in 5 more rows and columns, but not so far that we start loading in
- // the ones that are bigger. Loading in more rows and columns of the same
- // size as the initial ones should not change the first prediction.
- qreal flickTo = ((cellSizeSmall + spacing) * 5);
- tableView->setContentX(flickTo);
- tableView->setContentY(flickTo);
+ // Flick to the end, and check that content width/height stays unchanged
+ tableView->setContentX(tableView->contentWidth() - tableView->width());
+ tableView->setContentY(tableView->contentHeight() - tableView->height());
QCOMPARE(tableView->contentWidth(), expectedSizeInit);
QCOMPARE(tableView->contentHeight(), expectedSizeInit);
- QCOMPARE(tableViewPrivate->averageEdgeSize.width(), cellSizeSmall);
- QCOMPARE(tableViewPrivate->averageEdgeSize.height(), cellSizeSmall);
-
- // Flick to row and column 20 (smallCellCount), since there the row and
- // column sizes increases with 100. Check that TableView then adjusts
- // contentWidth and contentHeight accordingly.
- flickTo = ((cellSizeSmall + spacing) * smallCellCount) - spacing;
- tableView->setContentX(flickTo);
- tableView->setContentY(flickTo);
-
- // Since we move the viewport more than a page, tableview
- // will jump to the new position and do a rebuild.
- QVERIFY(tableViewPrivate->polishScheduled);
- QVERIFY(tableViewPrivate->rebuildScheduled);
- WAIT_UNTIL_POLISHED;
-
- // Check that the average cell size is now matching the
- // large cells since they fill up the whole view.
- QCOMPARE(tableViewPrivate->averageEdgeSize.width(), cellSizeLarge);
- QCOMPARE(tableViewPrivate->averageEdgeSize.height(), cellSizeLarge);
-
- const int largeSizeCellCountInView = qCeil(tableView->width() / cellSizeLarge);
- const int columnCount = smallCellCount + largeSizeCellCountInView;
- QCOMPARE(tableViewPrivate->leftColumn(), smallCellCount);
- QCOMPARE(tableViewPrivate->rightColumn(), columnCount - 1);
-
- const qreal firstHalfLength = smallCellCount * cellSizeSmall;
- const qreal secondHalfOneScreenLength = largeSizeCellCountInView * cellSizeLarge;
- const qreal lengthAfterFlick = firstHalfLength + secondHalfOneScreenLength;
-
- // Check that loadedTableOuterRect has been calculated correct thus far
- const qreal spacingAfterFlick = (smallCellCount + largeSizeCellCountInView - 1) * spacing;
- QCOMPARE(tableViewPrivate->loadedTableOuterRect.left(), flickTo + spacing);
- QCOMPARE(tableViewPrivate->loadedTableOuterRect.right(), lengthAfterFlick + spacingAfterFlick);
- QCOMPARE(tableViewPrivate->loadedTableOuterRect.top(), flickTo + spacing);
- QCOMPARE(tableViewPrivate->loadedTableOuterRect.bottom(), lengthAfterFlick + spacingAfterFlick);
-
- // At this point, we should have the exact content width/height set, because
- // TableView knows where the large cells start in the viewport, and how many
- // columns that remain in the model. It will assume that the rest of the the
- // columns have the same average size as the ones currently inside the viewport.
- const qreal expectedContentSize = (smallCellCount * cellSizeSmall) + (largeCellCount * cellSizeLarge) + accumulatedSpacing;
- QCOMPARE(tableView->contentWidth(), expectedContentSize);
- QCOMPARE(tableView->contentHeight(), expectedContentSize);
-
- // Flick to the end (row/column 100, and overshoot a bit), and
- // check that we then end up with the exact content width/height.
- const qreal secondHalfLength = largeCellCount * cellSizeLarge;
- const qreal expectedFullSize = (firstHalfLength + secondHalfLength) + accumulatedSpacing;
- const qreal overshoot = 100;
- const qreal endPosX = expectedFullSize - tableView->width() + overshoot;
- const qreal endPosY = expectedFullSize - tableView->height() + overshoot;
- tableView->setContentX(endPosX);
- tableView->setContentY(endPosY);
-
- QCOMPARE(tableView->contentWidth(), expectedFullSize);
- QCOMPARE(tableView->contentHeight(), expectedFullSize);
// Flick back to start
tableView->setContentX(0);
@@ -681,10 +627,10 @@ void tst_QQuickTableView::checkContentWidthAndHeight()
// Since we move the viewport more than a page, tableview
// will jump to the new position and do a rebuild.
QVERIFY(tableViewPrivate->polishScheduled);
- QVERIFY(tableViewPrivate->rebuildScheduled);
+ QVERIFY(tableViewPrivate->scheduledRebuildOptions);
WAIT_UNTIL_POLISHED;
- // We should now have the same content width/height as when we started
+ // We should still have the same content width/height as when we started
QCOMPARE(tableView->contentWidth(), expectedSizeInit);
QCOMPARE(tableView->contentHeight(), expectedSizeInit);
}
@@ -716,7 +662,6 @@ void tst_QQuickTableView::checkPageFlicking()
QCOMPARE(tableViewPrivate->averageEdgeSize.width(), cellWidth);
QCOMPARE(tableViewPrivate->averageEdgeSize.height(), cellHeight);
- QVERIFY(!tableViewPrivate->rebuildScheduled);
QCOMPARE(tableViewPrivate->scheduledRebuildOptions, QQuickTableViewPrivate::RebuildOption::None);
// Flick 5000 columns to the right, and check that this triggers a
@@ -726,7 +671,6 @@ void tst_QQuickTableView::checkPageFlicking()
const qreal flickToColumnInPixels = ((cellWidth + columnSpacing) * flickToColumn) - columnSpacing;
tableView->setContentX(flickToColumnInPixels);
- QVERIFY(tableViewPrivate->rebuildScheduled);
QVERIFY(tableViewPrivate->scheduledRebuildOptions & QQuickTableViewPrivate::RebuildOption::ViewportOnly);
QVERIFY(tableViewPrivate->scheduledRebuildOptions & QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftColumn);
QVERIFY(!(tableViewPrivate->scheduledRebuildOptions & QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftRow));
@@ -748,7 +692,6 @@ void tst_QQuickTableView::checkPageFlicking()
const qreal flickToRowInPixels = ((cellHeight + rowSpacing) * flickToRow) - rowSpacing;
tableView->setContentY(flickToRowInPixels);
- QVERIFY(tableViewPrivate->rebuildScheduled);
QVERIFY(tableViewPrivate->scheduledRebuildOptions & QQuickTableViewPrivate::RebuildOption::ViewportOnly);
QVERIFY(!(tableViewPrivate->scheduledRebuildOptions & QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftColumn));
QVERIFY(tableViewPrivate->scheduledRebuildOptions & QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftRow);
@@ -783,6 +726,164 @@ void tst_QQuickTableView::checkExplicitContentWidthAndHeight()
QCOMPARE(tableView->contentHeight(), 1000);
}
+void tst_QQuickTableView::checkExtents_origin()
+{
+ // Check that if the beginning of the content view doesn't match the
+ // actual size of the table, origin will be adjusted to make it fit.
+ LOAD_TABLEVIEW("contentwidthheight.qml");
+
+ const int rows = 10;
+ const int columns = rows;
+ const qreal columnWidth = 100;
+ const qreal rowHeight = 100;
+ const qreal actualTableSize = columns * columnWidth;
+
+ // Set a content size that is far too large
+ // compared to the size of the table.
+ tableView->setContentWidth(actualTableSize * 2);
+ tableView->setContentHeight(actualTableSize * 2);
+ tableView->setRowSpacing(0);
+ tableView->setColumnSpacing(0);
+ tableView->setLeftMargin(0);
+ tableView->setRightMargin(0);
+ tableView->setTopMargin(0);
+ tableView->setBottomMargin(0);
+
+ auto model = TestModelAsVariant(rows, columns);
+ tableView->setModel(model);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Flick slowly to column 5 (to avoid rebuilds). Flick two columns at a
+ // time to ensure that we create a gap before TableView gets a chance to
+ // adjust endExtent first. This gap on the right side will make TableView
+ // move the table to move to the edge. Because of this, the table will not
+ // be aligned at the start of the content view when we next flick back again.
+ // And this will cause origin to move.
+ for (int x = 0; x <= 6; x += 2) {
+ tableView->setContentX(x * columnWidth);
+ tableView->setContentY(x * rowHeight);
+ }
+
+ // Check that the table has now been moved one column to the right
+ // (One column because that's how far outside the table we ended up flicking above).
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.right(), actualTableSize + columnWidth);
+
+ // Flick back one column at a time so that TableView detects that the first
+ // column is not at the origin before the "table move" logic kicks in. This
+ // will make TableView adjust the origin.
+ for (int x = 6; x >= 0; x -= 1) {
+ tableView->setContentX(x * columnWidth);
+ tableView->setContentY(x * rowHeight);
+ }
+
+ // The origin will be moved with the same offset that the table was
+ // moved on the right side earlier, which is one column length.
+ QCOMPARE(tableViewPrivate->origin.x(), columnWidth);
+ QCOMPARE(tableViewPrivate->origin.y(), rowHeight);
+}
+
+void tst_QQuickTableView::checkExtents_endExtent()
+{
+ // Check that if we the content view size doesn't match the actual size
+ // of the table, endExtent will be adjusted to make it fit (so that
+ // e.g the the flicking will bounce to a stop at the edge of the table).
+ LOAD_TABLEVIEW("contentwidthheight.qml");
+
+ const int rows = 10;
+ const int columns = rows;
+ const qreal columnWidth = 100;
+ const qreal rowHeight = 100;
+ const qreal actualTableSize = columns * columnWidth;
+
+ // Set a content size that is far too large
+ // compared to the size of the table.
+ tableView->setContentWidth(actualTableSize * 2);
+ tableView->setContentHeight(actualTableSize * 2);
+ tableView->setRowSpacing(0);
+ tableView->setColumnSpacing(0);
+ tableView->setLeftMargin(0);
+ tableView->setRightMargin(0);
+ tableView->setTopMargin(0);
+ tableView->setBottomMargin(0);
+
+ auto model = TestModelAsVariant(rows, columns);
+ tableView->setModel(model);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Flick slowly to column 5 (to avoid rebuilds). This will flick the table to
+ // the last column in the model. But since there still is a lot space left in
+ // the content view, endExtent will be set accordingly to compensate.
+ for (int x = 1; x <= 5; x++)
+ tableView->setContentX(x * columnWidth);
+ QCOMPARE(tableViewPrivate->rightColumn(), columns - 1);
+ qreal expectedEndExtentWidth = actualTableSize - tableView->contentWidth();
+ QCOMPARE(tableViewPrivate->endExtent.width(), expectedEndExtentWidth);
+
+ for (int y = 1; y <= 5; y++)
+ tableView->setContentY(y * rowHeight);
+ QCOMPARE(tableViewPrivate->bottomRow(), rows - 1);
+ qreal expectedEndExtentHeight = actualTableSize - tableView->contentHeight();
+ QCOMPARE(tableViewPrivate->endExtent.height(), expectedEndExtentHeight);
+}
+
+void tst_QQuickTableView::checkExtents_moveTableToEdge()
+{
+ // Check that if we the content view size doesn't match the actual
+ // size of the table, and we fast-flick the viewport to outside
+ // the table, we end up moving the table back into the viewport to
+ // avoid any visual glitches.
+ LOAD_TABLEVIEW("contentwidthheight.qml");
+
+ const int rows = 10;
+ const int columns = rows;
+ const qreal columnWidth = 100;
+ const qreal rowHeight = 100;
+ const qreal actualTableSize = columns * columnWidth;
+
+ // Set a content size that is far to large
+ // compared to the size of the table.
+ tableView->setContentWidth(actualTableSize * 2);
+ tableView->setContentHeight(actualTableSize * 2);
+ tableView->setRowSpacing(0);
+ tableView->setColumnSpacing(0);
+ tableView->setLeftMargin(0);
+ tableView->setRightMargin(0);
+ tableView->setTopMargin(0);
+ tableView->setBottomMargin(0);
+
+ auto model = TestModelAsVariant(rows, columns);
+ tableView->setModel(model);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Flick slowly to column 5 (to avoid rebuilds). Flick two columns at a
+ // time to ensure that we create a gap before TableView gets a chance to
+ // adjust endExtent first. This gap on the right side will make TableView
+ // move the table to the edge (in addition to adjusting the extents, but that
+ // will happen in a subsequent polish, and is not for this test verify).
+ for (int x = 0; x <= 6; x += 2)
+ tableView->setContentX(x * columnWidth);
+ QCOMPARE(tableViewPrivate->rightColumn(), columns - 1);
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect, tableViewPrivate->viewportRect);
+
+ for (int y = 0; y <= 6; y += 2)
+ tableView->setContentY(y * rowHeight);
+ QCOMPARE(tableViewPrivate->bottomRow(), rows - 1);
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect, tableViewPrivate->viewportRect);
+
+ for (int x = 6; x >= 0; x -= 2)
+ tableView->setContentX(x * columnWidth);
+ QCOMPARE(tableViewPrivate->leftColumn(), 0);
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect, tableViewPrivate->viewportRect);
+
+ for (int y = 6; y >= 0; y -= 2)
+ tableView->setContentY(y * rowHeight);
+ QCOMPARE(tableViewPrivate->topRow(), 0);
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect, tableViewPrivate->viewportRect);
+}
+
void tst_QQuickTableView::checkContentXY()
{
// Check that you can bind contentX and contentY to
@@ -1165,8 +1266,11 @@ void tst_QQuickTableView::checkSpacingValues()
tableView->polish();
WAIT_UNTIL_POLISHED;
- QCOMPARE(tableView->contentWidth(), columnCount * (delegateWidth + tableView->columnSpacing()) - tableView->columnSpacing());
- QCOMPARE(tableView->contentHeight(), rowCount * (delegateHeight + tableView->rowSpacing()) - tableView->rowSpacing());
+
+ const qreal expectedInitialContentWidth = columnCount * (delegateWidth + tableView->columnSpacing()) - tableView->columnSpacing();
+ const qreal expectedInitialContentHeight = rowCount * (delegateHeight + tableView->rowSpacing()) - tableView->rowSpacing();
+ QCOMPARE(tableView->contentWidth(), expectedInitialContentWidth);
+ QCOMPARE(tableView->contentHeight(), expectedInitialContentHeight);
// Valid spacing assignment
tableView->setRowSpacing(42);
@@ -1176,8 +1280,13 @@ void tst_QQuickTableView::checkSpacingValues()
tableView->polish();
WAIT_UNTIL_POLISHED;
- QCOMPARE(tableView->contentWidth(), columnCount * (delegateWidth + tableView->columnSpacing()) - tableView->columnSpacing());
- QCOMPARE(tableView->contentHeight(), rowCount * (delegateHeight + tableView->rowSpacing()) - tableView->rowSpacing());
+
+ // Even after changing spacing, TableView will keep the initial guesstimated content size. The
+ // reason is that deciding the content size based on the currently visible row/columns/spacing
+ // will almost always be at a little bit wrong at best. So instead of pretending that TableView
+ // knows what the size of the full table is, it sticks with the first guesstimate.
+ QCOMPARE(tableView->contentWidth(), expectedInitialContentWidth);
+ QCOMPARE(tableView->contentHeight(), expectedInitialContentHeight);
// Invalid assignments (should ignore)
tableView->setRowSpacing(-1);
@@ -1969,7 +2078,7 @@ void tst_QQuickTableView::checkChangingModelFromDelegate()
// And since the QML code tried to add another row as well, we
// expect rebuildScheduled to be true, and a polish event to be pending.
- QCOMPARE(tableViewPrivate->rebuildScheduled, true);
+ QVERIFY(tableViewPrivate->scheduledRebuildOptions);
QCOMPARE(tableViewPrivate->polishScheduled, true);
WAIT_UNTIL_POLISHED;
@@ -2052,7 +2161,7 @@ void tst_QQuickTableView::checkTableviewInsideAsyncLoader()
QCOMPARE(loader->status(), QQuickLoader::Ready);
// Check that TableView has finished building
- QCOMPARE(tableViewPrivate->rebuildScheduled, false);
+ QVERIFY(!tableViewPrivate->scheduledRebuildOptions);
QCOMPARE(tableViewPrivate->rebuildState, QQuickTableViewPrivate::RebuildState::Done);
// Check that all expected delegate items have been loaded
@@ -2179,6 +2288,337 @@ void tst_QQuickTableView::checkThatRevisionedPropertiesCannotBeUsedInOldImports(
QCOMPARE(resolvedColumn, 42);
}
+void tst_QQuickTableView::checkSyncView_rootView_data()
+{
+ QTest::addColumn<qreal>("flickToPos");
+
+ QTest::newRow("pos:110") << 110.;
+ QTest::newRow("pos:2010") << 2010.;
+}
+
+void tst_QQuickTableView::checkSyncView_rootView()
+{
+ // Check that if you flick on the root tableview (the view that has
+ // no other view as syncView), all the other tableviews will sync
+ // their content view position according to their syncDirection flag.
+ QFETCH(qreal, flickToPos);
+ LOAD_TABLEVIEW("syncviewsimple.qml");
+ GET_QML_TABLEVIEW(tableViewH);
+ GET_QML_TABLEVIEW(tableViewV);
+ GET_QML_TABLEVIEW(tableViewHV);
+ QQuickTableView *views[] = {tableViewH, tableViewV, tableViewHV};
+
+ auto model = TestModelAsVariant(100, 100);
+
+ tableView->setModel(model);
+ for (auto view : views)
+ view->setModel(model);
+
+ tableView->setContentX(flickToPos);
+ tableView->setContentY(flickToPos);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Check that geometry properties are mirrored
+ QCOMPARE(tableViewH->columnSpacing(), tableView->columnSpacing());
+ QCOMPARE(tableViewH->rowSpacing(), 0);
+ QCOMPARE(tableViewH->contentWidth(), tableView->contentWidth());
+ QCOMPARE(tableViewV->columnSpacing(), 0);
+ QCOMPARE(tableViewV->rowSpacing(), tableView->rowSpacing());
+ QCOMPARE(tableViewV->contentHeight(), tableView->contentHeight());
+
+ // Check that viewport is in sync after the flick
+ QCOMPARE(tableView->contentX(), flickToPos);
+ QCOMPARE(tableView->contentY(), flickToPos);
+ QCOMPARE(tableViewH->contentX(), tableView->contentX());
+ QCOMPARE(tableViewH->contentY(), 0);
+ QCOMPARE(tableViewV->contentX(), 0);
+ QCOMPARE(tableViewV->contentY(), tableView->contentY());
+ QCOMPARE(tableViewHV->contentX(), tableView->contentX());
+ QCOMPARE(tableViewHV->contentY(), tableView->contentY());
+
+ // Check that topLeft cell is in sync after the flick
+ QCOMPARE(tableViewHPrivate->leftColumn(), tableViewPrivate->leftColumn());
+ QCOMPARE(tableViewHPrivate->rightColumn(), tableViewPrivate->rightColumn());
+ QCOMPARE(tableViewHPrivate->topRow(), 0);
+ QCOMPARE(tableViewVPrivate->leftColumn(), 0);
+ QCOMPARE(tableViewVPrivate->topRow(), tableViewPrivate->topRow());
+ QCOMPARE(tableViewHVPrivate->leftColumn(), tableViewPrivate->leftColumn());
+ QCOMPARE(tableViewHVPrivate->topRow(), tableViewPrivate->topRow());
+
+ // Check that the geometry of the tables are in sync after the flick
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.left(), tableViewPrivate->loadedTableOuterRect.left());
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.right(), tableViewPrivate->loadedTableOuterRect.right());
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.top(), 0);
+
+ QCOMPARE(tableViewVPrivate->loadedTableOuterRect.top(), tableViewPrivate->loadedTableOuterRect.top());
+ QCOMPARE(tableViewVPrivate->loadedTableOuterRect.bottom(), tableViewPrivate->loadedTableOuterRect.bottom());
+ QCOMPARE(tableViewVPrivate->loadedTableOuterRect.left(), 0);
+
+ QCOMPARE(tableViewHVPrivate->loadedTableOuterRect, tableViewPrivate->loadedTableOuterRect);
+}
+
+void tst_QQuickTableView::checkSyncView_childViews_data()
+{
+ QTest::addColumn<int>("viewIndexToFlick");
+ QTest::addColumn<qreal>("flickToPos");
+
+ QTest::newRow("tableViewH, pos:100") << 0 << 100.;
+ QTest::newRow("tableViewV, pos:100") << 1 << 100.;
+ QTest::newRow("tableViewHV, pos:100") << 2 << 100.;
+ QTest::newRow("tableViewH, pos:2000") << 0 << 2000.;
+ QTest::newRow("tableViewV, pos:2000") << 1 << 2000.;
+ QTest::newRow("tableViewHV, pos:2000") << 2 << 2000.;
+}
+
+void tst_QQuickTableView::checkSyncView_childViews()
+{
+ // Check that if you flick on a tableview that has a syncView, the
+ // syncView will move to the new position as well, which will also
+ // recursivly move all other connected child views of the syncView.
+ QFETCH(int, viewIndexToFlick);
+ QFETCH(qreal, flickToPos);
+ LOAD_TABLEVIEW("syncviewsimple.qml");
+ GET_QML_TABLEVIEW(tableViewH);
+ GET_QML_TABLEVIEW(tableViewV);
+ GET_QML_TABLEVIEW(tableViewHV);
+ QQuickTableView *views[] = {tableViewH, tableViewV, tableViewHV};
+ QQuickTableView *viewToFlick = views[viewIndexToFlick];
+ QQuickTableViewPrivate *viewToFlickPrivate = QQuickTableViewPrivate::get(viewToFlick);
+
+ auto model = TestModelAsVariant(100, 100);
+
+ tableView->setModel(model);
+ for (auto view : views)
+ view->setModel(model);
+
+ viewToFlick->setContentX(flickToPos);
+ viewToFlick->setContentY(flickToPos);
+
+ WAIT_UNTIL_POLISHED;
+
+ // The view the user flicks on can always be flicked in both directions
+ // (unless is has a flickingDirection set, which is not the case here).
+ QCOMPARE(viewToFlick->contentX(), flickToPos);
+ QCOMPARE(viewToFlick->contentY(), flickToPos);
+
+ // The root view (tableView) will move in sync according
+ // to the syncDirection of the view being flicked.
+ if (viewToFlick->syncDirection() & Qt::Horizontal) {
+ QCOMPARE(tableView->contentX(), flickToPos);
+ QCOMPARE(tableViewPrivate->leftColumn(), viewToFlickPrivate->leftColumn());
+ QCOMPARE(tableViewPrivate->rightColumn(), viewToFlickPrivate->rightColumn());
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.left(), viewToFlickPrivate->loadedTableOuterRect.left());
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.right(), viewToFlickPrivate->loadedTableOuterRect.right());
+ } else {
+ QCOMPARE(tableView->contentX(), 0);
+ QCOMPARE(tableViewPrivate->leftColumn(), 0);
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.left(), 0);
+ }
+
+ if (viewToFlick->syncDirection() & Qt::Vertical) {
+ QCOMPARE(tableView->contentY(), flickToPos);
+ QCOMPARE(tableViewPrivate->topRow(), viewToFlickPrivate->topRow());
+ QCOMPARE(tableViewPrivate->bottomRow(), viewToFlickPrivate->bottomRow());
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.top(), viewToFlickPrivate->loadedTableOuterRect.top());
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.bottom(), viewToFlickPrivate->loadedTableOuterRect.bottom());
+ } else {
+ QCOMPARE(tableView->contentY(), 0);
+ QCOMPARE(tableViewPrivate->topRow(), 0);
+ QCOMPARE(tableViewPrivate->loadedTableOuterRect.top(), 0);
+ }
+
+ // The other views should continue to stay in sync with
+ // the root view, unless it was the view being flicked.
+ if (viewToFlick != tableViewH) {
+ QCOMPARE(tableViewH->contentX(), tableView->contentX());
+ QCOMPARE(tableViewH->contentY(), 0);
+ QCOMPARE(tableViewHPrivate->leftColumn(), tableViewPrivate->leftColumn());
+ QCOMPARE(tableViewHPrivate->rightColumn(), tableViewPrivate->rightColumn());
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.left(), tableViewPrivate->loadedTableOuterRect.left());
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.right(), tableViewPrivate->loadedTableOuterRect.right());
+ QCOMPARE(tableViewHPrivate->topRow(), 0);
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.top(), 0);
+ }
+
+ if (viewToFlick != tableViewV) {
+ QCOMPARE(tableViewV->contentX(), 0);
+ QCOMPARE(tableViewV->contentY(), tableView->contentY());
+ QCOMPARE(tableViewVPrivate->topRow(), tableViewPrivate->topRow());
+ QCOMPARE(tableViewVPrivate->bottomRow(), tableViewPrivate->bottomRow());
+ QCOMPARE(tableViewVPrivate->loadedTableOuterRect.top(), tableViewPrivate->loadedTableOuterRect.top());
+ QCOMPARE(tableViewVPrivate->loadedTableOuterRect.bottom(), tableViewPrivate->loadedTableOuterRect.bottom());
+ QCOMPARE(tableViewVPrivate->leftColumn(), 0);
+ QCOMPARE(tableViewVPrivate->loadedTableOuterRect.left(), 0);
+ }
+
+ if (viewToFlick != tableViewHV) {
+ QCOMPARE(tableViewHV->contentX(), tableView->contentX());
+ QCOMPARE(tableViewHV->contentY(), tableView->contentY());
+ QCOMPARE(tableViewHVPrivate->leftColumn(), tableViewPrivate->leftColumn());
+ QCOMPARE(tableViewHVPrivate->rightColumn(), tableViewPrivate->rightColumn());
+ QCOMPARE(tableViewHVPrivate->topRow(), tableViewPrivate->topRow());
+ QCOMPARE(tableViewHVPrivate->bottomRow(), tableViewPrivate->bottomRow());
+ QCOMPARE(tableViewHVPrivate->loadedTableOuterRect, tableViewPrivate->loadedTableOuterRect);
+ }
+}
+
+void tst_QQuickTableView::checkSyncView_differentSizedModels()
+{
+ // Check that you can have two tables in a syncView relation, where
+ // the sync "child" has fewer rows/columns than the syncView. In that
+ // case, it will be possible to flick the syncView further out than
+ // the child have rows/columns to follow. This causes some extra
+ // challenges for TableView to ensure that they are still kept in
+ // sync once you later flick the syncView back to a point where both
+ // tables ends up visible. This test will check this sitiation.
+ LOAD_TABLEVIEW("syncviewsimple.qml");
+ GET_QML_TABLEVIEW(tableViewH);
+ GET_QML_TABLEVIEW(tableViewV);
+ GET_QML_TABLEVIEW(tableViewHV);
+
+ auto tableViewModel = TestModelAsVariant(100, 100);
+ auto tableViewHModel = TestModelAsVariant(100, 50);
+ auto tableViewVModel = TestModelAsVariant(50, 100);
+ auto tableViewHVModel = TestModelAsVariant(5, 5);
+
+ tableView->setModel(tableViewModel);
+ tableViewH->setModel(tableViewHModel);
+ tableViewV->setModel(tableViewVModel);
+ tableViewHV->setModel(tableViewHVModel);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Flick far out, beyond the smaller tables, which will
+ // also force a rebuild (and as such, cause layout properties
+ // like average cell width to be temporarily out of sync).
+ tableView->setContentX(5000);
+ QVERIFY(tableViewPrivate->scheduledRebuildOptions);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Check that the smaller tables are now flicked out of view
+ qreal leftEdge = tableViewPrivate->loadedTableOuterRect.left();
+ QVERIFY(tableViewHPrivate->loadedTableOuterRect.right() < leftEdge);
+ QVERIFY(tableViewHVPrivate->loadedTableOuterRect.right() < leftEdge);
+
+ // Flick slowly back so that we don't trigger a rebuild (since
+ // we want to check that we stay in sync also when not rebuilding).
+ while (tableView->contentX() > 200) {
+ tableView->setContentX(tableView->contentX() - 200);
+ QVERIFY(!tableViewPrivate->rebuildOptions);
+ QVERIFY(!tableViewPrivate->polishScheduled);
+ }
+
+ leftEdge = tableViewPrivate->loadedTableOuterRect.left();
+ const int leftColumn = tableViewPrivate->leftColumn();
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.left(), leftEdge);
+ QCOMPARE(tableViewHPrivate->leftColumn(), leftColumn);
+
+ // Because the tableView was fast flicked and then slowly flicked back, the
+ // left column is now 49, which is actually far too high, since we're almost
+ // at the beginning of the content view. But this "miscalculation" is expected
+ // when the column widths are increasing for each column, like they do in this
+ // test. In that case, the algorithm that predicts where each column should end
+ // up gets slightly confused. Anyway, check that tableViewHV, that has only
+ // 5 columns, is not showing any columns, since it should always stay in sync with
+ // syncView regardless of the content view position.
+ QVERIFY(tableViewHVPrivate->loadedColumns.isEmpty());
+}
+
+void tst_QQuickTableView::checkSyncView_connect_late_data()
+{
+ QTest::addColumn<qreal>("flickToPos");
+
+ QTest::newRow("pos:110") << 110.;
+ QTest::newRow("pos:2010") << 2010.;
+}
+
+void tst_QQuickTableView::checkSyncView_connect_late()
+{
+ // Check that if you assign a syncView to a TableView late, and
+ // after the views have been flicked around, they will still end up in sync.
+ QFETCH(qreal, flickToPos);
+ LOAD_TABLEVIEW("syncviewsimple.qml");
+ GET_QML_TABLEVIEW(tableViewH);
+ GET_QML_TABLEVIEW(tableViewV);
+ GET_QML_TABLEVIEW(tableViewHV);
+ QQuickTableView *views[] = {tableViewH, tableViewV, tableViewHV};
+
+ auto model = TestModelAsVariant(100, 100);
+
+ tableView->setModel(model);
+
+ // Start with no syncView connections
+ for (auto view : views) {
+ view->setSyncView(nullptr);
+ view->setModel(model);
+ }
+
+ WAIT_UNTIL_POLISHED;
+
+ tableView->setContentX(flickToPos);
+ tableView->setContentY(flickToPos);
+
+ WAIT_UNTIL_POLISHED;
+
+ // Check that viewport is not in sync after the flick
+ QCOMPARE(tableView->contentX(), flickToPos);
+ QCOMPARE(tableView->contentY(), flickToPos);
+ QCOMPARE(tableViewH->contentX(), 0);
+ QCOMPARE(tableViewH->contentY(), 0);
+ QCOMPARE(tableViewV->contentX(), 0);
+ QCOMPARE(tableViewV->contentY(), 0);
+ QCOMPARE(tableViewHV->contentX(), 0);
+ QCOMPARE(tableViewHV->contentY(), 0);
+
+ // Assign the syncView late. This should make
+ // all the views end up in sync.
+ for (auto view : views) {
+ view->setSyncView(tableView);
+ WAIT_UNTIL_POLISHED_ARG(view);
+ }
+
+ // Check that geometry properties are mirrored
+ QCOMPARE(tableViewH->columnSpacing(), tableView->columnSpacing());
+ QCOMPARE(tableViewH->rowSpacing(), 0);
+ QCOMPARE(tableViewH->contentWidth(), tableView->contentWidth());
+ QCOMPARE(tableViewV->columnSpacing(), 0);
+ QCOMPARE(tableViewV->rowSpacing(), tableView->rowSpacing());
+ QCOMPARE(tableViewV->contentHeight(), tableView->contentHeight());
+
+ // Check that viewport is in sync after the flick
+ QCOMPARE(tableView->contentX(), flickToPos);
+ QCOMPARE(tableView->contentY(), flickToPos);
+ QCOMPARE(tableViewH->contentX(), tableView->contentX());
+ QCOMPARE(tableViewH->contentY(), 0);
+ QCOMPARE(tableViewV->contentX(), 0);
+ QCOMPARE(tableViewV->contentY(), tableView->contentY());
+ QCOMPARE(tableViewHV->contentX(), tableView->contentX());
+ QCOMPARE(tableViewHV->contentY(), tableView->contentY());
+
+ // Check that topLeft cell is in sync after the flick
+ QCOMPARE(tableViewHPrivate->leftColumn(), tableViewPrivate->leftColumn());
+ QCOMPARE(tableViewHPrivate->rightColumn(), tableViewPrivate->rightColumn());
+ QCOMPARE(tableViewHPrivate->topRow(), 0);
+ QCOMPARE(tableViewVPrivate->leftColumn(), 0);
+ QCOMPARE(tableViewVPrivate->topRow(), tableViewPrivate->topRow());
+ QCOMPARE(tableViewHVPrivate->leftColumn(), tableViewPrivate->leftColumn());
+ QCOMPARE(tableViewHVPrivate->topRow(), tableViewPrivate->topRow());
+
+ // Check that the geometry of the tables are in sync after the flick
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.left(), tableViewPrivate->loadedTableOuterRect.left());
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.right(), tableViewPrivate->loadedTableOuterRect.right());
+ QCOMPARE(tableViewHPrivate->loadedTableOuterRect.top(), 0);
+
+ QCOMPARE(tableViewVPrivate->loadedTableOuterRect.top(), tableViewPrivate->loadedTableOuterRect.top());
+ QCOMPARE(tableViewVPrivate->loadedTableOuterRect.bottom(), tableViewPrivate->loadedTableOuterRect.bottom());
+ QCOMPARE(tableViewVPrivate->loadedTableOuterRect.left(), 0);
+
+ QCOMPARE(tableViewHVPrivate->loadedTableOuterRect, tableViewPrivate->loadedTableOuterRect);
+
+}
+
QTEST_MAIN(tst_QQuickTableView)
#include "tst_qquicktableview.moc"
diff --git a/tests/auto/quick/qquicktext/BLACKLIST b/tests/auto/quick/qquicktext/BLACKLIST
index 531d981159..6c3c3af154 100644
--- a/tests/auto/quick/qquicktext/BLACKLIST
+++ b/tests/auto/quick/qquicktext/BLACKLIST
@@ -1,6 +1,6 @@
[dependentImplicitSizes]
-*
-[lineLaidOutRelayout]
-msvc-2015
+b2qt
+qemu
+osx-10.12
[fontSizeMode]
opensuse-42.1
diff --git a/tests/auto/quick/qquicktextedit/BLACKLIST b/tests/auto/quick/qquicktextedit/BLACKLIST
new file mode 100644
index 0000000000..9df9c7d75a
--- /dev/null
+++ b/tests/auto/quick/qquicktextedit/BLACKLIST
@@ -0,0 +1,2 @@
+[mouseSelection]
+opensuse-leap
diff --git a/tests/auto/quick/qquickview/tst_qquickview.cpp b/tests/auto/quick/qquickview/tst_qquickview.cpp
index e259ed1ae7..dbce0d308c 100644
--- a/tests/auto/quick/qquickview/tst_qquickview.cpp
+++ b/tests/auto/quick/qquickview/tst_qquickview.cpp
@@ -49,6 +49,7 @@ private slots:
void errors();
void engine();
void findChild();
+ void setInitialProperties();
};
@@ -283,6 +284,17 @@ void tst_QQuickView::findChild()
QVERIFY(!view.rootObject()->findChild<QObject *>("rootObject")); // self
}
+void tst_QQuickView::setInitialProperties()
+{
+ QQuickView view;
+ view.setInitialProperties({{"z", 4}, {"width", 100}});
+ view.setSource(testFileUrl("resizemodeitem.qml"));
+ QObject *rootObject = view.rootObject();
+ QVERIFY(rootObject);
+ QCOMPARE(rootObject->property("z").toInt(), 4);
+ QCOMPARE(rootObject->property("width").toInt(), 100);
+}
+
QTEST_MAIN(tst_QQuickView)
#include "tst_qquickview.moc"
diff --git a/tests/auto/quick/qquickvisualdatamodel/qquickvisualdatamodel.pro b/tests/auto/quick/qquickvisualdatamodel/qquickvisualdatamodel.pro
index 9222e39477..5445f6768d 100644
--- a/tests/auto/quick/qquickvisualdatamodel/qquickvisualdatamodel.pro
+++ b/tests/auto/quick/qquickvisualdatamodel/qquickvisualdatamodel.pro
@@ -9,5 +9,5 @@ include (../shared/util.pri)
TESTDATA = data/*
-QT += core-private gui-private qml-private quick-private testlib
+QT += core-private gui-private qml-private quick-private testlib qmlmodels-private
qtHaveModule(widgets): QT += widgets
diff --git a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp
index fac8283e2c..fe56cad018 100644
--- a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp
+++ b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp
@@ -39,7 +39,7 @@
#include <QtQuick/qquickview.h>
#include <private/qquicklistview_p.h>
#include <QtQuick/private/qquicktext_p.h>
-#include <QtQml/private/qqmldelegatemodel_p.h>
+#include <QtQmlModels/private/qqmldelegatemodel_p.h>
#include <private/qqmlvaluetype_p.h>
#include <private/qqmlchangeset_p.h>
#include <private/qqmlengine_p.h>
@@ -432,6 +432,7 @@ private slots:
void asynchronousCancel();
void invalidContext();
void externalManagedModel();
+ void delegateModelChangeDelegate();
private:
template <int N> void groups_verify(
@@ -4302,6 +4303,45 @@ void tst_qquickvisualdatamodel::externalManagedModel()
QTRY_VERIFY(!object->property("running").toBool());
}
+void tst_qquickvisualdatamodel::delegateModelChangeDelegate()
+{
+ // Verify that QTBUG-63477 is fixed.
+ // Changing the delegate would not update existing items.
+ QQmlEngine engine;
+ QScopedPointer<QQmlContext> context(new QQmlContext(engine.rootContext()));
+
+ QQmlComponent c(&engine);
+ c.setData("import QtQml.Models 2.2\nDelegateModel {}\n", QUrl());
+ QCOMPARE(c.status(), QQmlComponent::Ready);
+
+ QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(c.create(context.data()));
+ QVERIFY(visualModel);
+ visualModel->setModel(QVariant(3));
+
+ QQmlComponent first(&engine);
+ first.setData("import QtQuick 2.0\nItem { objectName: \"old\" }\n", QUrl());
+ QCOMPARE(first.status(), QQmlComponent::Ready);
+
+ // Without delegate, claim to have an item count of 0
+ QCOMPARE(visualModel->count(), 0);
+
+ visualModel->setDelegate(&first);
+ // The first delegate has been set, verify we get it
+ QObject* old = visualModel->object(0, QQmlIncubator::Synchronous);
+ QVERIFY(old);
+ QCOMPARE(visualModel->object(0, QQmlIncubator::Synchronous)->objectName(), QStringLiteral("old"));
+ QCOMPARE(visualModel->count(), 3);
+
+ QQmlComponent second(&engine);
+ second.setData("import QtQuick 2.0\nItem { objectName: \"new\" }\n", QUrl());
+ QCOMPARE(second.status(), QQmlComponent::Ready);
+
+ visualModel->setDelegate(&second);
+ // After changing the delegate, expect the existing item to have the new delegate
+ QCOMPARE(visualModel->object(0, QQmlIncubator::Synchronous)->objectName(), QStringLiteral("new"));
+ QCOMPARE(visualModel->count(), 3);
+}
+
QTEST_MAIN(tst_qquickvisualdatamodel)
#include "tst_qquickvisualdatamodel.moc"
diff --git a/tests/auto/quick/qquickwindow/BLACKLIST b/tests/auto/quick/qquickwindow/BLACKLIST
index 1282a9d5ec..b4b7d2d761 100644
--- a/tests/auto/quick/qquickwindow/BLACKLIST
+++ b/tests/auto/quick/qquickwindow/BLACKLIST
@@ -1,6 +1,3 @@
[openglContextCreatedSignal]
opensuse-42.3
opensuse-leap
-# QTBUG-62177
-[attachedProperty]
-osx
diff --git a/tests/auto/quick/qquickwindow/data/shortcut.qml b/tests/auto/quick/qquickwindow/data/shortcut.qml
new file mode 100644
index 0000000000..2632e27859
--- /dev/null
+++ b/tests/auto/quick/qquickwindow/data/shortcut.qml
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+import QtQuick 2.9
+import QtQuick.Window 2.2
+
+Window {
+ id: root
+ visible: true
+ width: 200
+ height: 200
+ property bool received: false
+ Item {
+ focus: true
+ Shortcut {
+ sequence: "B"
+ onActivated: {
+ root.received = true
+ }
+ }
+ }
+}
+
diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
index f08b9207d1..7faa621e86 100644
--- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
+++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
@@ -482,6 +482,10 @@ private slots:
void testChildMouseEventFilter_data();
void cleanupGrabsOnRelease();
+#if QT_CONFIG(shortcut)
+ void testShortCut();
+#endif
+
private:
QTouchDevice *touchDevice;
QTouchDevice *touchDeviceWithVelocity;
@@ -3579,6 +3583,30 @@ void tst_qquickwindow::cleanupGrabsOnRelease()
QCOMPARE(parent->mouseUngrabEventCount, 1);
}
+#if QT_CONFIG(shortcut)
+void tst_qquickwindow::testShortCut()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadUrl(testFileUrl("shortcut.qml"));
+
+ QObject *created = component.create();
+ QScopedPointer<QObject> cleanup(created);
+ QVERIFY(created);
+
+ QQuickWindow *window = qobject_cast<QQuickWindow *>(created);
+ QVERIFY(QTest::qWaitForWindowActive(window));
+
+ EventFilter eventFilter;
+ window->activeFocusItem()->installEventFilter(&eventFilter);
+ //Send non-spontaneous key press event
+ QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_B, Qt::NoModifier);
+ QCoreApplication::sendEvent(window, &keyEvent);
+ QVERIFY(eventFilter.events.contains(int(QEvent::ShortcutOverride)));
+ QVERIFY(window->property("received").value<bool>());
+}
+#endif
+
QTEST_MAIN(tst_qquickwindow)
#include "tst_qquickwindow.moc"
diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro
index 7257a99d2a..b6ffdc0f7e 100644
--- a/tests/auto/quick/quick.pro
+++ b/tests/auto/quick/quick.pro
@@ -29,6 +29,7 @@ PRIVATETESTS += \
qquickanimations \
qquickapplication \
qquickbehaviors \
+ qquickboundaryrule \
qquickfontloader \
qquickfontloader_static \
qquickfontmetrics \
diff --git a/tests/auto/quick/rendernode/tst_rendernode.cpp b/tests/auto/quick/rendernode/tst_rendernode.cpp
index 0e06ee6f50..6ca5231343 100644
--- a/tests/auto/quick/rendernode/tst_rendernode.cpp
+++ b/tests/auto/quick/rendernode/tst_rendernode.cpp
@@ -49,8 +49,7 @@ public:
view.setResizeMode(QQuickView::SizeViewToRootObject);
view.setSource(testFileUrl(fileName));
view.setVisible(true);
- QTest::qWaitForWindowExposed(&view);
- return view.grabWindow();
+ return QTest::qWaitForWindowExposed(&view) ? view.grabWindow() : QImage();
}
//It is important for platforms that only are able to show fullscreen windows
@@ -61,6 +60,9 @@ private slots:
void renderOrder();
void messUpState();
void matrix();
+
+private:
+ bool isRunningOnRhi() const;
};
class ClearNode : public QSGRenderNode
@@ -218,7 +220,11 @@ void tst_rendernode::renderOrder()
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms");
+ if (isRunningOnRhi())
+ QSKIP("Render nodes not yet supported with QRhi");
+
QImage fb = runTest("RenderOrder.qml");
+ QVERIFY(!fb.isNull());
const qreal scaleFactor = QGuiApplication::primaryScreen()->devicePixelRatio();
QCOMPARE(fb.width(), qRound(200 * scaleFactor));
@@ -247,7 +253,11 @@ void tst_rendernode::messUpState()
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms");
+ if (isRunningOnRhi())
+ QSKIP("Render nodes not yet supported with QRhi");
+
QImage fb = runTest("MessUpState.qml");
+ QVERIFY(!fb.isNull());
int x1 = 0;
int x2 = fb.width() / 2;
int x3 = fb.width() - 1;
@@ -304,9 +314,12 @@ void tst_rendernode::matrix()
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms");
+ if (isRunningOnRhi())
+ QSKIP("Render nodes not yet supported with QRhi");
+
qmlRegisterType<StateRecordingRenderNodeItem>("RenderNode", 1, 0, "StateRecorder");
StateRecordingRenderNode::matrices.clear();
- runTest("matrix.qml");
+ QVERIFY(!runTest("matrix.qml").isNull());
QMatrix4x4 noRotateOffset;
noRotateOffset.translate(20, 20);
@@ -351,6 +364,22 @@ void tst_rendernode::matrix()
}
}
+bool tst_rendernode::isRunningOnRhi() const
+{
+ static bool retval = false;
+ static bool decided = false;
+ if (!decided) {
+ decided = true;
+ QQuickView dummy;
+ dummy.show();
+ if (QTest::qWaitForWindowExposed(&dummy)) {
+ QSGRendererInterface::GraphicsApi api = dummy.rendererInterface()->graphicsApi();
+ retval = QSGRendererInterface::isApiRhiBased(api);
+ }
+ dummy.hide();
+ }
+ return retval;
+}
QTEST_MAIN(tst_rendernode)
diff --git a/tests/auto/quick/scenegraph/data/render_bug37422.frag b/tests/auto/quick/scenegraph/data/render_bug37422.frag
new file mode 100644
index 0000000000..da157232ac
--- /dev/null
+++ b/tests/auto/quick/scenegraph/data/render_bug37422.frag
@@ -0,0 +1,9 @@
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord0;
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ fragColor = vec4(1, 0, 0, 1);
+}
diff --git a/tests/auto/quick/scenegraph/data/render_bug37422.frag.qsb b/tests/auto/quick/scenegraph/data/render_bug37422.frag.qsb
new file mode 100644
index 0000000000..1233ccfbca
--- /dev/null
+++ b/tests/auto/quick/scenegraph/data/render_bug37422.frag.qsb
Binary files differ
diff --git a/tests/auto/quick/scenegraph/data/render_bug37422.qml b/tests/auto/quick/scenegraph/data/render_bug37422.qml
index c1b47eddb8..09661d8ccb 100644
--- a/tests/auto/quick/scenegraph/data/render_bug37422.qml
+++ b/tests/auto/quick/scenegraph/data/render_bug37422.qml
@@ -26,7 +26,7 @@
**
****************************************************************************/
-import QtQuick 2.2
+import QtQuick 2.12
/*
The test verifies that batching does not interfere with overlapping
@@ -71,7 +71,9 @@ RenderTestBase
width: 100
height: 9
y: 10
- fragmentShader: "varying highp vec2 qt_TexCoord0; void main() { gl_FragColor = vec4(1, 0, 0, 1); }"
+ fragmentShader: GraphicsInfo.shaderType === GraphicsInfo.GLSL
+ ? "varying highp vec2 qt_TexCoord0; void main() { gl_FragColor = vec4(1, 0, 0, 1); }"
+ : "file:data/render_bug37422.frag.qsb"
Rectangle {
width: 5
diff --git a/tests/auto/quick/scenegraph/tst_scenegraph.cpp b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
index 063358c795..3f605348c3 100644
--- a/tests/auto/quick/scenegraph/tst_scenegraph.cpp
+++ b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
@@ -70,16 +70,17 @@ public:
QColor color() const { return m_color; }
- QSGNode *updatePaintNode(QSGNode *old, UpdatePaintNodeData *)
+ QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
{
- if (old)
- delete old;
-
- QSGNode *node = new QSGNode();
-
- for (int y=0; y<height(); ++y) {
- for (int x=0; x<width(); ++x) {
- QSGSimpleRectNode *rn = new QSGSimpleRectNode();
+ delete node;
+ node = new QSGNode;
+
+ const int w = int(width());
+ const int h = int(height());
+ QQuickWindow *win = window();
+ for (int y = 0; y < h; ++y) {
+ for (int x = 0; x < w; ++x) {
+ QSGRectangleNode *rn = win->createRectangleNode();
rn->setRect(x, y, 1, 1);
rn->setColor(m_color);
node->appendChildNode(rn);
@@ -90,7 +91,7 @@ public:
}
Q_SIGNALS:
- void colorChanged(const QColor &c );
+ void colorChanged(const QColor &c);
private:
QColor m_color;
@@ -117,7 +118,8 @@ private slots:
private:
bool m_brokenMipmapSupport;
QQuickView *createView(const QString &file, QWindow *parent = nullptr, int x = -1, int y = -1, int w = -1, int h = -1);
- bool isRunningOnOpenGL();
+ bool isRunningOnOpenGLDirectly();
+ bool isRunningOnRhi();
};
template <typename T> class ScopedList : public QList<T> {
@@ -402,7 +404,7 @@ void tst_SceneGraph::render_data()
<< "render_bug37422.qml"
<< "render_OpacityThroughBatchRoot.qml";
if (!m_brokenMipmapSupport)
- files << "render_Mipmap.qml";
+ files << "render_Mipmap.qml";
QRegExp sampleCount("#samples: *(\\d+)");
// X:int Y:int R:float G:float B:float Error:float
@@ -444,8 +446,8 @@ void tst_SceneGraph::render_data()
void tst_SceneGraph::render()
{
- if (!isRunningOnOpenGL())
- QSKIP("Skipping complex rendering tests due to not running with OpenGL");
+ if (!isRunningOnOpenGLDirectly() && !isRunningOnRhi())
+ QSKIP("Skipping complex rendering tests due to not running with OpenGL or QRhi");
QFETCH(QString, file);
QFETCH(QList<Sample>, baseStage);
@@ -495,7 +497,7 @@ void tst_SceneGraph::render()
// current on the other window.
void tst_SceneGraph::hideWithOtherContext()
{
- if (!isRunningOnOpenGL())
+ if (!isRunningOnOpenGLDirectly())
QSKIP("Skipping OpenGL context test due to not running with OpenGL");
QWindow window;
@@ -559,15 +561,37 @@ void tst_SceneGraph::createTextureFromImage()
QCOMPARE(texture->hasAlphaChannel(), expectedAlpha);
}
-bool tst_SceneGraph::isRunningOnOpenGL()
+bool tst_SceneGraph::isRunningOnOpenGLDirectly()
{
- bool retval = false;
- QQuickView dummy;
- dummy.show();
- QTest::qWaitForWindowExposed(&dummy);
- if (dummy.rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL)
- retval = true;
- dummy.hide();
+ static bool retval = false;
+ static bool decided = false;
+ if (!decided) {
+ decided = true;
+ QQuickView dummy;
+ dummy.show();
+ if (QTest::qWaitForWindowExposed(&dummy))
+ retval = dummy.rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL;
+ dummy.hide();
+ }
+ return retval;
+}
+
+bool tst_SceneGraph::isRunningOnRhi()
+{
+ static bool retval = false;
+ static bool decided = false;
+ if (!decided) {
+ decided = true;
+ QQuickView dummy;
+ dummy.show();
+ if (!QTest::qWaitForWindowExposed(&dummy)) {
+ [](){ QFAIL("Could not show a QQuickView"); }();
+ return false;
+ }
+ QSGRendererInterface::GraphicsApi api = dummy.rendererInterface()->graphicsApi();
+ retval = QSGRendererInterface::isApiRhiBased(api);
+ dummy.hide();
+ }
return retval;
}
diff --git a/tests/auto/quick/shared/viewtestutil.cpp b/tests/auto/quick/shared/viewtestutil.cpp
index 5631ffe047..1680e850f7 100644
--- a/tests/auto/quick/shared/viewtestutil.cpp
+++ b/tests/auto/quick/shared/viewtestutil.cpp
@@ -328,7 +328,8 @@ QQuickViewTestUtil::ListRange QQuickViewTestUtil::ListRange::operator+(const Lis
bool QQuickViewTestUtil::ListRange::operator==(const ListRange &other) const
{
- return indexes.toSet() == other.indexes.toSet();
+ return QSet<int>(indexes.cbegin(), indexes.cend())
+ == QSet<int>(other.indexes.cbegin(), other.indexes.cend());
}
bool QQuickViewTestUtil::ListRange::operator!=(const ListRange &other) const
diff --git a/tests/auto/shared/testhttpserver.cpp b/tests/auto/shared/testhttpserver.cpp
index 09f16e8635..f0d5a89984 100644
--- a/tests/auto/shared/testhttpserver.cpp
+++ b/tests/auto/shared/testhttpserver.cpp
@@ -32,6 +32,7 @@
#include <QFile>
#include <QTimer>
#include <QTest>
+#include <QQmlFile>
/*!
\internal
@@ -152,17 +153,17 @@ bool TestHTTPServer::wait(const QUrl &expect, const QUrl &reply, const QUrl &bod
m_state = AwaitingHeader;
m_data.clear();
- QFile expectFile(expect.toLocalFile());
+ QFile expectFile(QQmlFile::urlToLocalFileOrQrc(expect));
if (!expectFile.open(QIODevice::ReadOnly))
return false;
- QFile replyFile(reply.toLocalFile());
+ QFile replyFile(QQmlFile::urlToLocalFileOrQrc(reply));
if (!replyFile.open(QIODevice::ReadOnly))
return false;
m_bodyData = QByteArray();
if (body.isValid()) {
- QFile bodyFile(body.toLocalFile());
+ QFile bodyFile(QQmlFile::urlToLocalFileOrQrc(body));
if (!bodyFile.open(QIODevice::ReadOnly))
return false;
m_bodyData = bodyFile.readAll();
diff --git a/tests/auto/shared/util.h b/tests/auto/shared/util.h
index 6f3f0a06a8..2088258378 100644
--- a/tests/auto/shared/util.h
+++ b/tests/auto/shared/util.h
@@ -48,7 +48,12 @@ public:
inline QString testFile(const char *fileName) const
{ return testFile(QLatin1String(fileName)); }
inline QUrl testFileUrl(const QString &fileName) const
- { return QUrl::fromLocalFile(testFile(fileName)); }
+ {
+ const QString fn = testFile(fileName);
+ return fn.startsWith(QLatin1Char(':'))
+ ? QUrl(QLatin1String("qrc") + fn)
+ : QUrl::fromLocalFile(fn);
+ }
inline QUrl testFileUrl(const char *fileName) const
{ return testFileUrl(QLatin1String(fileName)); }
diff --git a/tests/auto/toolsupport/tst_toolsupport.cpp b/tests/auto/toolsupport/tst_toolsupport.cpp
index eec96f9174..f743a6f5c6 100644
--- a/tests/auto/toolsupport/tst_toolsupport.cpp
+++ b/tests/auto/toolsupport/tst_toolsupport.cpp
@@ -35,6 +35,7 @@
#include <private/qobject_p.h>
#include <private/qv4compileddata_p.h>
#include <private/qv4string_p.h>
+#include <private/qqmlrefcount_p.h>
#include <qobject.h>
#if defined(Q_CC_GNU) || defined(Q_CC_MSVC)
diff --git a/tests/benchmarks/qml/js/qjsengine/tst_qjsengine.cpp b/tests/benchmarks/qml/js/qjsengine/tst_qjsengine.cpp
index d13751385c..cb23d6edbd 100644
--- a/tests/benchmarks/qml/js/qjsengine/tst_qjsengine.cpp
+++ b/tests/benchmarks/qml/js/qjsengine/tst_qjsengine.cpp
@@ -452,7 +452,7 @@ void tst_QJSEngine::installTranslatorFunctions()
{
newEngine();
QBENCHMARK {
- m_engine->installTranslatorFunctions();
+ m_engine->installExtensions(QJSEngine::TranslationExtension);
}
}
@@ -471,7 +471,7 @@ void tst_QJSEngine::translation()
QFETCH(QString, text);
QFETCH(QString, fileName);
newEngine();
- m_engine->installTranslatorFunctions();
+ m_engine->installExtensions(QJSEngine::TranslationExtension);
QBENCHMARK {
(void)m_engine->evaluate(text, fileName);
diff --git a/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp b/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp
index d7c54703ad..64b909caf5 100644
--- a/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp
+++ b/tests/benchmarks/qml/librarymetrics_performance/tst_librarymetrics_performance.cpp
@@ -35,6 +35,16 @@
// This benchmark produces performance statistics
// for the standard set of elements, properties and expressions which
// are provided in the QtDeclarative library (QtQml and QtQuick).
+//
+// Note that we have hand-rolled our own benchmark harness, rather
+// than directly using QBENCHMARK, in order to avoid contaminating
+// the benchmark results with unwanted initialization or side-effects
+// and allow us to more completely isolate the required specific area
+// (compilation, instantiation, or cached type-data instantiation)
+// that we wish to benchmark.
+
+#define AVERAGE_OVER_N 10
+#define IGNORE_N_OUTLIERS 2
class ModuleApi : public QObject
{
@@ -197,6 +207,18 @@ void tst_librarymetrics_performance::metrics_data()
QTest::newRow("062) positioning - binding (with grid) positioning") << testFileUrl("data/bindingwithgridpositioning.qml");
}
+// This method is intended to benchmark the amount of time / cpu cycles
+// required to compile the given QML input.
+// Note that this deliberately does NOT include the time taken to
+// construct the QML engine.
+// Also note that between each iteration, we attempt to clean the
+// engine state (destroying and reconstructing the engine, clearing
+// the QML type registrations, etc) to simulate a cold-start environment.
+//
+// The benchmark result is expected to be dominated by QML parsing,
+// compilation, and optimization paths, and since the compiled component
+// is never instantiated, no construction or binding evaluation should
+// occur.
void tst_librarymetrics_performance::compilation()
{
QFETCH(QUrl, qmlfile);
@@ -211,37 +233,148 @@ void tst_librarymetrics_performance::compilation()
}
}
- QBENCHMARK {
+ QList<qint64> nResults;
+
+ // generate AVERAGE_OVER_N results
+ for (int i = 0; i < AVERAGE_OVER_N; ++i) {
cleanState(&e);
- QQmlComponent c(e, this);
- c.loadUrl(qmlfile); // just compile.
+ {
+ QElapsedTimer et;
+ et.start();
+ // BEGIN benchmarked code block
+ QQmlComponent c(e, this);
+ c.loadUrl(qmlfile);
+ // END benchmarked code block
+ qint64 etime = et.nsecsElapsed();
+ nResults.append(etime);
+ }
}
+
+ // sort the list
+ std::sort(nResults.begin(), nResults.end());
+
+ // remove IGNORE_N_OUTLIERS*2 from ONLY the worst end (remove gc interference)
+ for (int i = 0; i < IGNORE_N_OUTLIERS; ++i) {
+ if (!nResults.isEmpty()) nResults.removeLast();
+ if (!nResults.isEmpty()) nResults.removeLast();
+ }
+
+ // now generate an average
+ qint64 totaltime = 0;
+ if (nResults.size() == 0) nResults.append(9999);
+ for (int i = 0; i < nResults.size(); ++i)
+ totaltime += nResults.at(i);
+ double average = ((double)totaltime) / nResults.count();
+
+ // and return it as the result
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds);
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); // twice to workaround bug in QTestLib
}
+// This method is intended to benchmark the amount of time / cpu cycles
+// required to compile and instantiate the given QML input,
+// where the QML state has NOT been cleared in between each iteration.
+// Thus, cached type data should be used when instantiating the object.
+//
+// The benchmark result is expected to be dominated by QObject
+// hierarchy construction and first-time binding evaluation.
void tst_librarymetrics_performance::instantiation_cached()
{
QFETCH(QUrl, qmlfile);
+
cleanState(&e);
+ QList<qint64> nResults;
- QBENCHMARK {
+ // generate AVERAGE_OVER_N results
+ for (int i = 0; i < AVERAGE_OVER_N; ++i) {
+ QElapsedTimer et;
+ et.start();
+ // BEGIN benchmarked code block
QQmlComponent c(e, this);
- c.loadUrl(qmlfile); // just compile.
+ c.loadUrl(qmlfile);
QObject *o = c.create();
+ // END benchmarked code block
+ qint64 etime = et.nsecsElapsed();
+ nResults.append(etime);
delete o;
}
+
+ // sort the list
+ std::sort(nResults.begin(), nResults.end());
+
+ // remove IGNORE_N_OUTLIERS*2 from ONLY the worst end (remove gc interference)
+ for (int i = 0; i < IGNORE_N_OUTLIERS; ++i) {
+ if (!nResults.isEmpty()) nResults.removeLast();
+ if (!nResults.isEmpty()) nResults.removeLast();
+ }
+
+ // now generate an average
+ qint64 totaltime = 0;
+ if (nResults.size() == 0) nResults.append(9999);
+ for (int i = 0; i < nResults.size(); ++i)
+ totaltime += nResults.at(i);
+ double average = ((double)totaltime) / nResults.count();
+
+ // and return it as the result
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds);
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); // twice to workaround bug in QTestLib
}
+// This method is intended to benchmark the amount of time / cpu cycles
+// required to compile and instantiate the given QML input,
+// where the QML state has been cleared in between each iteration.
+// This will cause the engine to parse, compile and optimize the
+// input QML in each iteration. After compilation, the component
+// will be instantiated, and so QObject hierarchy construction and
+// first time binding evaluation will contribute to the result.
+//
+// The compilation phase is expected to dominate the result, however
+// in some cases (complex positioning via expensive bindings, or
+// other pathological cases) instantiation may dominate. Those
+// cases are prime candidates for further investigation...
void tst_librarymetrics_performance::instantiation()
{
QFETCH(QUrl, qmlfile);
- QBENCHMARK {
+ cleanState(&e);
+ QList<qint64> nResults;
+
+ // generate AVERAGE_OVER_N results
+ for (int i = 0; i < AVERAGE_OVER_N; ++i) {
cleanState(&e);
- QQmlComponent c(e, this);
- c.loadUrl(qmlfile); // just compile.
- QObject *o = c.create();
- delete o;
+ {
+ QElapsedTimer et;
+ et.start();
+ // BEGIN benchmarked code block
+ QQmlComponent c(e, this);
+ c.loadUrl(qmlfile);
+ QObject *o = c.create();
+ // END benchmarked code block
+ qint64 etime = et.nsecsElapsed();
+ nResults.append(etime);
+ delete o;
+ }
+ }
+
+ // sort the list
+ std::sort(nResults.begin(), nResults.end());
+
+ // remove IGNORE_N_OUTLIERS*2 from ONLY the worst end (remove gc interference)
+ for (int i = 0; i < IGNORE_N_OUTLIERS; ++i) {
+ if (!nResults.isEmpty()) nResults.removeLast();
+ if (!nResults.isEmpty()) nResults.removeLast();
}
+
+ // now generate an average
+ qint64 totaltime = 0;
+ if (nResults.size() == 0) nResults.append(9999);
+ for (int i = 0; i < nResults.size(); ++i)
+ totaltime += nResults.at(i);
+ double average = ((double)totaltime) / nResults.count();
+
+ // and return it as the result
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds);
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); // twice to workaround bug in QTestLib
}
void tst_librarymetrics_performance::positioners_data()
@@ -256,19 +389,58 @@ void tst_librarymetrics_performance::positioners_data()
QTest::newRow("07) positioning - binding (with grid) positioning") << testFileUrl("data/bindingwithgridpositioning.2.qml");
}
-// this test triggers repositioning a large number of times,
+// This method triggers repositioning a large number of times,
// so we can track the cost of different repositioning methods.
+// Note that the engine state is cleared before every iteration,
+// so the benchmark result will include the cost of compilation
+// as well as instantiation and first-time binding evaluation.
+//
+// The repositioning triggered within the QML is expected
+// to dominate the benchmark time, especially if slow-path
+// binding evaluation occurs.
void tst_librarymetrics_performance::positioners()
{
QFETCH(QUrl, qmlfile);
- QBENCHMARK {
+ cleanState(&e);
+ QList<qint64> nResults;
+
+ // generate AVERAGE_OVER_N results
+ for (int i = 0; i < AVERAGE_OVER_N; ++i) {
cleanState(&e);
- QQmlComponent c(e, this);
- c.loadUrl(qmlfile); // just compile.
- QObject *o = c.create();
- delete o;
+ {
+ QElapsedTimer et;
+ et.start();
+ // BEGIN benchmarked code block
+ QQmlComponent c(e, this);
+ c.loadUrl(qmlfile);
+ QObject *o = c.create();
+ // END benchmarked code block
+ qint64 etime = et.nsecsElapsed();
+ nResults.append(etime);
+ delete o;
+ }
}
+
+ // sort the list
+ std::sort(nResults.begin(), nResults.end());
+
+ // remove IGNORE_N_OUTLIERS*2 from ONLY the worst end (remove gc interference)
+ for (int i = 0; i < IGNORE_N_OUTLIERS; ++i) {
+ if (!nResults.isEmpty()) nResults.removeLast();
+ if (!nResults.isEmpty()) nResults.removeLast();
+ }
+
+ // now generate an average
+ qint64 totaltime = 0;
+ if (nResults.size() == 0) nResults.append(9999);
+ for (int i = 0; i < nResults.size(); ++i)
+ totaltime += nResults.at(i);
+ double average = ((double)totaltime) / nResults.count();
+
+ // and return it as the result
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds);
+ QTest::setBenchmarkResult(average, QTest::WalltimeNanoseconds); // twice to workaround bug in QTestLib
}
QTEST_MAIN(tst_librarymetrics_performance)
diff --git a/tests/benchmarks/qml/painting/paintbenchmark.cpp b/tests/benchmarks/qml/painting/paintbenchmark.cpp
index d195675ab8..1500f39c9a 100644
--- a/tests/benchmarks/qml/painting/paintbenchmark.cpp
+++ b/tests/benchmarks/qml/painting/paintbenchmark.cpp
@@ -34,7 +34,7 @@
#include <QGLWidget>
#include <QTextLayout>
#include <QVBoxLayout>
-#include <QTime>
+#include <QElapsedTimer>
#include <QDebug>
#include <QRandomGenerator>
#include <QStaticText>
@@ -328,20 +328,20 @@ public:
}
void paintEvent(QPaintEvent *) {
- static int last = 0;
+ static qint64 last = 0;
static bool firstRun = true;
if (firstRun) {
timer.start();
firstRun = false;
} else {
- int elapsed = timer.elapsed();
+ qint64 elapsed = timer.elapsed();
qDebug() << "frame elapsed:" << elapsed - last;
last = elapsed;
}
QPainter p(this);
p.fillRect(rect(), Qt::white);
p.setPen(Qt::black);
- QTime drawTimer;
+ QElapsedTimer drawTimer;
drawTimer.start();
testFunc(p);
qDebug() << "draw time" << drawTimer.elapsed();
@@ -351,7 +351,7 @@ public:
qApp->quit();
}
- QTime timer;
+ QElapsedTimer timer;
int frames;
};
diff --git a/tests/libfuzzer/qml/jsapi/evaluate/evaluate.pro b/tests/libfuzzer/qml/jsapi/evaluate/evaluate.pro
new file mode 100644
index 0000000000..301b4f606a
--- /dev/null
+++ b/tests/libfuzzer/qml/jsapi/evaluate/evaluate.pro
@@ -0,0 +1,6 @@
+QT -= gui
+QT += qml
+CONFIG += console
+CONFIG -= app_bundle
+SOURCES += main.cpp
+LIBS += -fsanitize=fuzzer
diff --git a/tests/libfuzzer/qml/jsapi/evaluate/main.cpp b/tests/libfuzzer/qml/jsapi/evaluate/main.cpp
new file mode 100644
index 0000000000..9e90ba7cbd
--- /dev/null
+++ b/tests/libfuzzer/qml/jsapi/evaluate/main.cpp
@@ -0,0 +1,43 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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 <QCoreApplication>
+#include <QJSEngine>
+
+// libfuzzer test for QJSEngine::evaluate()
+
+extern "C" int LLVMFuzzerTestOneInput(const char *Data, size_t Size) {
+ const QByteArray ba(Data, Size);
+ // avoid potential endless loops
+ if (ba.contains("for") || ba.contains("while"))
+ return 1;
+ int c = 0;
+ QCoreApplication a(c, nullptr);
+ QJSEngine().evaluate(ba);
+ return 0;
+}
diff --git a/tests/manual/nodetypes_ng/AtlasedImages.qml b/tests/manual/nodetypes_ng/AtlasedImages.qml
new file mode 100644
index 0000000000..5cb5451dfd
--- /dev/null
+++ b/tests/manual/nodetypes_ng/AtlasedImages.qml
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+
+// The images here should result in a single draw call that uses an atlas
+// texture. The ShaderEffect is then another one (and exercises having an
+// effect on an Image backed by an atlased texture).
+
+Item {
+ Row {
+ Image {
+ source: "qrc:/qt.png"
+ sourceSize: Qt.size(64, 64)
+ }
+ Image {
+ source: "qrc:/face-smile.png"
+ }
+ Image {
+ source: "qrc:/arrow-down.png"
+ }
+ Image {
+ source: "qrc:/arrow-up.png"
+ NumberAnimation on rotation {
+ from: 0; to: 360; duration: 3000
+ loops: Animation.Infinite
+ }
+ }
+ Image {
+ id: minusSign
+ source: "qrc:/minus-sign.png"
+ }
+ // Using a ShaderEffectSource would go through an extra render target
+ // texture. By specifying the Image directly as the source, no extra
+ // texture is created. However, when the source Image is atlased, extra
+ // steps are taken internally to create a non-atlased texture for the
+ // effect.
+ ShaderEffect {
+ id: eff
+ width: minusSign.width
+ height: minusSign.height
+ property variant source: minusSign
+ property real amplitude: 0.05
+ property real frequency: 20
+ property real time: 0
+ NumberAnimation on time { loops: Animation.Infinite; from: 0; to: Math.PI * 2; duration: 600 }
+ vertexShader: GraphicsInfo.shaderType === GraphicsInfo.GLSL ? "qrc:/wobble_legacy_gl.vert" : "qrc:/wobble.vert.qsb"
+ fragmentShader: GraphicsInfo.shaderType === GraphicsInfo.GLSL ? "qrc:/wobble_legacy_gl.frag" : "qrc:/wobble.frag.qsb"
+ }
+ Image {
+ source: "qrc:/plus-sign.png"
+ }
+ }
+}
diff --git a/tests/manual/nodetypes_ng/CompressedImages.qml b/tests/manual/nodetypes_ng/CompressedImages.qml
new file mode 100644
index 0000000000..b05baf8ccb
--- /dev/null
+++ b/tests/manual/nodetypes_ng/CompressedImages.qml
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ Rectangle {
+ border.color: "red"
+ border.width: 4
+ width: im1.sourceSize.width + 8
+ height: im1.sourceSize.height + 8
+ Image {
+ id: im1
+ source: "qrc:/car_etc2_nomips.ktx"
+ anchors.centerIn: parent
+ }
+ }
+
+ Rectangle {
+ anchors.centerIn: parent
+ border.color: "red"
+ border.width: 4
+ width: im2.sourceSize.width + 8
+ height: im2.sourceSize.height + 8
+ Image {
+ id: im2
+ source: "qrc:/qt_bc1_10mips.ktx"
+ anchors.centerIn: parent
+ }
+ }
+}
diff --git a/tests/manual/nodetypes_ng/DistanceFieldText.qml b/tests/manual/nodetypes_ng/DistanceFieldText.qml
new file mode 100644
index 0000000000..3a6eb4186e
--- /dev/null
+++ b/tests/manual/nodetypes_ng/DistanceFieldText.qml
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ Text {
+ id: text1
+ renderType: Text.QtRendering
+ anchors.top: parent.top
+ text: "árvíztűrő tükörfúrógép\nÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP"
+ }
+ Text {
+ renderType: Text.QtRendering
+ anchors.bottom: parent.bottom
+ text: "the quick brown fox jumps over the lazy dog\nTHE QUICK BROWN FOX JUMPS OVER THE LAZY DOG"
+ color: "red"
+ }
+ Text {
+ renderType: Text.QtRendering
+ anchors.centerIn: parent
+ text: "rotate rotate rotate"
+ font.bold: true
+ font.pointSize: 20
+ color: "green"
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ }
+
+ Row {
+ anchors.top: text1.bottom
+ anchors.margins: 10
+ Text { renderType: Text.QtRendering; font.pointSize: 24; text: "Normal" }
+ Text { renderType: Text.QtRendering; font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
+ Text { renderType: Text.QtRendering; font.pointSize: 24; text: "Outline"; style: Text.Outline; styleColor: "red" }
+ Text { renderType: Text.QtRendering; font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
+ }
+}
diff --git a/tests/manual/nodetypes_ng/Images.qml b/tests/manual/nodetypes_ng/Images.qml
new file mode 100644
index 0000000000..809a6dc74d
--- /dev/null
+++ b/tests/manual/nodetypes_ng/Images.qml
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.3
+
+Item {
+ Rectangle {
+ width: 100
+ height: 100
+ anchors.centerIn: parent
+ color: "red"
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ }
+
+ Image {
+ id: im
+ source: "qrc:/qt.png"
+ mipmap: true
+
+ // Changing the mipmap property results in...nothing but a warning, but
+ // regardless, enable the following to test.
+// Timer {
+// interval: 5000
+// onTriggered: {
+// if (im.mipmap) {
+// console.log("disabling mipmap");
+// im.mipmap = false;
+// } else {
+// console.log("enabling mipmap");
+// im.mipmap = true;
+// }
+// }
+// running: true
+// repeat: true
+// }
+
+ SequentialAnimation on scale {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 1.0
+ to: 4.0
+ duration: 2000
+ }
+ NumberAnimation {
+ from: 4.0
+ to: 0.1
+ duration: 3000
+ }
+ NumberAnimation {
+ from: 0.1
+ to: 1.0
+ duration: 1000
+ }
+ }
+
+ Image {
+ anchors.centerIn: parent
+ source: "qrc:/face-smile.png"
+ }
+ }
+
+ Image {
+ source: "qrc:/face-smile.png"
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ antialiasing: true // trigger smooth texture material
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ }
+
+ Item {
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.margins: 10
+ scale: 20
+ width: 20
+ Image { x: 0; source: "blacknwhite.png"; smooth: false } // solid black
+ Image { x: 2; source: "blacknwhite.png"; smooth: true } // fade to white on right
+ Image { x: 4; source: "blacknwhite.png"; smooth: false } // solid black
+ Image { x: 6; source: "blacknwhite.png"; smooth: true } // fade to white on right
+ }
+}
diff --git a/tests/manual/nodetypes_ng/Layers.qml b/tests/manual/nodetypes_ng/Layers.qml
new file mode 100644
index 0000000000..defab85f7e
--- /dev/null
+++ b/tests/manual/nodetypes_ng/Layers.qml
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ Rectangle {
+ color: "lightGray"
+ anchors.fill: parent
+ anchors.margins: 10
+
+ Column {
+ anchors.fill: parent
+ spacing: 10
+
+ Row {
+ width: parent.width
+ Rectangle {
+ color: "red"
+ width: 300
+ height: 100
+ layer.enabled: true
+ Text { text: "this is in a layer, going through an offscreen render target" }
+ clip: true
+ Rectangle {
+ color: "lightGreen"
+ width: 50
+ height: 50
+ x: 275
+ y: 75
+ }
+ }
+ Rectangle {
+ color: "white"
+ width: 300
+ height: 100
+ Text { text: "this is not a layer" }
+ }
+ Rectangle {
+ color: "green"
+ width: 300
+ height: 100
+ layer.enabled: true
+ Text { text: "this is another layer" }
+ Rectangle {
+ border.width: 4
+ border.color: "black"
+ anchors.centerIn: parent
+ width: 150
+ height: 50
+ layer.enabled: true
+ Text {
+ anchors.centerIn: parent
+ text: "layer in a layer"
+ }
+ }
+ Image {
+ source: "qrc:/face-smile.png"
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ }
+ }
+ }
+
+ Row {
+ width: parent.width
+ Rectangle {
+ color: "white"
+ border.color: "black"
+ border.width: 4
+ width: 300
+ height: 100
+ layer.enabled: true
+ layer.smooth: true // sets min/mag filter in the sampler to Linear
+ layer.textureSize: Qt.size(width * 2, height * 2)
+ Text { x: 10; y: 10; text: "supersampled layer\n(rendered at 2x, sampled with linear min/mag)" }
+ Rectangle {
+ width: 30
+ height: 30
+ anchors.centerIn: parent
+ color: "red"
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ }
+ }
+ Rectangle {
+ color: "white"
+ border.color: "black"
+ border.width: 4
+ width: 300
+ height: 100
+ layer.enabled: true
+ layer.samples: 4 // 4x MSAA
+ Text { x: 10; y: 10; text: "4x MSAA layer\n(rendered into multisample texture/renderbuffer,\nthen resolved into non-msaa texture)" }
+ Rectangle {
+ width: 30
+ height: 30
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.margins: 10
+ color: "red"
+ NumberAnimation on rotation { from: 360; to: 0; duration: 2000; loops: Animation.Infinite; }
+ }
+ }
+ Rectangle {
+ color: "white"
+ border.color: "black"
+ border.width: 4
+ width: 300
+ height: 100
+ layer.enabled: true
+ layer.mipmap: true
+ Text { x: 10; y: 10; text: "Mipmapped layer" }
+ Rectangle {
+ width: 30
+ height: 30
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.margins: 10
+ color: "red"
+ NumberAnimation on rotation { from: 360; to: 0; duration: 2000; loops: Animation.Infinite; }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/manual/nodetypes_ng/LotsOfNodes.qml b/tests/manual/nodetypes_ng/LotsOfNodes.qml
new file mode 100644
index 0000000000..eee1828b96
--- /dev/null
+++ b/tests/manual/nodetypes_ng/LotsOfNodes.qml
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import Stuff 1.0
+
+Item {
+ id: root
+
+ Column {
+ width: 100
+ clip: true
+ PerPixelRect { width: 100; height: 100; color: "red" }
+ PerPixelRect { width: 100; height: 100; color: "blue" }
+ }
+
+ Column {
+ x: 100
+ width: 100
+ PerPixelRect { width: 100; height: 100; color: "black" }
+ PerPixelRect { width: 100; height: 100; color: "#00ff00" }
+ }
+}
diff --git a/tests/manual/nodetypes_ng/LotsOfRects.qml b/tests/manual/nodetypes_ng/LotsOfRects.qml
new file mode 100644
index 0000000000..f20839d6c3
--- /dev/null
+++ b/tests/manual/nodetypes_ng/LotsOfRects.qml
@@ -0,0 +1,260 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ Rectangle {
+ anchors.margins: 4
+ anchors.fill: parent
+
+ // Background
+ gradient: Gradient {
+ GradientStop { position: 0; color: "steelblue" }
+ GradientStop { position: 1; color: "black" }
+ }
+
+ // Animated gradient stops.
+ // NB! Causes a full buffer rebuild on every animated change due to the geometry change!
+ Row {
+ spacing: 10
+ Repeater {
+ model: 20
+ Rectangle {
+ width: 20
+ height: 20
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: "red" }
+ GradientStop { NumberAnimation on position { from: 0.01; to: 0.99; duration: 5000; loops: Animation.Infinite } color: "yellow" }
+ GradientStop { position: 1.0; color: "green" }
+ }
+ }
+ }
+ }
+
+ // Rounded rects with border (smooth material)
+ Row {
+ spacing: 10
+ Repeater {
+ model: 5
+ Rectangle {
+ color: "blue"
+ width: 100
+ height: 50
+ y: 50
+ radius: 16
+ border.color: "red"
+ border.width: 4
+
+ SequentialAnimation on y {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 50
+ to: 150
+ duration: 7000
+ }
+ NumberAnimation {
+ from: 150
+ to: 50
+ duration: 3000
+ }
+ }
+ }
+ }
+ }
+
+ // Clip using scissor
+ Row {
+ spacing: 10
+ Repeater {
+ model: 5
+ Rectangle {
+ color: "green"
+ width: 100
+ height: 100
+ y: 150
+ NumberAnimation on y {
+ from: 150
+ to: 200
+ duration: 2000
+ loops: Animation.Infinite
+ }
+ clip: true
+ Rectangle {
+ color: "lightGreen"
+ width: 50
+ height: 50
+ x: 75
+ y: 75
+ }
+ }
+ }
+ }
+
+ // Clip using scissor
+ Row {
+ spacing: 10
+ Repeater {
+ model: 5
+ Rectangle {
+ color: "green"
+ width: 100
+ height: 100
+ y: 300
+ NumberAnimation on y {
+ from: 300
+ to: 400
+ duration: 2000
+ loops: Animation.Infinite
+ }
+ clip: true
+ Rectangle {
+ color: "lightGreen"
+ width: 50
+ height: 50
+ x: 75
+ y: 75
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ }
+ }
+ }
+ }
+
+ // Clip using stencil
+ Row {
+ spacing: 10
+ Repeater {
+ model: 5
+ Rectangle {
+ color: "green"
+ width: 100
+ height: 100
+ y: 450
+ NumberAnimation on y {
+ from: 450
+ to: 550
+ duration: 2000
+ loops: Animation.Infinite
+ }
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ clip: true
+ Rectangle {
+ color: "lightGreen"
+ width: 50
+ height: 50
+ x: 75
+ y: 75
+ }
+ }
+ }
+ }
+
+ // The signature red square with another item with animated opacity blended on top
+ Rectangle {
+ width: 100
+ height: 100
+ anchors.centerIn: parent
+ color: "red"
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+
+ Rectangle {
+ color: "gray"
+ width: 50
+ height: 50
+ anchors.centerIn: parent
+
+ SequentialAnimation on opacity {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 1.0
+ to: 0.0
+ duration: 4000
+ }
+ NumberAnimation {
+ from: 0.0
+ to: 1.0
+ duration: 4000
+ easing.type: Easing.InOutQuad
+ }
+ }
+ }
+ }
+
+ // Animated size and color.
+ // NB! Causes a full buffer rebuild on every animated change due to the geometry change!
+ Rectangle {
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ width: 10
+ height: 100
+ ColorAnimation on color {
+ from: "blue"
+ to: "purple"
+ duration: 5000
+ loops: Animation.Infinite
+ }
+ NumberAnimation on width {
+ from: 10
+ to: 300
+ duration: 5000
+ loops: Animation.Infinite
+ }
+ }
+
+ // Semi-transparent rect on top.
+ Rectangle {
+ anchors.centerIn: parent
+ opacity: 0.2
+ color: "black"
+ anchors.fill: parent
+ anchors.margins: 10
+ }
+ }
+}
diff --git a/tests/manual/nodetypes_ng/MoreWindows.qml b/tests/manual/nodetypes_ng/MoreWindows.qml
new file mode 100644
index 0000000000..1144572ebe
--- /dev/null
+++ b/tests/manual/nodetypes_ng/MoreWindows.qml
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtQuick.Window 2.12
+
+Item {
+ Rectangle {
+ x: 20
+ y: 20
+ width: 300
+ height: 120
+ color: "red"
+ border.color: "black"
+ border.width: 2
+ Text {
+ text: "Click to toggle window visibility\n(switch to another test to destroy)"
+ font.bold: true
+ anchors.centerIn: parent
+ }
+ MouseArea {
+ anchors.fill: parent
+ onClicked: win.visible = !win.visible
+ }
+ }
+
+ Rectangle {
+ width: 100
+ height: 100
+ anchors.centerIn: parent
+ color: "green"
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ }
+
+ Window {
+ id: win
+ width: 640
+ height: 480
+
+ Rectangle {
+ color: "lightGray"
+ anchors.fill: parent
+ Rectangle {
+ width: 100
+ height: 100
+ anchors.centerIn: parent
+ color: "red"
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ }
+ Text {
+ text: "Another QQuickWindow"
+ anchors.top: parent.top
+ anchors.margins: 20
+ ColorAnimation on color { from: "red"; to: "green"; duration: 2000; loops: Animation.Infinite }
+ }
+ }
+ }
+}
diff --git a/tests/manual/nodetypes_ng/MultiClipRects.qml b/tests/manual/nodetypes_ng/MultiClipRects.qml
new file mode 100644
index 0000000000..2d3804af21
--- /dev/null
+++ b/tests/manual/nodetypes_ng/MultiClipRects.qml
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ Rectangle {
+ anchors.margins: 4
+ anchors.fill: parent
+
+ // Background
+ gradient: Gradient {
+ GradientStop { position: 0; color: "steelblue" }
+ GradientStop { position: 1; color: "black" }
+ }
+
+ // Clip using scissor, up to 2 levels. This means that the
+ // lightGreen-yellow-blue batch's clip list will have two clips.
+ Row {
+ spacing: 10
+ Repeater {
+ model: 5
+ Rectangle {
+ color: "green"
+ width: 150
+ height: 150
+ y: 200
+ clip: true
+ Rectangle {
+ color: "lightGreen"
+ width: 150
+ height: 150
+ x: 25
+ y: 25
+ clip: true
+
+ Rectangle {
+ color: "yellow"
+ width: 50
+ height: 50
+ x: 100
+ y: 100
+ NumberAnimation on rotation { from: 360; to: 0; duration: 5000; loops: Animation.Infinite; }
+ }
+
+ Rectangle {
+ color: "blue"
+ width: 50
+ height: 50
+ x: -25
+ y: 100
+ NumberAnimation on rotation { from: 360; to: 0; duration: 5000; loops: Animation.Infinite; }
+ }
+ }
+ }
+ }
+ }
+
+ // Clip using stencil, up to 3 levels. This means that the
+ // lightGreen-yellow-blue batch's clip list will have three clips and
+ // so two stencil draw calls before drawing the actual content.
+ Row {
+ spacing: 10
+ Repeater {
+ model: 5
+ Rectangle {
+ color: "green"
+ width: 200
+ height: 200
+ y: 450
+ NumberAnimation on rotation { from: 0; to: 360; duration: 5000; loops: Animation.Infinite; }
+ clip: true
+ Rectangle {
+ color: "lightGreen"
+ width: 150
+ height: 150
+ x: 50
+ y: 50
+ rotation: 30
+ clip: true
+
+ Rectangle {
+ color: "yellow"
+ width: 100
+ height: 100
+ x: 75
+ y: 75
+ NumberAnimation on rotation { from: 360; to: 0; duration: 5000; loops: Animation.Infinite; }
+ clip: true
+
+ Rectangle {
+ color: "blue"
+ width: 50
+ height: 50
+ x: 0
+ y: 0
+ NumberAnimation on rotation { from: 360; to: 0; duration: 5000; loops: Animation.Infinite; }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/manual/nodetypes_ng/Painter.qml b/tests/manual/nodetypes_ng/Painter.qml
new file mode 100644
index 0000000000..c5db3496f8
--- /dev/null
+++ b/tests/manual/nodetypes_ng/Painter.qml
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import Stuff 1.0
+
+Item {
+ ListModel {
+ id: balloonModel
+ ListElement {
+ balloonWidth: 200
+ }
+ ListElement {
+ balloonWidth: 120
+ }
+ ListElement {
+ balloonWidth: 120
+ }
+ ListElement {
+ balloonWidth: 120
+ }
+ ListElement {
+ balloonWidth: 120
+ }
+ }
+
+ ListView {
+ anchors.fill: parent
+ anchors.margins: 10
+ id: balloonView
+ model: balloonModel
+ spacing: 5
+ delegate: TextBalloon {
+ anchors.right: index % 2 == 0 ? undefined : parent.right
+ height: 60
+ rightAligned: index % 2 == 0 ? false : true
+ width: balloonWidth
+ innerAnim: model.index === 1
+ NumberAnimation on width {
+ from: 200
+ to: 300
+ duration: 5000
+ running: model.index === 0
+ }
+ }
+ }
+}
diff --git a/tests/manual/nodetypes_ng/Rects.qml b/tests/manual/nodetypes_ng/Rects.qml
new file mode 100644
index 0000000000..b370fc7b27
--- /dev/null
+++ b/tests/manual/nodetypes_ng/Rects.qml
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ Rectangle {
+ width: 100
+ height: 100
+ anchors.centerIn: parent
+ color: "red"
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+
+ Rectangle {
+ color: "gray"
+ width: 50
+ height: 50
+ anchors.centerIn: parent
+
+ SequentialAnimation on opacity {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 1.0
+ to: 0.0
+ duration: 4000
+ }
+ NumberAnimation {
+ from: 0.0
+ to: 1.0
+ duration: 4000
+ easing.type: Easing.InOutQuad
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ color: "green"
+ width: 100
+ height: 200
+ x: 0
+ y: 0
+
+ NumberAnimation on x {
+ from: 0
+ to: 300
+ duration: 5000
+ }
+ NumberAnimation on y {
+ from: 0
+ to: 50
+ duration: 2000
+ }
+
+ clip: true // scissor
+ Rectangle {
+ color: "lightGreen"
+ width: 50
+ height: 50
+ x: 75
+ y: 175
+ }
+ }
+
+ Rectangle {
+ color: "blue"
+ width: 200
+ height: 100
+ x: 100
+ y: 300
+ radius: 16
+ border.color: "red"
+ border.width: 4
+
+ SequentialAnimation on y {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 300
+ to: 500
+ duration: 7000
+ }
+ NumberAnimation {
+ from: 500
+ to: 300
+ duration: 3000
+ }
+ }
+ }
+
+ Rectangle {
+ anchors.right: parent.right
+ width: 100
+ height: 100
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: "red" }
+ GradientStop { position: 0.33; color: "yellow" }
+ GradientStop { position: 1.0; color: "green" }
+ }
+ }
+}
diff --git a/tests/manual/nodetypes_ng/ShaderEffect.qml b/tests/manual/nodetypes_ng/ShaderEffect.qml
new file mode 100644
index 0000000000..cb2caf61a9
--- /dev/null
+++ b/tests/manual/nodetypes_ng/ShaderEffect.qml
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// Use QtQuick 2.8 to get GraphicsInfo and others
+import QtQuick 2.8
+
+Item {
+ Rectangle {
+ color: "gray"
+ anchors.margins: 10
+ anchors.fill: parent
+ Image {
+ id: image1
+ source: "qrc:/qt.png"
+ }
+ ShaderEffectSource {
+ id: effectSource1
+ sourceItem: image1
+ hideSource: true
+ }
+ ShaderEffect { // wobble
+ id: eff
+ width: image1.width
+ height: image1.height
+ anchors.centerIn: parent
+
+ property variant source: effectSource1
+ property real amplitude: 0.04 * 0.2
+ property real frequency: 20
+ property real time: 0
+
+ NumberAnimation on time { loops: Animation.Infinite; from: 0; to: Math.PI * 2; duration: 600 }
+
+ vertexShader: GraphicsInfo.shaderType === GraphicsInfo.GLSL ? "qrc:/wobble_legacy_gl.vert" : "qrc:/wobble.vert.qsb"
+ fragmentShader: GraphicsInfo.shaderType === GraphicsInfo.GLSL ? "qrc:/wobble_legacy_gl.frag" : "qrc:/wobble.frag.qsb"
+ }
+
+ Image {
+ id: image2
+ source: "qrc:/face-smile.png"
+ }
+ ShaderEffectSource {
+ id: effectSource2
+ sourceItem: image2
+ hideSource: true
+ }
+ ShaderEffect { // dropshadow
+ id: eff2
+ width: image2.width
+ height: image2.height
+ scale: 2
+ x: 40
+ y: 40
+
+ property variant source: effectSource2
+
+ property variant shadow: ShaderEffectSource {
+ sourceItem: ShaderEffect {
+ width: eff2.width
+ height: eff2.height
+ property variant delta: Qt.size(0.0, 1.0 / height)
+ property variant source: ShaderEffectSource {
+ sourceItem: ShaderEffect {
+ id: innerEff
+ width: eff2.width
+ height: eff2.height
+ property variant delta: Qt.size(1.0 / width, 0.0)
+ property variant source: effectSource2
+ fragmentShader: GraphicsInfo.shaderType === GraphicsInfo.GLSL ? "qrc:/shadow_pass1_legacy_gl.frag" : "qrc:/shadow_pass1.frag.qsb"
+ }
+ }
+ fragmentShader: GraphicsInfo.shaderType === GraphicsInfo.GLSL ? "qrc:/shadow_pass1_legacy_gl.frag" : "qrc:/shadow_pass1.frag.qsb"
+ }
+ }
+ property real angle: 0
+ property variant offset: Qt.point(5.0 * Math.cos(angle), 5.0 * Math.sin(angle))
+ NumberAnimation on angle { loops: Animation.Infinite; from: 0; to: Math.PI * 2; duration: 6000 }
+ property variant delta: Qt.size(offset.x / width, offset.y / height)
+ property real darkness: 0.5
+ fragmentShader: GraphicsInfo.shaderType === GraphicsInfo.GLSL ? "qrc:/shadow_pass2_legacy_gl.frag" : "qrc:/shadow_pass2.frag.qsb"
+ }
+
+ Column {
+ anchors.bottom: parent.bottom
+ Text {
+ color: "yellow"
+ font.pointSize: 24
+ text: {
+ if (GraphicsInfo.api === GraphicsInfo.OpenGL)
+ "OpenGL";
+ else if (GraphicsInfo.api === GraphicsInfo.Software)
+ "Software";
+ else if (GraphicsInfo.api === GraphicsInfo.Direct3D12)
+ "D3D12";
+ else if (GraphicsInfo.api === GraphicsInfo.OpenVG)
+ "OpenVG";
+ else if (GraphicsInfo.api === GraphicsInfo.OpenGLRhi)
+ "OpenGL via QRhi";
+ else if (GraphicsInfo.api === GraphicsInfo.Direct3D11Rhi)
+ "D3D11 via QRhi";
+ else if (GraphicsInfo.api === GraphicsInfo.VulkanRhi)
+ "Vulkan via QRhi";
+ else if (GraphicsInfo.api === GraphicsInfo.MetalRhi)
+ "Metal via QRhi";
+ else if (GraphicsInfo.api === GraphicsInfo.Null)
+ "Null via QRhi";
+ else
+ "Unknown API";
+ }
+ }
+ Text {
+ color: "yellow"
+ font.pointSize: 24
+ text: "Shader effect is " + (GraphicsInfo.shaderType === GraphicsInfo.HLSL
+ ? "HLSL" : (GraphicsInfo.shaderType === GraphicsInfo.GLSL
+ ? "GLSL" : (GraphicsInfo.shaderType === GraphicsInfo.RhiShader
+ ? "QRhiShader" : "UNKNOWN"))) + " based";
+ }
+ Text {
+ text: GraphicsInfo.shaderType + " " + GraphicsInfo.shaderCompilationType + " " + GraphicsInfo.shaderSourceType
+ }
+ Text {
+ //text: eff.status + " " + eff.log
+ }
+ }
+ }
+}
diff --git a/tests/manual/nodetypes_ng/ShaderEffectNoAnim.qml b/tests/manual/nodetypes_ng/ShaderEffectNoAnim.qml
new file mode 100644
index 0000000000..638775bd2a
--- /dev/null
+++ b/tests/manual/nodetypes_ng/ShaderEffectNoAnim.qml
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+
+Item {
+ // make sure we render the scene continuously
+ Rectangle { color: "red"; width: 10; height: 10; NumberAnimation on rotation { from: 0; to: 360; loops: -1 } }
+
+ Rectangle {
+ color: "gray"
+ anchors.margins: 10
+ anchors.fill: parent
+ Image {
+ id: image1
+ source: "qrc:/qt.png"
+ }
+ ShaderEffectSource {
+ id: effectSource1
+ sourceItem: image1
+ hideSource: true
+ }
+ ShaderEffect { // wobble, no animation -> should not cause re-rendering into the texture
+ id: eff
+ width: image1.width
+ height: image1.height
+ anchors.centerIn: parent
+
+ property variant source: effectSource1
+ property real amplitude: 0.04 * 0.2
+ property real frequency: 20
+ property real time: 0
+
+ vertexShader: GraphicsInfo.shaderType === GraphicsInfo.GLSL ? "qrc:/wobble_legacy_gl.vert" : "qrc:/wobble.vert.qsb"
+ fragmentShader: GraphicsInfo.shaderType === GraphicsInfo.GLSL ? "qrc:/wobble_legacy_gl.frag" : "qrc:/wobble.frag.qsb"
+ }
+ }
+}
diff --git a/tests/manual/nodetypes_ng/ShaderEffectSource.qml b/tests/manual/nodetypes_ng/ShaderEffectSource.qml
new file mode 100644
index 0000000000..dee9477336
--- /dev/null
+++ b/tests/manual/nodetypes_ng/ShaderEffectSource.qml
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Rectangle {
+ width: 200
+ height: 100
+ gradient: Gradient {
+ GradientStop { position: 0; color: "white" }
+ GradientStop { position: 1; color: "black" }
+ }
+ Row {
+ opacity: 0.5
+ Item {
+ id: foo
+ width: 100; height: 100
+ Rectangle { x: 5; y: 5; width: 60; height: 60; color: "red" }
+ Rectangle { x: 20; y: 20; width: 60; height: 60; color: "orange" }
+ Rectangle { x: 35; y: 35; width: 60; height: 60; color: "yellow" }
+ }
+ ShaderEffectSource {
+ width: 100; height: 100
+ sourceItem: foo
+ }
+ ShaderEffectSource {
+ width: 100; height: 100
+ sourceItem: foo
+ recursive: true
+ live: true
+ }
+ }
+}
diff --git a/tests/manual/nodetypes_ng/SimpleRect.qml b/tests/manual/nodetypes_ng/SimpleRect.qml
new file mode 100644
index 0000000000..d4aa3434ba
--- /dev/null
+++ b/tests/manual/nodetypes_ng/SimpleRect.qml
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ Rectangle {
+ width: 100
+ height: 100
+ anchors.centerIn: parent
+ color: "red"
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+
+ Rectangle {
+ color: "gray"
+ width: 50
+ height: 50
+ anchors.centerIn: parent
+ }
+ }
+}
diff --git a/tests/manual/nodetypes_ng/Text.qml b/tests/manual/nodetypes_ng/Text.qml
new file mode 100644
index 0000000000..1741f7e5ab
--- /dev/null
+++ b/tests/manual/nodetypes_ng/Text.qml
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ Text {
+ id: text1
+ renderType: Text.NativeRendering
+ anchors.top: parent.top
+ text: "árvíztűrő tükörfúrógép\nÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP"
+ }
+ Text {
+ renderType: Text.NativeRendering
+ anchors.bottom: parent.bottom
+ text: "the quick brown fox jumps over the lazy dog\nTHE QUICK BROWN FOX JUMPS OVER THE LAZY DOG"
+ color: "red"
+ }
+ Text {
+ renderType: Text.NativeRendering
+ anchors.centerIn: parent
+ text: "rotate rotate rotate"
+ font.bold: true
+ font.pointSize: 20
+ color: "green"
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ }
+
+ Row {
+ anchors.top: text1.bottom
+ anchors.margins: 10
+ Text { renderType: Text.NativeRendering; font.pointSize: 24; text: "Normal" }
+ Text { renderType: Text.NativeRendering; font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
+ Text { renderType: Text.NativeRendering; font.pointSize: 24; text: "Outline"; style: Text.Outline; styleColor: "red" }
+ Text { renderType: Text.NativeRendering; font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
+ }
+}
diff --git a/tests/manual/nodetypes_ng/arrow-down.png b/tests/manual/nodetypes_ng/arrow-down.png
new file mode 100644
index 0000000000..29d1d4439a
--- /dev/null
+++ b/tests/manual/nodetypes_ng/arrow-down.png
Binary files differ
diff --git a/tests/manual/nodetypes_ng/arrow-up.png b/tests/manual/nodetypes_ng/arrow-up.png
new file mode 100644
index 0000000000..e437312217
--- /dev/null
+++ b/tests/manual/nodetypes_ng/arrow-up.png
Binary files differ
diff --git a/tests/manual/nodetypes_ng/blacknwhite.png b/tests/manual/nodetypes_ng/blacknwhite.png
new file mode 100644
index 0000000000..efbc61e79d
--- /dev/null
+++ b/tests/manual/nodetypes_ng/blacknwhite.png
Binary files differ
diff --git a/tests/manual/nodetypes_ng/buildshaders.bat b/tests/manual/nodetypes_ng/buildshaders.bat
new file mode 100755
index 0000000000..328b216c07
--- /dev/null
+++ b/tests/manual/nodetypes_ng/buildshaders.bat
@@ -0,0 +1,4 @@
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o wobble.vert.qsb wobble.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o wobble.frag.qsb wobble.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o shadow_pass1.frag.qsb shadow_pass1.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o shadow_pass2.frag.qsb shadow_pass2.frag
diff --git a/tests/manual/nodetypes_ng/car_etc2_nomips.ktx b/tests/manual/nodetypes_ng/car_etc2_nomips.ktx
new file mode 100644
index 0000000000..2aefdd306b
--- /dev/null
+++ b/tests/manual/nodetypes_ng/car_etc2_nomips.ktx
Binary files differ
diff --git a/tests/manual/nodetypes_ng/face-smile.png b/tests/manual/nodetypes_ng/face-smile.png
new file mode 100644
index 0000000000..3d66d72578
--- /dev/null
+++ b/tests/manual/nodetypes_ng/face-smile.png
Binary files differ
diff --git a/tests/manual/nodetypes_ng/main.qml b/tests/manual/nodetypes_ng/main.qml
new file mode 100644
index 0000000000..938ae02c8f
--- /dev/null
+++ b/tests/manual/nodetypes_ng/main.qml
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ focus: true
+
+ Loader {
+ anchors.fill: parent
+ id: loader
+ }
+
+ Keys.onPressed: {
+ if (event.key === Qt.Key_S)
+ loader.source = "";
+
+ if (event.key === Qt.Key_R)
+ loader.source = "qrc:/SimpleRect.qml";
+ if (event.key === Qt.Key_3)
+ loader.source = "qrc:/Rects.qml";
+ if (event.key === Qt.Key_4)
+ loader.source = "qrc:/LotsOfRects.qml";
+ if (event.key === Qt.Key_5)
+ loader.source = "qrc:/MultiClipRects.qml";
+ if (event.key === Qt.Key_I)
+ loader.source = "qrc:/Images.qml";
+ if (event.key === Qt.Key_A)
+ loader.source = "qrc:/AtlasedImages.qml";
+ if (event.key === Qt.Key_P)
+ loader.source = "qrc:/Painter.qml";
+ if (event.key === Qt.Key_C)
+ loader.source = "qrc:/CompressedImages.qml";
+ if (event.key === Qt.Key_T)
+ loader.source = "qrc:/Text.qml";
+ if (event.key === Qt.Key_D)
+ loader.source = "qrc:/DistanceFieldText.qml";
+ if (event.key === Qt.Key_L)
+ loader.source = "qrc:/Layers.qml";
+ if (event.key === Qt.Key_6)
+ loader.source = "qrc:/ShaderEffectSource.qml";
+ if (event.key === Qt.Key_E)
+ loader.source = "qrc:/ShaderEffect.qml";
+ if (event.key === Qt.Key_Z)
+ loader.source = "qrc:/ShaderEffectNoAnim.qml";
+ if (event.key === Qt.Key_G)
+ helper.testGrabWindow()
+ if (event.key === Qt.Key_F)
+ helper.testGrabItem(loader.item)
+ if (event.key === Qt.Key_W)
+ loader.source = "qrc:/MoreWindows.qml";
+ if (event.key === Qt.Key_N)
+ loader.source = "qrc:/LotsOfNodes.qml";
+ }
+}
diff --git a/tests/manual/nodetypes_ng/minus-sign.png b/tests/manual/nodetypes_ng/minus-sign.png
new file mode 100644
index 0000000000..d6f233d739
--- /dev/null
+++ b/tests/manual/nodetypes_ng/minus-sign.png
Binary files differ
diff --git a/tests/manual/nodetypes_ng/nodetypes_ng.cpp b/tests/manual/nodetypes_ng/nodetypes_ng.cpp
new file mode 100644
index 0000000000..8fe0e0dc98
--- /dev/null
+++ b/tests/manual/nodetypes_ng/nodetypes_ng.cpp
@@ -0,0 +1,325 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QThread>
+#include <QQuickView>
+#include <QQmlEngine>
+#include <QQmlContext>
+#include <QQuickPaintedItem>
+#include <QPainter>
+#include <QTimer>
+#include <QQuickItemGrabResult>
+#include <QSGRectangleNode>
+
+class Helper : public QObject
+{
+ Q_OBJECT
+
+public:
+ Helper(QQuickWindow *w) : m_window(w) { }
+
+ Q_INVOKABLE void sleep(int ms) {
+ QThread::msleep(ms);
+ }
+
+ Q_INVOKABLE void testGrabWindow() {
+ QImage img = m_window->grabWindow();
+ qDebug() << "Saving image to grab_window_result.png" << img;
+ img.save("grab_window_result.png");
+ }
+
+ Q_INVOKABLE void testGrabItem(QQuickItem *item) {
+ qDebug() << item;
+ if (!item)
+ return;
+
+ QSharedPointer<QQuickItemGrabResult> result = item->grabToImage();
+ if (!result)
+ return;
+
+ auto f = [](const QImage &image) {
+ qDebug() << "Saving image to grab_item_result.png" << image;
+ image.save("grab_item_result.png");
+ };
+ if (result->image().isNull()) {
+ connect(result.data(), &QQuickItemGrabResult::ready, [f, result] {
+ f(result->image());
+ });
+ } else {
+ f(result->image());
+ }
+ }
+
+ QQuickWindow *m_window;
+};
+
+class TextBalloon : public QQuickPaintedItem
+{
+ Q_OBJECT
+ Q_PROPERTY(bool rightAligned READ isRightAligned WRITE setRightAligned NOTIFY rightAlignedChanged)
+ Q_PROPERTY(bool innerAnim READ innerAnimEnabled WRITE setInnerAnimEnabled NOTIFY innerAnimChanged)
+
+public:
+ TextBalloon(QQuickItem *parent = nullptr) : QQuickPaintedItem(parent) {
+ connect(&m_timer, &QTimer::timeout, this, &TextBalloon::onAnim);
+ m_timer.setInterval(500);
+ }
+ void paint(QPainter *painter);
+
+ bool isRightAligned() { return m_rightAligned; }
+ void setRightAligned(bool rightAligned);
+
+ bool innerAnimEnabled() const { return m_innerAnim; }
+ void setInnerAnimEnabled(bool b);
+
+signals:
+ void rightAlignedChanged();
+ void innerAnimChanged();
+
+private slots:
+ void onAnim();
+
+private:
+ bool m_rightAligned = false;
+ bool m_innerAnim = false;
+ QTimer m_timer;
+ QRect m_animRect = QRect(10, 10, 50, 20);
+ int m_anim = 0;
+};
+
+void TextBalloon::paint(QPainter *painter)
+{
+ QBrush brush(QColor("#007430"));
+
+ painter->setBrush(brush);
+ painter->setPen(Qt::NoPen);
+ painter->setRenderHint(QPainter::Antialiasing);
+
+ painter->drawRoundedRect(0, 0, boundingRect().width(), boundingRect().height() - 10, 10, 10);
+
+ if (m_rightAligned) {
+ const QPointF points[3] = {
+ QPointF(boundingRect().width() - 10.0, boundingRect().height() - 10.0),
+ QPointF(boundingRect().width() - 20.0, boundingRect().height()),
+ QPointF(boundingRect().width() - 30.0, boundingRect().height() - 10.0),
+ };
+ painter->drawConvexPolygon(points, 3);
+ } else {
+ const QPointF points[3] = {
+ QPointF(10.0, boundingRect().height() - 10.0),
+ QPointF(20.0, boundingRect().height()),
+ QPointF(30.0, boundingRect().height() - 10.0),
+ };
+ painter->drawConvexPolygon(points, 3);
+ }
+
+ if (m_innerAnim) {
+ painter->fillRect(m_animRect, Qt::lightGray);
+ const int x = m_animRect.x() + m_anim;
+ const int y = m_animRect.y() + m_animRect.height() / 2;
+ painter->setPen(QPen(QBrush(Qt::SolidLine), 4));
+ painter->drawLine(x + 4, y, x + 10, y);
+ m_anim += 10;
+ if (m_anim > m_animRect.width())
+ m_anim = 0;
+ }
+}
+
+void TextBalloon::setRightAligned(bool rightAligned)
+{
+ if (m_rightAligned == rightAligned)
+ return;
+
+ m_rightAligned = rightAligned;
+ emit rightAlignedChanged();
+}
+
+void TextBalloon::setInnerAnimEnabled(bool b)
+{
+ if (m_innerAnim == b)
+ return;
+
+ m_innerAnim = b;
+ if (!b)
+ m_timer.stop();
+ else
+ m_timer.start();
+ emit innerAnimChanged();
+}
+
+void TextBalloon::onAnim()
+{
+ update(m_animRect);
+}
+
+class PerPixelRect : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+
+public:
+ PerPixelRect();
+ void setColor(const QColor &c);
+ QColor color() const { return m_color; }
+ QSGNode *updatePaintNode(QSGNode *old, UpdatePaintNodeData *);
+
+signals:
+ void colorChanged(const QColor &c);
+
+private:
+ QColor m_color;
+};
+
+PerPixelRect::PerPixelRect()
+{
+ setFlag(ItemHasContents);
+}
+
+void PerPixelRect::setColor(const QColor &c)
+{
+ if (c == m_color)
+ return;
+ m_color = c;
+ emit colorChanged(c);
+}
+
+QSGNode *PerPixelRect::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
+{
+ delete node;
+ node = new QSGNode;
+
+ const int w = width();
+ const int h = height();
+ QQuickWindow *win = window();
+ for (int y = 0; y < h; ++y) {
+ for (int x = 0; x < w; ++x) {
+ QSGRectangleNode *rn = win->createRectangleNode();
+ rn->setRect(x, y, 1, 1);
+ rn->setColor(m_color);
+ node->appendChildNode(rn);
+ }
+ }
+
+ return node;
+}
+
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+
+ qDebug("Available tests:");
+ qDebug(" [R] - Simple rectangle (vertexcolor material)");
+ qDebug(" [3] - Rectangles (incl. smoothcolor material, scissor)");
+ qDebug(" [4] - A lot of rectangles (incl. stencil and scissor)");
+ qDebug(" [5] - Rectangles with multiple clip list entries");
+ qDebug(" [I] - Images");
+ qDebug(" [A] - Atlased images");
+ qDebug(" [P] - QQuickPaintedItem");
+ qDebug(" [C] - Compressed textures");
+ qDebug(" [T] - Text (native)");
+ qDebug(" [D] - Text (distance field)");
+ qDebug(" [L] - Layers");
+ qDebug(" [6] - ShaderEffectSource without ShaderEffect");
+ qDebug(" [E] - ShaderEffect (and GraphicsInfo)");
+ qDebug(" [Z] - ShaderEffect without animated properties");
+ qDebug(" [G] - Grab current window");
+ qDebug(" [F] - Grab item");
+ qDebug(" [W] - Multiple windows");
+ qDebug(" [N] - Lots of rectangle nodes");
+ qDebug("\nPress S to stop the currently running test\n");
+
+ QQuickView view;
+ Helper helper(&view);
+
+ const bool usingRhi = qEnvironmentVariableIntValue("QSG_RHI") != 0;
+ const QString rhiBackend = QString::fromLatin1(qgetenv("QSG_RHI_BACKEND"));
+ if (usingRhi)
+ view.setTitle(QLatin1String("RHI: ") + (rhiBackend.isEmpty() ? QLatin1String("default") : rhiBackend));
+ else
+ view.setTitle(QLatin1String("legacy OpenGL"));
+
+ if (app.arguments().contains(QLatin1String("--multisample"))) {
+ qDebug("Requesting sample count 4");
+ QSurfaceFormat fmt = view.format();
+ fmt.setSamples(4);
+ fmt.setDepthBufferSize(24);
+ fmt.setStencilBufferSize(8);
+ view.setFormat(fmt);
+ }
+ if (app.arguments().contains(QLatin1String("--coreprofile"))) {
+ qDebug("Requesting core profile (applicable only with OpenGL)");
+ QSurfaceFormat fmt = view.format();
+ fmt.setVersion(3, 2);
+ fmt.setProfile(QSurfaceFormat::CoreProfile);
+ view.setFormat(fmt);
+ }
+ if (app.arguments().contains(QLatin1String("--transparent"))) {
+ qDebug("Requesting alpha channel for the window and using Qt::transparent as background");
+ QSurfaceFormat fmt = view.format();
+ fmt.setAlphaBufferSize(8);
+ view.setFormat(fmt);
+ view.setColor(Qt::transparent);
+ }
+
+ view.engine()->rootContext()->setContextProperty(QLatin1String("helper"), &helper);
+
+ qmlRegisterType<TextBalloon>("Stuff", 1, 0, "TextBalloon");
+ qmlRegisterType<PerPixelRect>("Stuff", 1, 0, "PerPixelRect");
+
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.resize(1024, 768);
+ view.setSource(QUrl("qrc:/main.qml"));
+ view.show();
+
+ return app.exec();
+}
+
+#include "nodetypes_ng.moc"
diff --git a/tests/manual/nodetypes_ng/nodetypes_ng.pro b/tests/manual/nodetypes_ng/nodetypes_ng.pro
new file mode 100644
index 0000000000..68b9211c4f
--- /dev/null
+++ b/tests/manual/nodetypes_ng/nodetypes_ng.pro
@@ -0,0 +1,11 @@
+QT += qml quick
+
+SOURCES += nodetypes_ng.cpp
+
+RESOURCES += nodetypes_ng.qrc
+
+OTHER_FILES += \
+ main.qml \
+ SimpleRect.qml \
+ Rects.qml \
+ LotsOfRects.qml
diff --git a/tests/manual/nodetypes_ng/nodetypes_ng.qrc b/tests/manual/nodetypes_ng/nodetypes_ng.qrc
new file mode 100644
index 0000000000..47ad8d2677
--- /dev/null
+++ b/tests/manual/nodetypes_ng/nodetypes_ng.qrc
@@ -0,0 +1,38 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ <file>SimpleRect.qml</file>
+ <file>Rects.qml</file>
+ <file>LotsOfRects.qml</file>
+ <file>MultiClipRects.qml</file>
+ <file>Images.qml</file>
+ <file>Painter.qml</file>
+ <file>CompressedImages.qml</file>
+ <file>Text.qml</file>
+ <file>DistanceFieldText.qml</file>
+ <file>Layers.qml</file>
+ <file>ShaderEffectSource.qml</file>
+ <file>AtlasedImages.qml</file>
+ <file>ShaderEffect.qml</file>
+ <file>ShaderEffectNoAnim.qml</file>
+ <file>MoreWindows.qml</file>
+ <file>LotsOfNodes.qml</file>
+ <file>qt.png</file>
+ <file>face-smile.png</file>
+ <file>car_etc2_nomips.ktx</file>
+ <file>qt_bc1_10mips.ktx</file>
+ <file>arrow-down.png</file>
+ <file>arrow-up.png</file>
+ <file>minus-sign.png</file>
+ <file>plus-sign.png</file>
+ <file>blacknwhite.png</file>
+ <file>wobble.vert.qsb</file>
+ <file>wobble.frag.qsb</file>
+ <file>shadow_pass1.frag.qsb</file>
+ <file>shadow_pass2.frag.qsb</file>
+ <file>wobble_legacy_gl.vert</file>
+ <file>wobble_legacy_gl.frag</file>
+ <file>shadow_pass1_legacy_gl.frag</file>
+ <file>shadow_pass2_legacy_gl.frag</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/nodetypes_ng/plus-sign.png b/tests/manual/nodetypes_ng/plus-sign.png
new file mode 100644
index 0000000000..40df1134f8
--- /dev/null
+++ b/tests/manual/nodetypes_ng/plus-sign.png
Binary files differ
diff --git a/tests/manual/nodetypes_ng/qt.png b/tests/manual/nodetypes_ng/qt.png
new file mode 100644
index 0000000000..f30eec0d4d
--- /dev/null
+++ b/tests/manual/nodetypes_ng/qt.png
Binary files differ
diff --git a/tests/manual/nodetypes_ng/qt_bc1_10mips.ktx b/tests/manual/nodetypes_ng/qt_bc1_10mips.ktx
new file mode 100644
index 0000000000..32c31bf6dc
--- /dev/null
+++ b/tests/manual/nodetypes_ng/qt_bc1_10mips.ktx
Binary files differ
diff --git a/tests/manual/nodetypes_ng/shadow_pass1.frag b/tests/manual/nodetypes_ng/shadow_pass1.frag
new file mode 100644
index 0000000000..14581eb80e
--- /dev/null
+++ b/tests/manual/nodetypes_ng/shadow_pass1.frag
@@ -0,0 +1,23 @@
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord0;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D source;
+
+layout(std140, binding = 0) uniform buf {
+ // The built-in vertex shader assumes the first 68 bytes are matrix and
+ // opacity so have them there even though the matrix is not used here.
+ mat4 qt_Matrix;
+ float qt_Opacity;
+ vec2 delta;
+} ubuf;
+
+void main()
+{
+ fragColor = (0.0538 * texture(source, qt_TexCoord0 - 3.182 * ubuf.delta)
+ + 0.3229 * texture(source, qt_TexCoord0 - 1.364 * ubuf.delta)
+ + 0.2466 * texture(source, qt_TexCoord0)
+ + 0.3229 * texture(source, qt_TexCoord0 + 1.364 * ubuf.delta)
+ + 0.0538 * texture(source, qt_TexCoord0 + 3.182 * ubuf.delta)) * ubuf.qt_Opacity;
+}
diff --git a/tests/manual/nodetypes_ng/shadow_pass1.frag.qsb b/tests/manual/nodetypes_ng/shadow_pass1.frag.qsb
new file mode 100644
index 0000000000..f3370caee2
--- /dev/null
+++ b/tests/manual/nodetypes_ng/shadow_pass1.frag.qsb
Binary files differ
diff --git a/tests/manual/nodetypes_ng/shadow_pass1_legacy_gl.frag b/tests/manual/nodetypes_ng/shadow_pass1_legacy_gl.frag
new file mode 100644
index 0000000000..65ce0d956c
--- /dev/null
+++ b/tests/manual/nodetypes_ng/shadow_pass1_legacy_gl.frag
@@ -0,0 +1,11 @@
+uniform lowp float qt_Opacity;
+uniform sampler2D source;
+uniform highp vec2 delta;
+varying highp vec2 qt_TexCoord0;
+void main() {
+ gl_FragColor = (0.0538 * texture2D(source, qt_TexCoord0 - 3.182 * delta)
+ + 0.3229 * texture2D(source, qt_TexCoord0 - 1.364 * delta)
+ + 0.2466 * texture2D(source, qt_TexCoord0)
+ + 0.3229 * texture2D(source, qt_TexCoord0 + 1.364 * delta)
+ + 0.0538 * texture2D(source, qt_TexCoord0 + 3.182 * delta)) * qt_Opacity;
+}
diff --git a/tests/manual/nodetypes_ng/shadow_pass2.frag b/tests/manual/nodetypes_ng/shadow_pass2.frag
new file mode 100644
index 0000000000..fa11f873bb
--- /dev/null
+++ b/tests/manual/nodetypes_ng/shadow_pass2.frag
@@ -0,0 +1,23 @@
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord0;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D source;
+layout(binding = 2) uniform sampler2D shadow;
+
+layout(std140, binding = 0) uniform buf {
+ // The built-in vertex shader assumes the first 68 bytes are matrix and
+ // opacity so have them there even though the matrix is not used here.
+ mat4 qt_Matrix;
+ float qt_Opacity;
+ vec2 delta;
+ float darkness;
+} ubuf;
+
+void main()
+{
+ vec4 fg = texture(source, qt_TexCoord0);
+ vec4 bg = texture(shadow, qt_TexCoord0 + ubuf.delta);
+ fragColor = (fg + vec4(0., 0., 0., ubuf.darkness * bg.a) * (1. - fg.a)) * ubuf.qt_Opacity;
+}
diff --git a/tests/manual/nodetypes_ng/shadow_pass2.frag.qsb b/tests/manual/nodetypes_ng/shadow_pass2.frag.qsb
new file mode 100644
index 0000000000..cbf9569373
--- /dev/null
+++ b/tests/manual/nodetypes_ng/shadow_pass2.frag.qsb
Binary files differ
diff --git a/tests/manual/nodetypes_ng/shadow_pass2_legacy_gl.frag b/tests/manual/nodetypes_ng/shadow_pass2_legacy_gl.frag
new file mode 100644
index 0000000000..2ea4cc8d89
--- /dev/null
+++ b/tests/manual/nodetypes_ng/shadow_pass2_legacy_gl.frag
@@ -0,0 +1,12 @@
+uniform lowp float qt_Opacity;
+uniform highp vec2 offset;
+uniform sampler2D source;
+uniform sampler2D shadow;
+uniform highp float darkness;
+uniform highp vec2 delta;
+varying highp vec2 qt_TexCoord0;
+void main() {
+ lowp vec4 fg = texture2D(source, qt_TexCoord0);
+ lowp vec4 bg = texture2D(shadow, qt_TexCoord0 + delta);
+ gl_FragColor = (fg + vec4(0., 0., 0., darkness * bg.a) * (1. - fg.a)) * qt_Opacity;
+}
diff --git a/tests/manual/nodetypes_ng/wobble.frag b/tests/manual/nodetypes_ng/wobble.frag
new file mode 100644
index 0000000000..a34481c2f2
--- /dev/null
+++ b/tests/manual/nodetypes_ng/wobble.frag
@@ -0,0 +1,20 @@
+#version 440
+
+layout(location = 0) in vec2 qt_TexCoord0;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D source;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float qt_Opacity;
+ float amplitude;
+ float frequency;
+ float time;
+} ubuf;
+
+void main()
+{
+ vec2 p = sin(ubuf.time + ubuf.frequency * qt_TexCoord0);
+ fragColor = texture(source, qt_TexCoord0 + ubuf.amplitude * vec2(p.y, -p.x)) * ubuf.qt_Opacity;
+}
diff --git a/tests/manual/nodetypes_ng/wobble.frag.qsb b/tests/manual/nodetypes_ng/wobble.frag.qsb
new file mode 100644
index 0000000000..9d3b80fad8
--- /dev/null
+++ b/tests/manual/nodetypes_ng/wobble.frag.qsb
Binary files differ
diff --git a/tests/manual/nodetypes_ng/wobble.vert b/tests/manual/nodetypes_ng/wobble.vert
new file mode 100644
index 0000000000..a49b2d9a9f
--- /dev/null
+++ b/tests/manual/nodetypes_ng/wobble.vert
@@ -0,0 +1,22 @@
+#version 440
+
+layout(location = 0) in vec4 qt_Vertex;
+layout(location = 1) in vec2 qt_MultiTexCoord0;
+
+layout(location = 0) out vec2 qt_TexCoord0;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float qt_Opacity;
+ float amplitude;
+ float frequency;
+ float time;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ qt_TexCoord0 = qt_MultiTexCoord0;
+ gl_Position = ubuf.qt_Matrix * qt_Vertex;
+}
diff --git a/tests/manual/nodetypes_ng/wobble.vert.qsb b/tests/manual/nodetypes_ng/wobble.vert.qsb
new file mode 100644
index 0000000000..0f44e87feb
--- /dev/null
+++ b/tests/manual/nodetypes_ng/wobble.vert.qsb
Binary files differ
diff --git a/tests/manual/nodetypes_ng/wobble_legacy_gl.frag b/tests/manual/nodetypes_ng/wobble_legacy_gl.frag
new file mode 100644
index 0000000000..2961ca5f50
--- /dev/null
+++ b/tests/manual/nodetypes_ng/wobble_legacy_gl.frag
@@ -0,0 +1,10 @@
+uniform sampler2D source;
+uniform highp float amplitude;
+uniform highp float frequency;
+uniform highp float time;
+uniform lowp float qt_Opacity;
+varying highp vec2 qt_TexCoord0;
+void main() {
+ highp vec2 p = sin(time + frequency * qt_TexCoord0);
+ gl_FragColor = texture2D(source, qt_TexCoord0 + amplitude * vec2(p.y, -p.x)) * qt_Opacity;
+}
diff --git a/tests/manual/nodetypes_ng/wobble_legacy_gl.vert b/tests/manual/nodetypes_ng/wobble_legacy_gl.vert
new file mode 100644
index 0000000000..b2f33ab402
--- /dev/null
+++ b/tests/manual/nodetypes_ng/wobble_legacy_gl.vert
@@ -0,0 +1,8 @@
+uniform highp mat4 qt_Matrix;
+attribute highp vec4 qt_Vertex;
+attribute highp vec2 qt_MultiTexCoord0;
+varying highp vec2 qt_TexCoord0;
+void main() {
+ qt_TexCoord0 = qt_MultiTexCoord0;
+ gl_Position = qt_Matrix * qt_Vertex;
+}
diff --git a/tests/manual/pointer/content/FakeFlickable.qml b/tests/manual/pointer/content/FakeFlickable.qml
index ffb5c4e914..636399ba2c 100644
--- a/tests/manual/pointer/content/FakeFlickable.qml
+++ b/tests/manual/pointer/content/FakeFlickable.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the manual tests of the Qt Toolkit.
@@ -26,10 +26,12 @@
**
****************************************************************************/
-import QtQuick 2.12
+import QtQuick 2.14
+import Qt.labs.animation 1.0
Item {
id: root
+ objectName: "viewport"
default property alias data: __contentItem.data
property alias velocity: anim.velocity
property alias contentX: __contentItem.x // sign is reversed compared to Flickable.contentX
@@ -45,52 +47,81 @@ Item {
width: childrenRect.width
height: childrenRect.height
- property real xlimit: root.width - __contentItem.width
- property real ylimit: root.height - __contentItem.height
+ BoundaryRule on x {
+ id: xbr
+ minimum: root.width - __contentItem.width
+ maximum: 0
+ minimumOvershoot: 100
+ maximumOvershoot: 100
+ overshootFilter: BoundaryRule.Peak
+ }
- function returnToBounds() {
- if (x > 0) {
- returnXAnim.to = 0
- returnXAnim.start()
- } else if (x < xlimit) {
- returnXAnim.to = xlimit
- returnXAnim.start()
- }
- if (y > 0) {
- returnYAnim.to = 0
- returnYAnim.start()
- } else if (y < ylimit) {
- returnYAnim.to = ylimit
- returnYAnim.start()
- }
+ BoundaryRule on y {
+ id: ybr
+ minimum: root.height - __contentItem.height
+ maximum: 0
+ minimumOvershoot: 100
+ maximumOvershoot: 100
+ overshootFilter: BoundaryRule.Peak
}
DragHandler {
id: dragHandler
- onActiveChanged: if (!active) anim.restart(centroid.velocity)
+ onActiveChanged:
+ if (active) {
+ anim.stop()
+ root.flickStarted()
+ } else {
+ var vel = centroid.velocity
+ if (xbr.returnToBounds())
+ vel.x = 0
+ if (ybr.returnToBounds())
+ vel.y = 0
+ if (vel.x !== 0 || vel.y !== 0)
+ anim.restart(vel)
+ else
+ root.flickEnded()
+ }
+ }
+ WheelHandler {
+ rotationScale: 15
+ property: "x"
+ orientation: Qt.Horizontal
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ onActiveChanged:
+ // emitting signals in both instances is redundant but hard to avoid
+ // when the touchpad is flicking along both axes
+ if (active) {
+ anim.stop()
+ root.flickStarted()
+ } else {
+ xbr.returnToBounds()
+ root.flickEnded()
+ }
+ }
+ WheelHandler {
+ rotationScale: 15
+ property: "y"
+ orientation: Qt.Vertical
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ onActiveChanged:
+ if (active) {
+ anim.stop()
+ root.flickStarted()
+ } else {
+ ybr.returnToBounds()
+ root.flickEnded()
+ }
}
MomentumAnimation {
id: anim
target: __contentItem
onStarted: root.flickStarted()
onStopped: {
- __contentItem.returnToBounds()
+ xbr.returnToBounds()
+ ybr.returnToBounds()
root.flickEnded()
}
}
- NumberAnimation {
- id: returnXAnim
- target: __contentItem
- property: "x"
- duration: 200
- easing.type: Easing.OutQuad
- }
- NumberAnimation {
- id: returnYAnim
- target: __contentItem
- property: "y"
- duration: 200
- easing.type: Easing.OutQuad
- }
}
}
diff --git a/tests/manual/pointer/content/LeftDrawer.qml b/tests/manual/pointer/content/LeftDrawer.qml
new file mode 100644
index 0000000000..08f2f67b5c
--- /dev/null
+++ b/tests/manual/pointer/content/LeftDrawer.qml
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the manual tests 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$
+**
+****************************************************************************/
+import QtQuick 2.14
+import Qt.labs.animation 1.0
+import QtGraphicalEffects 1.14
+
+Item {
+ id: root
+ objectName: "LeftHandlerDrawer"
+ width: 100
+ height: 400
+ property real stickout: 4
+ property real xOpen: rect.radius * -2
+ property real xClosed: stickout - width
+ x: xClosed
+ y: 10
+
+ function close() {
+ openCloseAnimation.to = xClosed
+ openCloseAnimation.start()
+ }
+ function open() {
+ openCloseAnimation.to = xOpen
+ openCloseAnimation.start()
+ }
+
+ DragHandler {
+ id: dragHandler
+ yAxis.enabled: false
+ xAxis.minimum: -1000
+ margin: 20
+ onActiveChanged:
+ if (!active) {
+ if (xbr.returnToBounds())
+ return;
+ if (translation.x > 0)
+ open()
+ if (translation.x < 0)
+ close()
+ }
+ }
+
+ BoundaryRule on x {
+ id: xbr
+ minimum: xClosed
+ maximum: xOpen
+ minimumOvershoot: rect.radius
+ maximumOvershoot: rect.radius
+ }
+
+ NumberAnimation on x {
+ id: openCloseAnimation
+ duration: 300
+ easing { type: Easing.OutBounce; overshoot: 5 }
+ }
+
+ RectangularGlow {
+ id: effect
+ anchors.fill: parent
+ glowRadius: dragHandler.margin
+ spread: 0.2
+ color: "cyan"
+ cornerRadius: rect.radius + glowRadius
+ }
+
+ Rectangle {
+ id: rect
+ anchors.fill: parent
+ anchors.margins: 3
+ color: "#333"
+ border.color: "cyan"
+ border.width: 2
+ radius: 10
+ antialiasing: true
+ }
+}
diff --git a/tests/manual/pointer/content/Slider.qml b/tests/manual/pointer/content/Slider.qml
index c381d97c7c..beb84b176b 100644
--- a/tests/manual/pointer/content/Slider.qml
+++ b/tests/manual/pointer/content/Slider.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the manual tests of the Qt Toolkit.
@@ -26,7 +26,8 @@
**
****************************************************************************/
-import QtQuick 2.12
+import QtQuick 2.14
+import Qt.labs.animation 1.0
Item {
id: root
@@ -42,8 +43,16 @@ Item {
objectName: label.text + " DragHandler"
target: knob
xAxis.enabled: false
- yAxis.minimum: slot.y
- yAxis.maximum: slot.height + slot.y - knob.height
+ }
+
+ WheelHandler {
+ id: wheelHandler
+ objectName: label.text + " WheelHandler"
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ invertible: false // Don't let the system "natural scrolling" setting affect this
+ rotationScale: -0.5 // But make it go consistently in the same direction as the fingers or wheel, a bit slow
+ target: knob
+ property: "y"
}
Rectangle {
@@ -51,8 +60,8 @@ Item {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.margins: 10
- anchors.topMargin: 30
- anchors.bottomMargin: 30
+ anchors.topMargin: label.height + 6
+ anchors.bottomMargin: valueLabel.height + 4
anchors.horizontalCenter: parent.horizontalCenter
width: 10
color: "black"
@@ -82,10 +91,10 @@ Item {
height: root.width / 2
width: implicitWidth / implicitHeight * height
property bool programmatic: false
- property real multiplier: root.maximumValue / (dragHandler.yAxis.maximum - dragHandler.yAxis.minimum)
- onYChanged: if (!programmatic) root.value = root.maximumValue - (knob.y - dragHandler.yAxis.minimum) * multiplier
+ property real multiplier: root.maximumValue / (ybr.maximum - ybr.minimum)
+ onYChanged: if (!programmatic) root.value = root.maximumValue - (knob.y - ybr.minimum) * multiplier
transformOrigin: Item.Center
- function setValue(value) { knob.y = dragHandler.yAxis.maximum - value / knob.multiplier }
+ function setValue(value) { knob.y = ybr.maximum - value / knob.multiplier }
TapHandler {
id: tap
objectName: label.text + " TapHandler"
@@ -95,9 +104,15 @@ Item {
root.tapped
}
}
+ BoundaryRule on y {
+ id: ybr
+ minimum: slot.y
+ maximum: slot.height + slot.y - knob.height
+ }
}
Text {
+ id: valueLabel
font.pointSize: 16
color: "red"
anchors.bottom: parent.bottom
diff --git a/tests/manual/pointer/fakeFlickable.qml b/tests/manual/pointer/fakeFlickable.qml
index 284e0d1f34..be52e4dbaa 100644
--- a/tests/manual/pointer/fakeFlickable.qml
+++ b/tests/manual/pointer/fakeFlickable.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the manual tests of the Qt Toolkit.
@@ -30,39 +30,34 @@ import QtQuick 2.12
import "content"
Rectangle {
+ id: root
color: "#444"
width: 480
- height: 480
+ height: 640
FakeFlickable {
id: ff
anchors.fill: parent
+ anchors.leftMargin: 20
anchors.rightMargin: rightSB.width
- Row {
- Item {
- width: 100
- height: 400
- Slider {
- id: slider
- label: "font size"
- anchors.fill: parent
- maximumValue: 36
- value: 14
- }
- }
- Text {
- id: text
- color: "beige"
- font.family: "mono"
- font.pointSize: slider.value
- onTextChanged: console.log("text geom " + width + "x" + height +
- ", parent " + parent + " geom " + parent.width + "x" + parent.height)
- }
- }
+ Text {
+ id: text
+ color: "beige"
+ font.family: "mono"
+ font.pointSize: slider.value
+ onTextChanged: console.log("text geom " + width + "x" + height +
+ ", parent " + parent + " geom " + parent.width + "x" + parent.height)
+ }
- onFlickStarted: console.log("flick started with velocity " + velocity)
- onFlickEnded: console.log("flick ended with velocity " + velocity)
+ onFlickStarted: {
+ root.border.color = "green"
+ console.log("flick started with velocity " + velocity)
+ }
+ onFlickEnded: {
+ root.border.color = "transparent"
+ console.log("flick ended with velocity " + velocity)
+ }
Component.onCompleted: {
var request = new XMLHttpRequest()
@@ -101,4 +96,17 @@ Rectangle {
bottom: parent.bottom
}
}
+
+ LeftDrawer {
+ width: 100
+ anchors.verticalCenter: parent.verticalCenter
+ Slider {
+ id: slider
+ label: "font\nsize"
+ anchors.fill: parent
+ anchors.margins: 10
+ maximumValue: 36
+ value: 14
+ }
+ }
}
diff --git a/tests/manual/pointer/map.qml b/tests/manual/pointer/map.qml
index c400874d58..0e815ccd9c 100644
--- a/tests/manual/pointer/map.qml
+++ b/tests/manual/pointer/map.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the manual tests of the Qt Toolkit.
@@ -26,7 +26,7 @@
**
****************************************************************************/
-import QtQuick 2.12
+import QtQuick 2.14
Item {
width: 640
@@ -41,6 +41,20 @@ Item {
height: image.height
transform: Rotation { id: tilt; origin.x: width / 2; origin.y: height / 2; axis { x: 1; y: 0; z: 0 } }
+ WheelHandler {
+ id: wheelHandler
+ objectName: "vertical mouse wheel for scaling"
+ property: "scale"
+ onWheel: console.log("rotation " + event.angleDelta + " scaled " + rotation + " @ " + point.position + " => " + map.scale)
+ }
+
+ WheelHandler {
+ id: horizontalWheelHandler
+ objectName: "horizontal mouse wheel for side-scrolling"
+ property: "x"
+ orientation: Qt.Horizontal
+ }
+
Image {
id: image
anchors.centerIn: parent
diff --git a/tests/manual/pointer/pinchAndWheel.qml b/tests/manual/pointer/pinchAndWheel.qml
new file mode 100644
index 0000000000..0944717bb7
--- /dev/null
+++ b/tests/manual/pointer/pinchAndWheel.qml
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the manual tests 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$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+import Qt.labs.animation 1.0
+import "content"
+
+Rectangle {
+ id: root
+ width: 1024; height: 600
+ color: "#eee"
+
+ CheckBox {
+ id: cbTouchpadEnabled
+ x: 10; y: 6
+ label: "Touchpad wheel emulation enabled"
+ }
+
+ CheckBox {
+ id: cbHorzWheelEnabled
+ x: cbTouchpadEnabled.width + 20; y: 6
+ label: "Horizontal wheel rotation"
+ }
+
+ function getTransformationDetails(item, pinchhandler) {
+ return "\n\npinch.scale:" + pinchhandler.scale.toFixed(2)
+ + "\npinch.rotation:" + pinchhandler.rotation.toFixed(2)
+ + "°\npinch.translation:" + "(" + pinchhandler.translation.x.toFixed(2) + "," + pinchhandler.translation.y.toFixed(2) + ")"
+ + "\nscale wheel.rotation:" + scaleWheelHandler.rotation.toFixed(2)
+ + "°\nhorizontal wheel.rotation:" + horizontalRotationWheelHandler.rotation.toFixed(2)
+ + "°\ncontrol-rotation wheel.rotation:" + controlRotationWheelHandler.rotation.toFixed(2)
+ + "°\nrect.scale: " + item.scale.toFixed(2)
+ + "\nrect.rotation: " + item.rotation.toFixed(2)
+ + "°\nrect.position: " + "(" + item.x.toFixed(2) + "," + item.y.toFixed(2) + ")"
+ }
+
+ Rectangle {
+ id: transformable
+ width: parent.width - 100; height: parent.height - 100; x: 50; y: 50
+ color: "#ffe0e0e0"
+ antialiasing: true
+
+ PinchHandler {
+ id: parentPinch
+ objectName: "parent pinch"
+ minimumScale: 0.5
+ maximumScale: 3
+ }
+
+ WheelHandler {
+ id: scaleWheelHandler
+ objectName: "mouse wheel for scaling"
+ acceptedDevices: cbTouchpadEnabled.checked ? PointerDevice.Mouse | PointerDevice.TouchPad : PointerDevice.Mouse
+ acceptedModifiers: Qt.NoModifier
+ property: "scale"
+ onActiveChanged: if (!active) sbr.returnToBounds();
+ onWheel: console.log(objectName + ": rotation " + event.angleDelta.y + " scaled " + rotation + " @ " + point.position + " => " + parent.scale)
+ }
+
+ BoundaryRule on scale {
+ id: sbr
+ minimum: 0.1
+ maximum: 2
+ minimumOvershoot: 0.05
+ maximumOvershoot: 0.05
+ }
+
+ BoundaryRule on rotation {
+ id: rbr
+ minimum: -90
+ maximum: 360
+ minimumOvershoot: 10
+ maximumOvershoot: 10
+ }
+
+ WheelHandler {
+ id: horizontalRotationWheelHandler
+ enabled: cbHorzWheelEnabled.checked
+ objectName: "horizontal mouse wheel for rotation"
+ acceptedDevices: cbTouchpadEnabled.checked ? PointerDevice.Mouse | PointerDevice.TouchPad : PointerDevice.Mouse
+ acceptedModifiers: Qt.NoModifier
+ property: "rotation"
+ orientation: Qt.Horizontal
+ onActiveChanged: if (!active) rbr.returnToBounds()
+ onWheel: console.log(objectName + ": rotation " + event.angleDelta.y + " scaled " + rotation + " @ " + point.position + " => " + parent.rotation)
+ }
+
+ WheelHandler {
+ id: controlRotationWheelHandler
+ objectName: "control-mouse wheel for rotation"
+ acceptedDevices: cbTouchpadEnabled.checked ? PointerDevice.Mouse | PointerDevice.TouchPad : PointerDevice.Mouse
+ acceptedModifiers: Qt.ControlModifier
+ property: "rotation"
+ orientation: Qt.Vertical // already the default
+ // TODO returnToBounds() causes trouble because position isn't being adjusted when this happens
+ onActiveChanged: if (!active) rbr.returnToBounds()
+ onWheel: console.log(objectName + ": rotation " + event.angleDelta.y + " scaled " + rotation + " @ " + point.position + " => " + parent.rotation)
+ }
+
+ HoverHandler {
+ id: hover
+ acceptedDevices: PointerDevice.AllDevices
+ property var scenePoint: transformable.mapToItem(root, point.position.x, point.position.y)
+ }
+
+ Text {
+ text: "Pinch with 2 fingers to scale, rotate and translate\nMouse wheel to scale, Ctrl+mouse wheel or horizontal wheel to rotate"
+ + getTransformationDetails(parent, parentPinch)
+ }
+ }
+
+ Rectangle {
+ width: 1; height: parent.height
+ color: "blue"
+ x: hover.scenePoint.x
+ Text {
+ x: implicitWidth / -2; style: Text.Outline; styleColor: "white"
+ y: 30
+ color: "blue"
+ text: "outer " + parent.x.toFixed(2) + " inner " + hover.point.position.x.toFixed(2)
+ }
+ }
+
+ Rectangle {
+ width: parent.width; height: 1
+ color: "blue"
+ y: hover.scenePoint.y
+ Text {
+ x: 45
+ y: implicitHeight / -2; style: Text.Outline; styleColor: "white"
+ color: "blue"
+ text: "outer " + parent.y.toFixed(2) + " inner " + hover.point.position.y.toFixed(2)
+ }
+ }
+}
diff --git a/tests/manual/scalablepath/ShapeTestScale.qml b/tests/manual/scalablepath/ShapeTestScale.qml
new file mode 100644
index 0000000000..097ecf3a93
--- /dev/null
+++ b/tests/manual/scalablepath/ShapeTestScale.qml
@@ -0,0 +1,287 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+import QtQuick.Shapes 1.14
+
+Rectangle {
+ id: i
+ width: parent.width * 0.5
+ height: parent.height * 0.5
+ anchors.centerIn: parent
+ color: "transparent"
+ border.color: "red"
+
+ Shape {
+ id: pathLineMove
+ vendorExtensionsEnabled: false
+ anchors {
+ left: parent.left
+ top: parent.top
+ bottom: parent.verticalCenter
+ right: parent.horizontalCenter
+ }
+
+ visible: true
+
+ ShapePath {
+ id: c_sp1
+ strokeWidth: -1
+ fillColor: Qt.rgba(1,0,1,1.0)
+ scale: Qt.size(pathLineMove.width - 1, pathLineMove.height - 1)
+
+ startX: 0.5;
+ startY: 1
+
+ PathLine {
+ x: 0
+ y: 1
+ }
+ PathLine {
+ x: 0.5
+ y: 0
+ }
+ PathLine {
+ x: 1
+ y: 1
+ }
+ PathLine {
+ x: c_sp1.startX
+ y: c_sp1.startY
+ }
+
+ // Inner shape
+ PathMove {
+ x: 0.5
+ y: 0.25
+ }
+ PathLine {
+ x: 0.8
+ y: 0.8
+ }
+ PathLine {
+ x: 0.2
+ y: 0.8
+ }
+ PathLine {
+ x: 0.5
+ y: 0.25
+ }
+ }
+ }
+ Shape {
+ id: pathCurveArcQuad
+ vendorExtensionsEnabled: false
+ anchors {
+ left: parent.horizontalCenter
+ top: parent.top
+ bottom: parent.verticalCenter
+ right: parent.right
+ }
+
+ visible: true
+
+ ShapePath {
+ strokeWidth: 1
+ fillColor: "transparent"
+ strokeColor: "goldenrod"
+ scale: Qt.size(pathCurveArcQuad.width - 1, pathCurveArcQuad.height - 1)
+
+ startX: 0/400; startY: 100/400
+
+ PathCurve { x: 75/400; y: 75/400 }
+ PathCurve { x: 200/400; y: 150/400 }
+ PathCurve { x: 325/400; y: 25/400 }
+ PathCurve { x: 400/400; y: 100/400 }
+ PathMove { x: 0.5; y: 0 }
+ PathArc {
+ x: 0; y: 100 / 200
+ radiusX: 100 / 200; radiusY: 100 / 200
+ useLargeArc: true
+ }
+ PathMove { x: 0; y: 0.5 }
+ PathQuad { x: 1; y: 0.5; controlX: 0.5; controlY: 1 }
+ }
+ }
+
+
+ Shape {
+ id: pathCubicAngleArc
+ vendorExtensionsEnabled: false
+ anchors {
+ left: parent.left
+ top: parent.verticalCenter
+ bottom: parent.bottom
+ right: parent.horizontalCenter
+ }
+
+ visible: true
+
+ ShapePath {
+ strokeWidth: 1
+ fillColor: "transparent"
+ strokeColor: "deepskyblue"
+ scale: Qt.size(pathCubicAngleArc.width - 1, pathCubicAngleArc.height - 1)
+
+ startX: 20/200; startY: 0
+
+ PathCubic {
+ x: 180/200; y: 0
+ control1X: -10/200; control1Y: 90/200
+ control2X: 210/200; control2Y: 90/200
+ }
+
+ PathAngleArc {
+ centerX: 0.5; centerY: 0.5
+ radiusX: 0.45; radiusY: 0.45
+ startAngle: -180
+ sweepAngle: 234
+ moveToStart: true
+ }
+ }
+ }
+ Shape {
+ id: pathSvg
+ vendorExtensionsEnabled: false
+ anchors {
+ left: parent.horizontalCenter
+ top: parent.verticalCenter
+ bottom: parent.bottom
+ right: parent.right
+ }
+
+ visible: true
+
+ ShapePath {
+ strokeWidth: 5
+ fillColor: "transparent"
+ strokeColor: "coral"
+ scale: Qt.size((pathSvg.width - 1), (pathSvg.height - 1))
+ startX: .25; startY: .25
+ PathSvg { path: "L .75 .25 L .5 .75 z" }
+ }
+
+ ShapePath {
+ strokeWidth: 1
+ fillColor: "transparent"
+ strokeColor: "black"
+ scale: Qt.size((pathSvg.width - 1) / 200, (pathSvg.height - 1) / 200)
+ startX: 50; startY: 50
+ PathSvg { path: "L 150 50 L 100 150 z" }
+ }
+
+ ShapePath {
+ strokeColor: "red"
+ strokeWidth: 4
+ fillColor: "transparent"
+ scale: Qt.size((pathSvg.width - 1) / 500, (pathSvg.height - 1) / 500)
+ PathSvg {
+ path: "m 325.03711,0.5
+ c -26.61408,6.4494547 -49.95197,2.1018066 -76.21132,1.0771669
+ -22.26577,7.6817151 -47.96405,9.3627181 -65.67832,25.8497861
+ -15.74718,12.80008 -41.1564,19.605644 -45.74903,40.600391
+ -12.46933,17.76181 -25.36105,35.720146 -29.20117,57.999996
+ -18.709864,3.10961 -16.347355,30.83801 -22.385143,46.675
+ -6.848711,11.2677 11.07278,24.69174 -8.514666,27.97383
+ -10.266901,5.61543 -12.859313,28.96588 -13.732346,5.78143
+ 0.940083,-11.53398 -13.486195,-38.30626 -16.81701,-34.20231
+ 14.608079,7.8234 21.299281,50.52979 11.380052,48.14418
+ -3.406456,-15.12428 -26.181106,-38.29457 -31.849471,-35.62945
+ 16.851912,6.41472 35.569884,31.75215 28.172486,47.93115
+ -7.906485,-15.42757 -37.758959,-35.53783 -44.275447,-31.28685
+ 18.975831,1.7428 37.986009,20.68109 42.87115,37.14427 C
+ 42.279655,225.774 9.879724,213.57795 4.7080253,219.04989
+ 20.780803,212.57418 55.055919,239.88547 49.602579,241.25683
+ 38.186641,230.40078 6.6930104,222.77983 2.5752529,228.41774 c
+ 13.6045481,-8.33065 49.4437901,14.89041 43.5525671,14.2358
+ -9.759981,-7.96123 -43.5842921,7.36937 -17.554974,-1.20248
+ 9.464499,-3.73452 40.555672,12.80659 16.398749,5.14121
+ -9.1987,-7.28225 -39.0013156,3.37352 -14.121965,-2.12828
+ 13.244874,-0.0206 35.758428,14.62706 10.562447,6.42228
+ -10.780465,-8.4873 -47.8282254,11.10651 -21.027329,-0.003
+ 11.640859,-4.82877 52.615601,10.74471 24.234828,8.2659
+ -10.695834,-7.03902 -42.9384162,8.93905 -34.227854,5.58373
+ 9.077539,-8.56443 49.068801,-5.28097 43.06838,0.45546
+ -10.900893,-0.7118 -27.449619,17.27258 -10.00187,3.46526
+ 15.705191,-9.18198 18.344231,9.31645 1.10807,8.73907
+ -9.908444,1.77856 -21.108189,20.66671 -7.974821,4.92019
+ 15.750746,-14.10374 34.01348,2.07267 9.796961,8.69337
+ -8.17128,5.49929 -12.642664,19.13654 -3.994573,4.19708
+ 9.044753,-8.7077 23.850399,-13.64552 21.404959,4.02329
+ 12.509737,17.12562 51.158782,11.0442 45.106112,43.34009
+ -0.65006,10.05318 -3.79228,13.95389 1.62128,14.30064
+ -4.30913,8.82737 -14.652714,37.9591 2.92144,17.46024
+ 7.37972,-3.68333 -7.62399,16.24161 -7.98007,23.83761
+ -9.336865,18.77418 19.74873,-18.55943 6.62229,5.46195
+ 5.46464,-3.7389 36.23886,-19.41901 14.78167,0.58987
+ -8.59505,4.55644 29.29441,-2.99423 8.95489,6.47134 -9.22562,5.54437
+ -24.09765,26.79976 -11.73274,22.20385 -0.81685,5.4936
+ -1.58629,21.47626 2.34158,9.14886 1.61237,14.67029
+ -2.38384,25.22225 12.26908,15.1741 -4.40761,8.01039
+ -8.23679,36.91214 5.12235,17.92578 1.53454,2.99551 9.37569,3.1726
+ 7.15304,14.93579 3.51234,-11.31873 18.4607,-29.83809
+ 12.36869,-6.48005 -0.22629,16.26174 5.44303,-7.24791
+ 6.56926,10.49819 12.45412,28.9931 3.40908,-41.89883
+ 17.52051,-9.19238 3.23093,11.1924 6.53006,29.46941 7.55984,5.1249
+ 15.37236,-19.52583 4.09776,20.07416 12.64063,1.48215
+ 18.11247,-24.55068 -8.92586,38.39355 6.73828,6.62225
+ 4.55353,-6.91007 15.35028,-38.88977 12.55806,-13.78666
+ 1.05309,27.02664 11.54743,-24.40259 12.40657,6.86306
+ -1.72561,13.28253 11.85393,-24.15909 13.85568,-1.38002
+ 3.12455,8.33539 8.76536,26.46432 8.73882,5.09231 3.57025,-10.37352
+ -16.025,-37.75672 0.20707,-22.5788 -1.2458,-14.17213
+ -2.38918,-16.90145 10.85489,-6.71468 -16.57629,-17.22152
+ 0.19706,-26.08949 5.7751,-19.14889 -14.91681,-16.1674
+ 19.74174,7.19334 2.31875,-9.86869 -4.32508,-15.23278
+ 27.25228,29.12341 20.27514,18.81172 -11.97527,-18.92603
+ -17.96305,-45.80333 11.70099,-51.52566 17.19069,-9.57351
+ 31.17452,21.93154 38.50541,1.56304 16.26048,-4.6633
+ 22.3749,38.26516 24.86349,9.11316 5.94153,-9.9731 30.14313,6.97379
+ 36.34294,4.75012 7.07435,18.27732 8.06778,14.78971 11.04264,3.86016
+ 2.73754,-15.85945 28.7269,10.06391 28.09146,25.96561 3.00672,2.4754
+ 6.55025,-22.10264 11.23552,-14.43872 2.84155,-11.4823
+ -3.28976,-27.88574 4.24895,-25.5189 -0.61494,-11.53957
+ 22.83611,0.11011 10.64648,-15.28756 -6.5587,-21.38598
+ 9.32959,-3.0159 13.5107,-4.69375 -1.38592,-16.74533
+ -8.66673,-31.83316 -1.90087,-41.0875 2.39623,-15.14303
+ -12.50533,-44.45478 -4.70573,-48.49375 15.08472,3.42779
+ -20.39159,-42.17451 -1.69776,-40.85728 24.07272,21.63552
+ -3.65989,-30.10299 2.27233,-33.17152 16.90643,17.53071
+ -12.7383,-38.42821 6.79531,-21.57013 -4.50946,-21.08135
+ -2.53357,-37.43561 -15.5535,-55.59527 -11.0035,-12.40086
+ -1.87775,-7.12745 1.34831,-8.11755 C 468.27562,118.9774
+ 451.40746,102.656 430.98897,92.119168 439.06192,78.203836
+ 455.88012,60.123881 457.38638,40.337815 463.2373,23.183067
+ 450.82861,4.7342783 435.04883,22.626367 409.5188,28.206712
+ 386.3569,24.131269 365.63904,8.0954152 352.788,2.8857182
+ 338.88892,0.40735091 325.03711,0.5 Z m -219.0625,357.04297
+ -0.97656,0.88476 z"
+ }
+ }
+ }
+}
diff --git a/tests/manual/scalablepath/main.cpp b/tests/manual/scalablepath/main.cpp
new file mode 100644
index 0000000000..d35c590020
--- /dev/null
+++ b/tests/manual/scalablepath/main.cpp
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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 <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+ if (engine.rootObjects().isEmpty())
+ return -1;
+
+ return app.exec();
+}
diff --git a/tests/manual/scalablepath/main.qml b/tests/manual/scalablepath/main.qml
new file mode 100644
index 0000000000..e549a753d7
--- /dev/null
+++ b/tests/manual/scalablepath/main.qml
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+import QtQuick.Window 2.14
+
+Window {
+ visible: true
+ width: 512
+ height: 512
+ color: "black"
+
+ ShapeTestScale {
+ anchors.centerIn: parent
+
+ }
+}
diff --git a/tests/manual/scalablepath/qml.qrc b/tests/manual/scalablepath/qml.qrc
new file mode 100644
index 0000000000..a7a14beed4
--- /dev/null
+++ b/tests/manual/scalablepath/qml.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ <file>ShapeTestScale.qml</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/scalablepath/scalablepath.pro b/tests/manual/scalablepath/scalablepath.pro
new file mode 100644
index 0000000000..b95ce1de44
--- /dev/null
+++ b/tests/manual/scalablepath/scalablepath.pro
@@ -0,0 +1,5 @@
+TEMPLATE = app
+QT += quick qml
+SOURCES += main.cpp
+RESOURCES += qml.qrc
+CONFIG += c++11
diff --git a/tests/manual/scenegraph_lancelot/data/shape/shape_multiline.qml b/tests/manual/scenegraph_lancelot/data/shape/shape_multiline.qml
new file mode 100644
index 0000000000..8524915bc4
--- /dev/null
+++ b/tests/manual/scenegraph_lancelot/data/shape/shape_multiline.qml
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite 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$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+import QtQuick.Shapes 1.14
+
+Item {
+ id: root
+ width: 320
+ height: 320
+
+ Shape {
+ vendorExtensionsEnabled: false
+ anchors.fill: parent
+
+ ShapePath {
+ strokeWidth: 1
+ strokeColor: "red"
+ fillColor: Qt.rgba(1,0,0,0.3)
+ scale: Qt.size(root.width - 1, root.height - 1)
+ PathMultiline {
+ paths: [
+ [Qt.point(0.5, 0.06698),
+ Qt.point(1, 0.93301),
+ Qt.point(0, 0.93301),
+ Qt.point(0.5, 0.06698)],
+
+ [Qt.point(0.5, 0.12472),
+ Qt.point(0.95, 0.90414),
+ Qt.point(0.05, 0.90414),
+ Qt.point(0.5, 0.12472)],
+
+ [Qt.point(0.47131, 0.32986),
+ Qt.point(0.36229, 0.64789),
+ Qt.point(0.51492, 0.58590),
+ Qt.point(0.47563, 0.76014),
+ Qt.point(0.44950, 0.73590),
+ Qt.point(0.46292, 0.83392),
+ Qt.point(0.52162, 0.75190),
+ Qt.point(0.48531, 0.76230),
+ Qt.point(0.57529, 0.53189),
+ Qt.point(0.41261, 0.59189),
+ Qt.point(0.53001, 0.32786),
+ Qt.point(0.47131, 0.32986)]
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/manual/tableview/abstracttablemodel/Button.qml b/tests/manual/tableview/abstracttablemodel/Button.qml
index 2d4dcde80d..7057e33633 100644
--- a/tests/manual/tableview/abstracttablemodel/Button.qml
+++ b/tests/manual/tableview/abstracttablemodel/Button.qml
@@ -40,7 +40,7 @@
import QtQuick 2.12
Rectangle {
- width: 100
+ width: 110
height: 40
border.width: 1
color: "lightgreen"
diff --git a/tests/manual/tableview/abstracttablemodel/main.qml b/tests/manual/tableview/abstracttablemodel/main.qml
index 52967a1a0d..4b9158f03c 100644
--- a/tests/manual/tableview/abstracttablemodel/main.qml
+++ b/tests/manual/tableview/abstracttablemodel/main.qml
@@ -37,10 +37,11 @@
**
****************************************************************************/
-import QtQuick 2.12
+import QtQuick 2.14
import QtQuick.Window 2.3
import QtQml.Models 2.2
import TestTableModel 0.1
+import QtQuick.Controls 2.5
Window {
id: window
@@ -54,7 +55,7 @@ Window {
TestTableModel {
id: tableModel
rowCount: 200
- columnCount: 5000
+ columnCount: 200
}
Rectangle {
@@ -62,45 +63,150 @@ Window {
anchors.margins: 10
color: "darkgray"
- Row {
+ Column {
id: menu
x: 2
y: 2
- spacing: 1
- Button {
- text: "Add row"
- onClicked: tableModel.insertRows(selectedY, 1)
+
+ Row {
+ spacing: 1
+ Button {
+ text: "Add row"
+ onClicked: tableModel.insertRows(selectedY, 1)
+ }
+ Button {
+ text: "Remove row"
+ onClicked: tableModel.removeRows(selectedY, 1)
+ }
+ Button {
+ text: "Add column"
+ onClicked: tableModel.insertColumns(selectedX, 1)
+ }
+ Button {
+ text: "Remove column"
+ onClicked: tableModel.removeColumns(selectedX, 1)
+ }
}
- Button {
- text: "Remove row"
- onClicked: tableModel.removeRows(selectedY, 1)
+
+ Row {
+ spacing: 1
+ Button {
+ text: "fast-flick<br>center table"
+ onClicked: {
+ tableView.contentX += tableView.width * 1.2
+ }
+ }
+ Button {
+ text: "flick to end<br>center table"
+ onClicked: {
+ tableView.contentX = tableView.contentWidth - tableView.width
+ }
+ }
+ Button {
+ text: "fast-flick<br>headers"
+ onClicked: {
+ leftHeader.contentY += 1000
+ topHeader.contentX += 1000
+ }
+ }
+ Button {
+ text: "set/unset<br>master bindings"
+ onClicked: {
+ leftHeader.syncView = leftHeader.syncView ? null : tableView
+ topHeader.syncView = topHeader.syncView ? null : tableView
+ }
+ }
+ Button {
+ text: "inc space"
+ onClicked: {
+ tableView.columnSpacing += 1
+ tableView.rowSpacing += 1
+ }
+ }
+ }
+ Text {
+ text: "Selected: x:" + selectedX + ", y:" + selectedY
}
- Button {
- text: "Add column"
- onClicked: tableModel.insertColumns(selectedX, 1)
+ }
+
+ TableView {
+ id: topHeader
+ objectName: "topHeader"
+ anchors.left: tableView.left
+ width: tableView.width
+ anchors.top: menu.bottom
+ height: 30
+ clip: true
+ ScrollBar.horizontal: ScrollBar {}
+
+ model: TestTableModel {
+ rowCount: 1
+ columnCount: 200
}
- Button {
- text: "Remove column"
- onClicked: tableModel.removeColumns(selectedX, 1)
+
+ delegate: Rectangle {
+ implicitHeight: topHeader.height
+ implicitWidth: 20
+ color: "lightgray"
+ Text { text: column }
}
+
+ columnSpacing: 1
+ rowSpacing: 1
+
+ syncDirection: Qt.Horizontal
}
- Text {
- anchors.right: parent.right
- anchors.top: parent.top
- anchors.margins: 2
- text: "x:" + selectedX + ", y:" + selectedY
+
+ TableView {
+ id: leftHeader
+ objectName: "leftHeader"
+ anchors.left: parent.left
+ anchors.top: tableView.top
+ height: tableView.height
+ width: 30
+ clip: true
+ ScrollBar.vertical: ScrollBar {}
+
+ model: TestTableModel {
+ rowCount: 200
+ columnCount: 1
+ }
+
+ delegate: Rectangle {
+ implicitHeight: 50
+ implicitWidth: leftHeader.width
+ color: "lightgray"
+ Text { text: row }
+ }
+
+ columnSpacing: 1
+ rowSpacing: 1
+
+ syncDirection: Qt.Vertical
}
TableView {
id: tableView
- anchors.left: parent.left
+ objectName: "tableview"
+ anchors.left: leftHeader.right
anchors.right: parent.right
- anchors.top: menu.bottom
+ anchors.top: topHeader.bottom
anchors.bottom: parent.bottom
- anchors.margins: 2
+ width: 200
clip: true
+ columnWidthProvider: function(c) {
+ if (c > 30)
+ return 100
+ else
+ return 200;
+ }
+ ScrollBar.horizontal: ScrollBar {}
+ ScrollBar.vertical: ScrollBar {}
- model: tableModel
+ model: TestTableModel {
+ rowCount: 200
+ columnCount: 60
+ }
delegate: tableViewDelegate
columnSpacing: 1
rowSpacing: 1
diff --git a/tests/manual/tableview/tablemodel/form/RowForm.qml b/tests/manual/tableview/tablemodel/form/RowForm.qml
new file mode 100644
index 0000000000..bb03e685c0
--- /dev/null
+++ b/tests/manual/tableview/tablemodel/form/RowForm.qml
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQuick.Controls 2.3
+import QtQuick.Layouts 1.11
+
+ScrollView {
+ clip: true
+
+ function inputAsRow() {
+ return {
+ checked: checkedCheckBox.checked,
+ amount: amountSpinBox.value,
+ fruitType: fruitTypeTextField.text,
+ fruitName: fruitNameTextField.text,
+ fruitPrice: parseFloat(fruitPriceTextField.text)
+ }
+ }
+
+ default property alias content: gridLayout.children
+
+ GridLayout {
+ id: gridLayout
+ columns: 2
+
+ Label {
+ text: "checked"
+ }
+ CheckBox {
+ id: checkedCheckBox
+ }
+
+ Label {
+ text: "amount"
+ }
+ SpinBox {
+ id: amountSpinBox
+ value: 1
+ }
+
+ Label {
+ text: "fruitType"
+ }
+ TextField {
+ id: fruitTypeTextField
+ text: "Pear"
+ }
+
+ Label {
+ text: "fruitName"
+ }
+ TextField {
+ id: fruitNameTextField
+ text: "Williams"
+ }
+
+ Label {
+ text: "fruitPrice"
+ }
+ TextField {
+ id: fruitPriceTextField
+ text: "1.50"
+ }
+ }
+}
diff --git a/tests/manual/tableview/tablemodel/form/form.pro b/tests/manual/tableview/tablemodel/form/form.pro
new file mode 100644
index 0000000000..ba6f7a91b1
--- /dev/null
+++ b/tests/manual/tableview/tablemodel/form/form.pro
@@ -0,0 +1,10 @@
+TEMPLATE = app
+TARGET = form
+QT += qml quick
+SOURCES += main.cpp
+RESOURCES += main.qml RowForm.qml
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
diff --git a/tests/manual/tableview/tablemodel/form/main.cpp b/tests/manual/tableview/tablemodel/form/main.cpp
new file mode 100644
index 0000000000..2a3b90d392
--- /dev/null
+++ b/tests/manual/tableview/tablemodel/form/main.cpp
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+
+ return app.exec();
+}
diff --git a/tests/manual/tableview/tablemodel/form/main.qml b/tests/manual/tableview/tablemodel/form/main.qml
new file mode 100644
index 0000000000..6c6874fb4b
--- /dev/null
+++ b/tests/manual/tableview/tablemodel/form/main.qml
@@ -0,0 +1,290 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQuick.Controls 2.5
+import QtQuick.Layouts 1.12
+import QtQuick.Window 2.12
+import Qt.labs.qmlmodels 1.0
+
+ApplicationWindow {
+ id: window
+ width: 800
+ height: 800
+ visible: true
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ TableView {
+ id: tableView
+ boundsBehavior: Flickable.StopAtBounds
+
+ ScrollBar.horizontal: ScrollBar {}
+ ScrollBar.vertical: ScrollBar {}
+
+ Layout.minimumHeight: window.height / 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ model: TableModel {
+ TableModelColumn { display: "checked" }
+ TableModelColumn { display: "amount" }
+ TableModelColumn { display: "fruitType" }
+ TableModelColumn { display: "fruitName" }
+ TableModelColumn { display: "fruitPrice" }
+
+ // One row = one type of fruit that can be ordered
+ rows: [
+ {
+ // Each object (line) is one column,
+ // and each property in that object represents a role.
+ checked: false,
+ amount: 1,
+ fruitType: "Apple",
+ fruitName: "Granny Smith",
+ fruitPrice: 1.50
+ },
+ {
+ checked: true,
+ amount: 4,
+ fruitType: "Orange",
+ fruitName: "Navel",
+ fruitPrice: 2.50
+ },
+ {
+ checked: false,
+ amount: 1,
+ fruitType: "Banana",
+ fruitName: "Cavendish",
+ fruitPrice: 3.50
+ }
+ ]
+ }
+
+ delegate: DelegateChooser {
+ DelegateChoice {
+ column: 0
+ delegate: CheckBox {
+ objectName: "tableViewCheckBoxDelegate"
+ checked: model.display
+ onToggled: model.display = display
+ }
+ }
+ DelegateChoice {
+ column: 1
+ delegate: SpinBox {
+ objectName: "tableViewSpinBoxDelegate"
+ value: model.display
+ onValueModified: model.display = value
+ }
+ }
+ DelegateChoice {
+ delegate: TextField {
+ objectName: "tableViewTextFieldDelegate"
+ text: model.display
+ selectByMouse: true
+ implicitWidth: 140
+ onAccepted: model.display = text
+ }
+ }
+ }
+ }
+
+ TabBar {
+ id: operationTabBar
+
+ Layout.fillWidth: true
+ Layout.preferredHeight: 40
+
+ TabButton {
+ text: "Append"
+ }
+ TabButton {
+ text: "Clear"
+ }
+ TabButton {
+ text: "Insert"
+ }
+ TabButton {
+ text: "Move"
+ }
+ TabButton {
+ text: "Remove"
+ }
+ TabButton {
+ text: "Set"
+ }
+ }
+
+ StackLayout {
+ currentIndex: operationTabBar.currentIndex
+
+ ColumnLayout {
+ RowForm {
+ id: appendRowForm
+
+ Layout.fillHeight: true
+ }
+
+ Button {
+ text: "Append"
+
+ Layout.alignment: Qt.AlignRight
+
+ onClicked: tableView.model.appendRow(appendRowForm.inputAsRow())
+ }
+ }
+ ColumnLayout {
+ Button {
+ text: "Clear"
+ enabled: tableView.rows > 0
+
+ onClicked: tableView.model.clear()
+ }
+ }
+ ColumnLayout {
+ RowForm {
+ id: insertRowForm
+
+ Layout.fillHeight: true
+
+ Label {
+ text: "Insert index"
+ }
+ SpinBox {
+ id: insertIndexSpinBox
+ from: 0
+ to: tableView.rows
+ }
+ }
+
+ Button {
+ text: "Insert"
+
+ Layout.alignment: Qt.AlignRight
+
+ onClicked: tableView.model.insertRow(insertIndexSpinBox.value, insertRowForm.inputAsRow())
+ }
+ }
+ GridLayout {
+ columns: 2
+
+ Label {
+ text: "Move from index"
+ }
+ SpinBox {
+ id: moveFromIndexSpinBox
+ from: 0
+ to: tableView.rows > 0 ? tableView.rows - 1 : 0
+ }
+
+ Label {
+ text: "Move to index"
+ }
+ SpinBox {
+ id: moveToIndexSpinBox
+ from: 0
+ to: tableView.rows > 0 ? tableView.rows - 1 : 0
+ }
+
+ Label {
+ text: "Rows to move"
+ }
+ SpinBox {
+ id: rowsToMoveSpinBox
+ from: 1
+ to: tableView.rows
+ }
+
+ Button {
+ text: "Move"
+ enabled: tableView.rows > 0
+
+ Layout.alignment: Qt.AlignRight
+ Layout.columnSpan: 2
+
+ onClicked: tableView.model.moveRow(moveFromIndexSpinBox.value, moveToIndexSpinBox.value, rowsToMoveSpinBox.value)
+ }
+ }
+ GridLayout {
+ Label {
+ text: "Remove index"
+ }
+ SpinBox {
+ id: removeIndexSpinBox
+ from: 0
+ to: tableView.rows > 0 ? tableView.rows - 1 : 0
+ }
+
+ Button {
+ text: "Remove"
+ enabled: tableView.rows > 0
+
+ Layout.alignment: Qt.AlignRight
+ Layout.columnSpan: 2
+
+ onClicked: tableView.model.removeRow(removeIndexSpinBox.value)
+ }
+ }
+ ColumnLayout {
+ RowForm {
+ id: setRowForm
+
+ Layout.fillHeight: true
+
+ Label {
+ text: "Set index"
+ }
+ SpinBox {
+ id: setIndexSpinBox
+ from: 0
+ to: tableView.rows > 0 ? tableView.rows - 1 : 0
+ }
+ }
+
+ Button {
+ text: "Set"
+
+ onClicked: tableView.model.setRow(setIndexSpinBox.value, setRowForm.inputAsRow());
+ }
+ }
+ }
+ }
+}
diff --git a/tests/manual/tableview/tablemodel/json/JsonData.js b/tests/manual/tableview/tablemodel/json/JsonData.js
new file mode 100644
index 0000000000..a37233c539
--- /dev/null
+++ b/tests/manual/tableview/tablemodel/json/JsonData.js
@@ -0,0 +1,186 @@
+let drivers = [
+ {
+ "driverId":"fisichella",
+ "code":"FIS",
+ "url":"http://en.wikipedia.org/wiki/Giancarlo_Fisichella",
+ "givenName":"Giancarlo",
+ "familyName":"Fisichella",
+ "dateOfBirth":"1973-01-14",
+ "nationality":"Italian"
+ },
+ {
+ "driverId":"barrichello",
+ "code":"BAR",
+ "url":"http://en.wikipedia.org/wiki/Rubens_Barrichello",
+ "givenName":"Rubens",
+ "familyName":"Barrichello",
+ "dateOfBirth":"1972-05-23",
+ "nationality":"Brazilian"
+ },
+ {
+ "driverId":"alonso",
+ "permanentNumber":"14",
+ "code":"ALO",
+ "url":"http://en.wikipedia.org/wiki/Fernando_Alonso",
+ "givenName":"Fernando",
+ "familyName":"Alonso",
+ "dateOfBirth":"1981-07-29",
+ "nationality":"Spanish"
+ },
+ {
+ "driverId":"coulthard",
+ "code":"COU",
+ "url":"http://en.wikipedia.org/wiki/David_Coulthard",
+ "givenName":"David",
+ "familyName":"Coulthard",
+ "dateOfBirth":"1971-03-27",
+ "nationality":"British"
+ },
+ {
+ "driverId":"webber",
+ "code":"WEB",
+ "url":"http://en.wikipedia.org/wiki/Mark_Webber",
+ "givenName":"Mark",
+ "familyName":"Webber",
+ "dateOfBirth":"1976-08-27",
+ "nationality":"Australian"
+ },
+ {
+ "driverId":"montoya",
+ "code":"MON",
+ "url":"http://en.wikipedia.org/wiki/Juan_Pablo_Montoya",
+ "givenName":"Juan",
+ "familyName":"Pablo Montoya",
+ "dateOfBirth":"1975-09-20",
+ "nationality":"Colombian"
+ },
+ {
+ "driverId":"klien",
+ "code":"KLI",
+ "url":"http://en.wikipedia.org/wiki/Christian_Klien",
+ "givenName":"Christian",
+ "familyName":"Klien",
+ "dateOfBirth":"1983-02-07",
+ "nationality":"Austrian"
+ },
+ {
+ "driverId":"raikkonen",
+ "permanentNumber":"7",
+ "code":"RAI",
+ "url":"http://en.wikipedia.org/wiki/Kimi_R%C3%A4ikk%C3%B6nen",
+ "givenName":"Kimi",
+ "familyName":"Räikkönen",
+ "dateOfBirth":"1979-10-17",
+ "nationality":"Finnish"
+ },
+ {
+ "driverId":"trulli",
+ "code":"TRU",
+ "url":"http://en.wikipedia.org/wiki/Jarno_Trulli",
+ "givenName":"Jarno",
+ "familyName":"Trulli",
+ "dateOfBirth":"1974-07-13",
+ "nationality":"Italian"
+ },
+ {
+ "driverId":"massa",
+ "permanentNumber":"19",
+ "code":"MAS",
+ "url":"http://en.wikipedia.org/wiki/Felipe_Massa",
+ "givenName":"Felipe",
+ "familyName":"Massa",
+ "dateOfBirth":"1981-04-25",
+ "nationality":"Brazilian"
+ },
+ {
+ "driverId":"button",
+ "permanentNumber":"22",
+ "code":"BUT",
+ "url":"http://en.wikipedia.org/wiki/Jenson_Button",
+ "givenName":"Jenson",
+ "familyName":"Button",
+ "dateOfBirth":"1980-01-19",
+ "nationality":"British"
+ },
+ {
+ "driverId":"ralf_schumacher",
+ "code":"SCH",
+ "url":"http://en.wikipedia.org/wiki/Ralf_Schumacher",
+ "givenName":"Ralf",
+ "familyName":"Schumacher",
+ "dateOfBirth":"1975-06-30",
+ "nationality":"German"
+ },
+ {
+ "driverId":"villeneuve",
+ "code":"VIL",
+ "url":"http://en.wikipedia.org/wiki/Jacques_Villeneuve",
+ "givenName":"Jacques",
+ "familyName":"Villeneuve",
+ "dateOfBirth":"1971-04-09",
+ "nationality":"Canadian"
+ },
+ {
+ "driverId":"sato",
+ "code":"SAT",
+ "url":"http://en.wikipedia.org/wiki/Takuma_Sato",
+ "givenName":"Takuma",
+ "familyName":"Sato",
+ "dateOfBirth":"1977-01-28",
+ "nationality":"Japanese"
+ },
+ {
+ "driverId":"karthikeyan",
+ "code":"KAR",
+ "url":"http://en.wikipedia.org/wiki/Narain_Karthikeyan",
+ "givenName":"Narain",
+ "familyName":"Karthikeyan",
+ "dateOfBirth":"1977-01-14",
+ "nationality":"Indian"
+ },
+ {
+ "driverId":"monteiro",
+ "code":"TMO",
+ "url":"http://en.wikipedia.org/wiki/Tiago_Monteiro",
+ "givenName":"Tiago",
+ "familyName":"Monteiro",
+ "dateOfBirth":"1976-07-24",
+ "nationality":"Portuguese"
+ },
+ {
+ "driverId":"friesacher",
+ "code":"FRI",
+ "url":"http://en.wikipedia.org/wiki/Patrick_Friesacher",
+ "givenName":"Patrick",
+ "familyName":"Friesacher",
+ "dateOfBirth":"1980-09-26",
+ "nationality":"Austrian"
+ },
+ {
+ "driverId":"michael_schumacher",
+ "code":"MSC",
+ "url":"http://en.wikipedia.org/wiki/Michael_Schumacher",
+ "givenName":"Michael",
+ "familyName":"Schumacher",
+ "dateOfBirth":"1969-01-03",
+ "nationality":"German"
+ },
+ {
+ "driverId":"heidfeld",
+ "code":"HEI",
+ "url":"http://en.wikipedia.org/wiki/Nick_Heidfeld",
+ "givenName":"Nick",
+ "familyName":"Heidfeld",
+ "dateOfBirth":"1977-05-10",
+ "nationality":"German"
+ },
+ {
+ "driverId":"albers",
+ "code":"ALB",
+ "url":"http://en.wikipedia.org/wiki/Christijan_Albers",
+ "givenName":"Christijan",
+ "familyName":"Albers",
+ "dateOfBirth":"1979-04-16",
+ "nationality":"Dutch"
+ }
+]
diff --git a/tests/manual/tableview/tablemodel/json/json.pro b/tests/manual/tableview/tablemodel/json/json.pro
new file mode 100644
index 0000000000..2339e488aa
--- /dev/null
+++ b/tests/manual/tableview/tablemodel/json/json.pro
@@ -0,0 +1,12 @@
+TEMPLATE = app
+TARGET = json
+QT += qml quick
+SOURCES += main.cpp
+RESOURCES += \
+ main.qml \
+ JsonData.js
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
diff --git a/tests/manual/tableview/tablemodel/json/main.cpp b/tests/manual/tableview/tablemodel/json/main.cpp
new file mode 100644
index 0000000000..176b532d49
--- /dev/null
+++ b/tests/manual/tableview/tablemodel/json/main.cpp
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+
+ return app.exec();
+}
diff --git a/tests/manual/tableview/tablemodel/json/main.qml b/tests/manual/tableview/tablemodel/json/main.qml
new file mode 100644
index 0000000000..2a835459c0
--- /dev/null
+++ b/tests/manual/tableview/tablemodel/json/main.qml
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQuick.Controls 2.5
+import QtQuick.Layouts 1.12
+import QtQuick.Window 2.12
+import Qt.labs.qmlmodels 1.0
+
+import "JsonData.js" as CachedJsonData
+
+ApplicationWindow {
+ id: window
+ width: 800
+ height: 300
+ visible: true
+
+ function requestJson() {
+ let doc = new XMLHttpRequest()
+ doc.onreadystatechange = function() {
+ if (doc.readyState === XMLHttpRequest.DONE) {
+ var root = JSON.parse(doc.responseText)
+ var race = root.MRData.RaceTable.Races[0]
+ var raceResults = race.Results
+ var drivers = []
+ for (let i = 0; i < raceResults.length; ++i) {
+ drivers.push(raceResults[i].Driver)
+ }
+ tableView.model.rows = drivers
+ print(JSON.stringify(drivers))
+ }
+ }
+
+ doc.open("GET", "http://ergast.com/api/f1/2005/1/results.json")
+ doc.send()
+ }
+
+ Component.onCompleted: requestJson()
+ // Same as the data we get from ergast. Use it while developing
+ // to avoid flooding the server with requests.
+// Component.onCompleted: tableView.model.rows = CachedJsonData.drivers
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ TableView {
+ id: tableView
+ boundsBehavior: Flickable.StopAtBounds
+
+ ScrollBar.horizontal: ScrollBar {
+ policy: ScrollBar.AlwaysOn
+ }
+ ScrollBar.vertical: ScrollBar {
+ policy: ScrollBar.AlwaysOn
+ }
+
+ Layout.minimumHeight: window.height / 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ model: TableModel {
+ TableModelColumn { display: "driverId" }
+ TableModelColumn { display: "code" }
+ TableModelColumn { display: "url" }
+ TableModelColumn { display: "givenName" }
+ TableModelColumn { display: "familyName" }
+ TableModelColumn { display: "dateOfBirth" }
+ TableModelColumn { display: "nationality" }
+ }
+
+ delegate: TextField {
+ objectName: "tableViewTextFieldDelegate"
+ text: model.display
+ selectByMouse: true
+ implicitWidth: 140
+ onAccepted: model.display = text
+ }
+ }
+ }
+}
diff --git a/tests/manual/tableview/tablemodel/tablemodel.pro b/tests/manual/tableview/tablemodel/tablemodel.pro
new file mode 100644
index 0000000000..4e4eba7653
--- /dev/null
+++ b/tests/manual/tableview/tablemodel/tablemodel.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS += form
diff --git a/tools/qml/conf/content/resizeItemToWindow.qml b/tools/qml/conf/content/resizeItemToWindow.qml
new file mode 100644
index 0000000000..a645cf8ea9
--- /dev/null
+++ b/tools/qml/conf/content/resizeItemToWindow.qml
@@ -0,0 +1,70 @@
+/*****************************************************************************
+**
+** Copyright (C) 2019 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:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+*****************************************************************************/
+import QtQuick.Window 2.0
+import QtQuick 2.0
+
+Window {
+ property Item containedObject: null
+ property bool __resizeGuard: false
+ onContainedObjectChanged: {
+ if (containedObject == undefined || containedObject == null) {
+ visible = false;
+ return;
+ }
+ __resizeGuard = true
+ width = containedObject.width;
+ height = containedObject.height;
+ containedObject.parent = contentItem;
+ visible = true;
+ __resizeGuard = false
+ }
+ onWidthChanged: if (!__resizeGuard && containedObject) containedObject.width = width
+ onHeightChanged: if (!__resizeGuard && containedObject) containedObject.height = height
+}
diff --git a/tools/qml/conf/qtquick.qml b/tools/qml/conf/content/resizeWindowToItem.qml
index ef7a46eb2f..cd03e5065a 100644
--- a/tools/qml/conf/qtquick.qml
+++ b/tools/qml/conf/content/resizeWindowToItem.qml
@@ -1,6 +1,6 @@
/*****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -57,8 +57,8 @@ Window {
visible = false;
return;
}
- width = containedObject.width;
- height = containedObject.height;
+ width = Qt.binding(function() { return containedObject.width });
+ height = Qt.binding(function() { return containedObject.height });
containedObject.parent = contentItem;
visible = true;
}
diff --git a/tools/qml/conf/configuration.qml b/tools/qml/conf/default.qml
index e12fa39a3e..be44ee79b4 100644
--- a/tools/qml/conf/configuration.qml
+++ b/tools/qml/conf/default.qml
@@ -1,6 +1,6 @@
/*****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -52,6 +52,6 @@ import QmlRuntime.Config 1.0
Configuration {
PartialScene {
itemType: "QQuickItem"
- container: "qtquick.qml"
+ container: "content/resizeItemToWindow.qml"
}
}
diff --git a/tools/qml/conf/resizeToItem.qml b/tools/qml/conf/resizeToItem.qml
new file mode 100644
index 0000000000..480995a6b0
--- /dev/null
+++ b/tools/qml/conf/resizeToItem.qml
@@ -0,0 +1,57 @@
+/*****************************************************************************
+**
+** Copyright (C) 2019 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:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR 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."
+**
+** $QT_END_LICENSE$
+**
+*****************************************************************************/
+import QmlRuntime.Config 1.0
+
+Configuration {
+ PartialScene {
+ itemType: "QQuickItem"
+ container: "content/resizeWindowToItem.qml"
+ }
+}
diff --git a/tools/qml/main.cpp b/tools/qml/main.cpp
index 8cfc0eaaac..daa278457d 100644
--- a/tools/qml/main.cpp
+++ b/tools/qml/main.cpp
@@ -44,9 +44,12 @@
#include <QQmlApplicationEngine>
#include <QQmlComponent>
+#include <QCommandLineOption>
+#include <QCommandLineParser>
#include <QDir>
#include <QFile>
#include <QFileInfo>
+#include <QLoggingCategory>
#include <QStringList>
#include <QScopedPointer>
#include <QDebug>
@@ -57,6 +60,7 @@
#include <qqml.h>
#include <qqmldebug.h>
+#include <private/qmemory_p.h>
#include <private/qtqmlglobal_p.h>
#if QT_CONFIG(qml_animation)
#include <private/qabstractanimation_p.h>
@@ -65,24 +69,41 @@
#include <cstdio>
#include <cstring>
#include <cstdlib>
-
-#define VERSION_MAJ 1
-#define VERSION_MIN 1
-#define VERSION_STR "1.1"
+#include <memory>
#define FILE_OPEN_EVENT_WAIT_TIME 3000 // ms
+enum QmlApplicationType {
+ QmlApplicationTypeUnknown
+ , QmlApplicationTypeCore
+#ifdef QT_GUI_LIB
+ , QmlApplicationTypeGui
+#ifdef QT_WIDGETS_LIB
+ , QmlApplicationTypeWidget
+#endif // QT_WIDGETS_LIB
+#endif // QT_GUI_LIB
+};
+
+static QmlApplicationType applicationType =
+#ifndef QT_GUI_LIB
+ QmlApplicationTypeCore;
+#else
+ QmlApplicationTypeGui;
+#endif // QT_GUI_LIB
+
static Config *conf = nullptr;
static QQmlApplicationEngine *qae = nullptr;
#if defined(Q_OS_DARWIN) || defined(QT_GUI_LIB)
static int exitTimerId = -1;
#endif
-bool verboseMode = false;
static const QString iconResourcePath(QStringLiteral(":/qt-project.org/QmlRuntime/resources/qml-64.png"));
+static const QString confResourcePath(QStringLiteral(":/qt-project.org/QmlRuntime/conf/"));
+static bool verboseMode = false;
+static bool quietMode = false;
static void loadConf(const QString &override, bool quiet) // Terminates app on failure
{
- const QString defaultFileName = QLatin1String("configuration.qml");
+ const QString defaultFileName = QLatin1String("default.qml");
QUrl settingsUrl;
bool builtIn = false; //just for keeping track of the warning
if (override.isEmpty()) {
@@ -92,29 +113,38 @@ static void loadConf(const QString &override, bool quiet) // Terminates app on f
settingsUrl = QUrl::fromLocalFile(fi.absoluteFilePath());
} else {
// ### If different built-in configs are needed per-platform, just apply QFileSelector to the qrc conf.qml path
- settingsUrl = QUrl(QLatin1String("qrc:///qt-project.org/QmlRuntime/conf/") + defaultFileName);
+ fi.setFile(confResourcePath + defaultFileName);
+ settingsUrl = QUrl::fromLocalFile(fi.absoluteFilePath());
builtIn = true;
}
} else {
QFileInfo fi;
- fi.setFile(override);
- if (!fi.exists()) {
- printf("qml: Couldn't find required configuration file: %s\n",
- qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath())));
- exit(1);
+ fi.setFile(confResourcePath + override + QLatin1String(".qml"));
+ if (fi.exists()) {
+ settingsUrl = QUrl::fromLocalFile(fi.absoluteFilePath());
+ builtIn = true;
+ } else {
+ fi.setFile(override);
+ if (!fi.exists()) {
+ printf("qml: Couldn't find required configuration file: %s\n",
+ qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath())));
+ exit(1);
+ }
+ settingsUrl = QUrl::fromLocalFile(fi.absoluteFilePath());
}
- settingsUrl = QUrl::fromLocalFile(fi.absoluteFilePath());
}
if (!quiet) {
printf("qml: %s\n", QLibraryInfo::build());
- if (builtIn)
- printf("qml: Using built-in configuration.\n");
- else
- printf("qml: Using configuration file: %s\n",
+ if (builtIn) {
+ printf("qml: Using built-in configuration: %s\n",
+ qPrintable(override.isEmpty() ? defaultFileName : override));
+ } else {
+ printf("qml: Using configuration: %s\n",
qPrintable(settingsUrl.isLocalFile()
? QDir::toNativeSeparators(settingsUrl.toLocalFile())
: settingsUrl.toString()));
+ }
}
// TODO: When we have better engine control, ban QtQuick* imports on this engine
@@ -128,9 +158,23 @@ static void loadConf(const QString &override, bool quiet) // Terminates app on f
}
}
-#ifdef QT_GUI_LIB
+void noFilesGiven()
+{
+ if (!quietMode)
+ printf("qml: No files specified. Terminating.\n");
+ exit(1);
+}
-void noFilesGiven();
+static void listConfFiles()
+{
+ QDir confResourceDir(confResourcePath);
+ printf("%s\n", qPrintable(QCoreApplication::translate("main", "Built-in configurations:")));
+ for (const QFileInfo &fi : confResourceDir.entryInfoList(QDir::Files))
+ printf(" %s\n", qPrintable(fi.baseName()));
+ exit(0);
+}
+
+#ifdef QT_GUI_LIB
// Loads qml after receiving a QFileOpenEvent
class LoaderApplication : public QGuiApplication
@@ -179,8 +223,8 @@ public:
connect(e, &QQmlEngine::exit, this, &LoadWatcher::exit);
}
- bool earlyExit = false;
int returnCode = 0;
+ bool earlyExit = false;
public Q_SLOTS:
void checkFinished(QObject *o, const QUrl &url)
@@ -221,8 +265,8 @@ private:
void checkForWindow(QObject *o);
private:
- int expectedFileCount;
bool haveWindow = false;
+ int expectedFileCount;
};
void LoadWatcher::contain(QObject *o, const QUrl &containPath)
@@ -282,93 +326,18 @@ void quietMessageHandler(QtMsgType type, const QMessageLogContext &ctxt, const Q
exit(-1);
case QtCriticalMsg:
case QtDebugMsg:
+ case QtInfoMsg:
case QtWarningMsg:
- default:
;
}
}
-
-// ### Should command line arguments have translations? Qt creator doesn't, so maybe it's not worth it.
-enum QmlApplicationType {
- QmlApplicationTypeUnknown
- , QmlApplicationTypeCore
-#ifdef QT_GUI_LIB
- , QmlApplicationTypeGui
-#ifdef QT_WIDGETS_LIB
- , QmlApplicationTypeWidget
-#endif // QT_WIDGETS_LIB
-#endif // QT_GUI_LIB
-};
-
-#ifndef QT_GUI_LIB
-QmlApplicationType applicationType = QmlApplicationTypeCore;
-#else
-QmlApplicationType applicationType = QmlApplicationTypeGui;
-#endif // QT_GUI_LIB
-bool quietMode = false;
-void printVersion()
-{
- printf("qml binary version ");
- printf(VERSION_STR);
- printf("\nbuilt with Qt version ");
- printf(QT_VERSION_STR);
- printf("\n");
- exit(0);
-}
-
-void printUsage()
-{
- printf("Usage: qml [options] [files] [-- args]\n");
- printf("\n");
- printf("Any unknown argument before '--' will be treated as a QML file to be loaded.\n");
- printf("Any number of QML files can be loaded. They will share the same engine.\n");
- printf("'gui' application type is only available if the QtGui module is available.\n");
- printf("'widget' application type is only available if the QtWidgets module is available.\n");
- printf("\n");
- printf("General Options:\n");
- printf("\t-h, -help..................... Print this usage information and exit.\n");
- printf("\t-v, -version.................. Print the version information and exit.\n");
-#ifdef QT_GUI_LIB
-#ifndef QT_WIDGETS_LIB
- printf("\t-apptype [core|gui] .......... Select which application class to use. Default is gui.\n");
-#else
- printf("\t-apptype [core|gui|widget] ... Select which application class to use. Default is gui.\n");
-#endif // QT_WIDGETS_LIB
-#endif // QT_GUI_LIB
- printf("\t-quiet ....................... Suppress all output.\n");
- printf("\t-I [path] .................... Prepend the given path to the import paths.\n");
- printf("\t-f [file] .................... Load the given file as a QML file.\n");
- printf("\t-config [file] ............... Load the given file as the configuration file.\n");
- printf("\t-- ........................... Arguments after this one are ignored by the launcher, but may be used within the QML application.\n");
- printf("\tGL options:\n");
- printf("\t-desktop.......................Force use of desktop GL (AA_UseDesktopOpenGL)\n");
- printf("\t-gles..........................Force use of GLES (AA_UseOpenGLES)\n");
- printf("\t-software......................Force use of software rendering (AA_UseOpenGLES)\n");
- printf("\t-scaling.......................Enable High DPI scaling (AA_EnableHighDpiScaling)\n");
- printf("\t-no-scaling....................Disable High DPI scaling (AA_DisableHighDpiScaling)\n");
- printf("\tDebugging options:\n");
- printf("\t-verbose ..................... Print information about what qml is doing, like specific file urls being loaded.\n");
- printf("\t-translation [file] .......... Load the given file as the translations file.\n");
- printf("\t-dummy-data [directory] ...... Load QML files from the given directory as context properties.\n");
- printf("\t-slow-animations ............. Run all animations in slow motion.\n");
- printf("\t-fixed-animations ............ Run animations off animation tick rather than wall time.\n");
- exit(0);
-}
-
-void noFilesGiven()
-{
- if (!quietMode)
- printf("qml: No files specified. Terminating.\n");
- exit(1);
-}
-
// Called before application initialization, removes arguments it uses
void getAppFlags(int &argc, char **argv)
{
#ifdef QT_GUI_LIB
for (int i=0; i<argc; i++) {
- if (!strcmp(argv[i], "-apptype")) { // Must be done before application, as it selects application
+ if (!strcmp(argv[i], "--apptype") || !strcmp(argv[i], "-a") || !strcmp(argv[i], "-apptype")) {
applicationType = QmlApplicationTypeUnknown;
if (i+1 < argc) {
if (!strcmp(argv[i+1], "core"))
@@ -380,15 +349,6 @@ void getAppFlags(int &argc, char **argv)
applicationType = QmlApplicationTypeWidget;
#endif // QT_WIDGETS_LIB
}
-
- if (applicationType == QmlApplicationTypeUnknown) {
-#ifndef QT_WIDGETS_LIB
- printf("-apptype must be followed by one of the following: core gui\n");
-#else
- printf("-apptype must be followed by one of the following: core gui widget\n");
-#endif // QT_WIDGETS_LIB
- printUsage();
- }
for (int j=i; j<argc-2; j++)
argv[j] = argv[j+2];
argc -= 2;
@@ -440,24 +400,23 @@ static void loadDummyDataFiles(QQmlEngine &engine, const QString& directory)
int main(int argc, char *argv[])
{
getAppFlags(argc, argv);
- QCoreApplication *app = nullptr;
+ std::unique_ptr<QCoreApplication> app;
switch (applicationType) {
- case QmlApplicationTypeCore:
- app = new QCoreApplication(argc, argv);
- break;
#ifdef QT_GUI_LIB
case QmlApplicationTypeGui:
- app = new LoaderApplication(argc, argv);
+ app = qt_make_unique<LoaderApplication>(argc, argv);
break;
#ifdef QT_WIDGETS_LIB
case QmlApplicationTypeWidget:
- app = new QApplication(argc, argv);
- static_cast<QApplication *>(app)->setWindowIcon(QIcon(iconResourcePath));
+ app = qt_make_unique<QApplication>(argc, argv);
+ static_cast<QApplication *>(app.get())->setWindowIcon(QIcon(iconResourcePath));
break;
#endif // QT_WIDGETS_LIB
#endif // QT_GUI_LIB
- default:
- Q_ASSERT_X(false, Q_FUNC_INFO, "impossible case");
+ case QmlApplicationTypeCore:
+ Q_FALLTHROUGH();
+ default: // QmlApplicationTypeUnknown: not allowed, but we'll exit after checking apptypeOption below
+ app = qt_make_unique<QCoreApplication>(argc, argv);
break;
}
@@ -475,67 +434,143 @@ int main(int argc, char *argv[])
QString dummyDir;
// Handle main arguments
- const QStringList argList = app->arguments();
- for (int i = 1; i < argList.count(); i++) {
- const QString &arg = argList[i];
- if (arg == QLatin1String("-quiet"))
- quietMode = true;
- else if (arg == QLatin1String("-v") || arg == QLatin1String("-version"))
- printVersion();
- else if (arg == QLatin1String("-h") || arg == QLatin1String("-help"))
- printUsage();
- else if (arg == QLatin1String("--"))
- break;
- else if (arg == QLatin1String("-verbose"))
- verboseMode = true;
+ QCommandLineParser parser;
+ parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
+ parser.setOptionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsPositionalArguments);
+ const QCommandLineOption helpOption = parser.addHelpOption();
+ const QCommandLineOption versionOption = parser.addVersionOption();
+#ifdef QT_GUI_LIB
+ QCommandLineOption apptypeOption(QStringList() << QStringLiteral("a") << QStringLiteral("apptype"),
+ QCoreApplication::translate("main", "Select which application class to use. Default is gui."),
+#ifdef QT_WIDGETS_LIB
+ QStringLiteral("core|gui|widget"));
+#else
+ QStringLiteral("core|gui"));
+#endif // QT_WIDGETS_LIB
+ parser.addOption(apptypeOption); // Just for the help text... we've already handled this argument above
+#endif // QT_GUI_LIB
+ QCommandLineOption importOption(QStringLiteral("I"),
+ QCoreApplication::translate("main", "Prepend the given path to the import paths."), QStringLiteral("path"));
+ parser.addOption(importOption);
+ QCommandLineOption qmlFileOption(QStringLiteral("f"),
+ QCoreApplication::translate("main", "Load the given file as a QML file."), QStringLiteral("file"));
+ parser.addOption(qmlFileOption);
+ QCommandLineOption configOption(QStringList() << QStringLiteral("c") << QStringLiteral("config"),
+ QCoreApplication::translate("main", "Load the given built-in configuration or configuration file."), QStringLiteral("file"));
+ parser.addOption(configOption);
+ QCommandLineOption listConfOption(QStringList() << QStringLiteral("list-conf"),
+ QCoreApplication::translate("main", "List the built-in configurations."));
+ parser.addOption(listConfOption);
+ QCommandLineOption translationOption(QStringLiteral("translation"),
+ QCoreApplication::translate("main", "Load the given file as the translations file."), QStringLiteral("file"));
+ parser.addOption(translationOption);
+ QCommandLineOption dummyDataOption(QStringLiteral("dummy-data"),
+ QCoreApplication::translate("main", "Load QML files from the given directory as context properties."), QStringLiteral("file"));
+ parser.addOption(dummyDataOption);
+ // OpenGL options
+ QCommandLineOption glDesktopOption(QStringLiteral("desktop"),
+ QCoreApplication::translate("main", "Force use of desktop OpenGL (AA_UseDesktopOpenGL)."));
+ parser.addOption(glDesktopOption);
+ QCommandLineOption glEsOption(QStringLiteral("gles"),
+ QCoreApplication::translate("main", "Force use of GLES (AA_UseOpenGLES)."));
+ parser.addOption(glEsOption);
+ QCommandLineOption glSoftwareOption(QStringLiteral("software"),
+ QCoreApplication::translate("main", "Force use of software rendering (AA_UseSoftwareOpenGL)."));
+ parser.addOption(glSoftwareOption);
+ QCommandLineOption scalingOption(QStringLiteral("scaling"),
+ QCoreApplication::translate("main", "Enable High DPI scaling (AA_EnableHighDpiScaling)."));
+ parser.addOption(scalingOption);
+ QCommandLineOption noScalingOption(QStringLiteral("no-scaling"),
+ QCoreApplication::translate("main", "Disable High DPI scaling (AA_DisableHighDpiScaling)."));
+ parser.addOption(noScalingOption);
+ // Debugging and verbosity options
+ QCommandLineOption quietOption(QStringLiteral("quiet"),
+ QCoreApplication::translate("main", "Suppress all output."));
+ parser.addOption(quietOption);
+ QCommandLineOption verboseOption(QStringLiteral("verbose"),
+ QCoreApplication::translate("main", "Print information about what qml is doing, like specific file URLs being loaded."));
+ parser.addOption(verboseOption);
+ QCommandLineOption slowAnimationsOption(QStringLiteral("slow-animations"),
+ QCoreApplication::translate("main", "Run all animations in slow motion."));
+ parser.addOption(slowAnimationsOption);
+ QCommandLineOption fixedAnimationsOption(QStringLiteral("fixed-animations"),
+ QCoreApplication::translate("main", "Run animations off animation tick rather than wall time."));
+ parser.addOption(fixedAnimationsOption);
+ QCommandLineOption rhiOption(QStringList() << QStringLiteral("r") << QStringLiteral("rhi"),
+ QCoreApplication::translate("main", "Use the Qt graphics abstraction (RHI) instead of OpenGL directly. "
+ "Backend is one of: default, vulkan, metal, d3d11, gl"),
+ QStringLiteral("backend"));
+ parser.addOption(rhiOption);
+
+ // Positional arguments
+ parser.addPositionalArgument("files",
+ QCoreApplication::translate("main", "Any number of QML files can be loaded. They will share the same engine."), "[files...]");
+ parser.addPositionalArgument("args",
+ QCoreApplication::translate("main", "Arguments after '--' are ignored, but passed through to the application.arguments variable in QML."), "[-- args...]");
+
+ if (!parser.parse(QCoreApplication::arguments())) {
+ qWarning() << parser.errorText();
+ exit(1);
+ }
+ if (parser.isSet(versionOption))
+ parser.showVersion();
+ if (parser.isSet(helpOption))
+ parser.showHelp();
+ if (parser.isSet(listConfOption))
+ listConfFiles();
+ if (applicationType == QmlApplicationTypeUnknown) {
+#ifdef QT_WIDGETS_LIB
+ qWarning() << QCoreApplication::translate("main", "--apptype must be followed by one of the following: core gui widget\n");
+#else
+ qWarning() << QCoreApplication::translate("main", "--apptype must be followed by one of the following: core gui\n");
+#endif // QT_WIDGETS_LIB
+ parser.showHelp();
+ }
+ if (parser.isSet(verboseOption))
+ verboseMode = true;
+ if (parser.isSet(quietOption)) {
+ quietMode = true;
+ verboseMode = false;
+ }
#if QT_CONFIG(qml_animation)
- else if (arg == QLatin1String("-slow-animations"))
- QUnifiedTimer::instance()->setSlowModeEnabled(true);
- else if (arg == QLatin1String("-fixed-animations"))
- QUnifiedTimer::instance()->setConsistentTiming(true);
+ if (parser.isSet(slowAnimationsOption))
+ QUnifiedTimer::instance()->setSlowModeEnabled(true);
+ if (parser.isSet(fixedAnimationsOption))
+ QUnifiedTimer::instance()->setConsistentTiming(true);
#endif
- else if (arg == QLatin1String("-I")) {
- if (i+1 == argList.count())
- continue; // Invalid usage, but just ignore it
- e.addImportPath(argList[i+1]);
- i++;
- } else if (arg == QLatin1String("-f")) {
- if (i+1 == argList.count())
- continue; // Invalid usage, but just ignore it
- files << argList[i+1];
- i++;
- } else if (arg == QLatin1String("-config")){
- if (i+1 == argList.count())
- continue; // Invalid usage, but just ignore it
- confFile = argList[i+1];
- i++;
- } else if (arg == QLatin1String("-translation")){
- if (i+1 == argList.count())
- continue; // Invalid usage, but just ignore it
- translationFile = argList[i+1];
- i++;
- } else if (arg == QLatin1String("-dummy-data")){
- if (i+1 == argList.count())
- continue; // Invalid usage, but just ignore it
- dummyDir = argList[i+1];
- i++;
- } else if (arg == QLatin1String("-gles")) {
- QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
- } else if (arg == QLatin1String("-software")) {
- QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
- } else if (arg == QLatin1String("-desktop")) {
- QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
- } else if (arg == QLatin1String("-scaling")) {
- QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
- } else if (arg == QLatin1String("-no-scaling")) {
- QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
- } else {
- files << arg;
- }
+ if (parser.isSet(glEsOption))
+ QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
+ if (parser.isSet(glSoftwareOption))
+ QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
+ if (parser.isSet(glDesktopOption))
+ QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
+ if (parser.isSet(scalingOption))
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ if (parser.isSet(noScalingOption))
+ QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
+ for (const QString &importPath : parser.values(importOption))
+ e.addImportPath(importPath);
+ files << parser.values(qmlFileOption);
+ if (parser.isSet(configOption))
+ confFile = parser.value(configOption);
+ if (parser.isSet(translationOption))
+ translationFile = parser.value(translationOption);
+ if (parser.isSet(dummyDataOption))
+ dummyDir = parser.value(dummyDataOption);
+ if (parser.isSet(rhiOption)) {
+ qputenv("QSG_RHI", "1");
+ const QString rhiBackend = parser.value(rhiOption);
+ if (rhiBackend == QLatin1String("default"))
+ qunsetenv("QSG_RHI_BACKEND");
+ else
+ qputenv("QSG_RHI_BACKEND", rhiBackend.toLatin1());
+ }
+ for (QString posArg : parser.positionalArguments()) {
+ if (posArg == QLatin1String("--"))
+ break;
+ else
+ files << posArg;
}
-
- if (quietMode && verboseMode)
- verboseMode = false;
#if QT_CONFIG(translation)
// Need to be installed before QQmlApplicationEngine's automatic translation loading
@@ -557,13 +592,15 @@ int main(int argc, char *argv[])
printf("qml: Translation file specified, but Qt built without translation support.\n");
#endif
- if (quietMode)
+ if (quietMode) {
qInstallMessageHandler(quietMessageHandler);
+ QLoggingCategory::setFilterRules(QStringLiteral("*=false"));
+ }
if (files.count() <= 0) {
#if defined(Q_OS_DARWIN)
if (applicationType == QmlApplicationTypeGui)
- exitTimerId = static_cast<LoaderApplication *>(app)->startTimer(FILE_OPEN_EVENT_WAIT_TIME);
+ exitTimerId = static_cast<LoaderApplication *>(app.get())->startTimer(FILE_OPEN_EVENT_WAIT_TIME);
else
#endif
noFilesGiven();
diff --git a/tools/qml/qml.qrc b/tools/qml/qml.qrc
index e4be1793d4..69aa4a5756 100644
--- a/tools/qml/qml.qrc
+++ b/tools/qml/qml.qrc
@@ -1,7 +1,9 @@
<RCC>
<qresource prefix="qt-project.org/QmlRuntime">
- <file>conf/configuration.qml</file>
- <file>conf/qtquick.qml</file>
+ <file>conf/default.qml</file>
+ <file>conf/resizeToItem.qml</file>
+ <file>conf/content/resizeItemToWindow.qml</file>
+ <file>conf/content/resizeWindowToItem.qml</file>
<file>resources/qml-64.png</file>
</qresource>
</RCC>
diff --git a/tools/qmlcachegen/generateloader.cpp b/tools/qmlcachegen/generateloader.cpp
index 4fbbb27afb..1c8a5a016a 100644
--- a/tools/qmlcachegen/generateloader.cpp
+++ b/tools/qmlcachegen/generateloader.cpp
@@ -75,7 +75,7 @@ QString mangledIdentifier(const QString &str)
|| (c >= QLatin1Char('a') && c <= QLatin1Char('z'))
|| (c >= QLatin1Char('A') && c <= QLatin1Char('Z'))
|| c == QLatin1Char('_')) {
- mangled += c;
+ mangled += QChar(c);
} else {
mangled += QLatin1String("_0x") + QString::number(c, 16) + QLatin1Char('_');
}
@@ -434,7 +434,11 @@ bool generateLoader(const QStringList &compiledFiles, const QStringList &sortedR
}
}
+#if QT_CONFIG(temporaryfile)
QSaveFile f(outputFileName);
+#else
+ QFile f(outputFileName);
+#endif
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
*errorString = f.errorString();
return false;
@@ -445,10 +449,12 @@ bool generateLoader(const QStringList &compiledFiles, const QStringList &sortedR
return false;
}
+#if QT_CONFIG(temporaryfile)
if (!f.commit()) {
*errorString = f.errorString();
return false;
}
+#endif
return true;
}
diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp
index df7468eaef..ac9cf039d3 100644
--- a/tools/qmlcachegen/qmlcachegen.cpp
+++ b/tools/qmlcachegen/qmlcachegen.cpp
@@ -45,6 +45,8 @@
#include <algorithm>
+using namespace QQmlJS;
+
int filterResourceFile(const QString &input, const QString &output);
bool generateLoader(const QStringList &compiledFiles, const QStringList &retainedFiles,
const QString &output, const QStringList &resourceFileMappings,
@@ -65,6 +67,7 @@ struct Error
void print();
Error augment(const QString &contextErrorMessage) const;
void appendDiagnostics(const QString &inputFileName, const QList<QQmlJS::DiagnosticMessage> &diagnostics);
+ void appendDiagnostic(const QString &inputFileName, const DiagnosticMessage &diagnostic);
};
void Error::print()
@@ -82,9 +85,9 @@ Error Error::augment(const QString &contextErrorMessage) const
QString diagnosticErrorMessage(const QString &fileName, const QQmlJS::DiagnosticMessage &m)
{
QString message;
- message = fileName + QLatin1Char(':') + QString::number(m.loc.startLine) + QLatin1Char(':');
- if (m.loc.startColumn > 0)
- message += QString::number(m.loc.startColumn) + QLatin1Char(':');
+ message = fileName + QLatin1Char(':') + QString::number(m.line) + QLatin1Char(':');
+ if (m.column > 0)
+ message += QString::number(m.column) + QLatin1Char(':');
if (m.isError())
message += QLatin1String(" error: ");
@@ -94,13 +97,17 @@ QString diagnosticErrorMessage(const QString &fileName, const QQmlJS::Diagnostic
return message;
}
+void Error::appendDiagnostic(const QString &inputFileName, const DiagnosticMessage &diagnostic)
+{
+ if (!message.isEmpty())
+ message += QLatin1Char('\n');
+ message += diagnosticErrorMessage(inputFileName, diagnostic);
+}
+
void Error::appendDiagnostics(const QString &inputFileName, const QList<DiagnosticMessage> &diagnostics)
{
- for (const QQmlJS::DiagnosticMessage &parseError: diagnostics) {
- if (!message.isEmpty())
- message += QLatin1Char('\n');
- message += diagnosticErrorMessage(inputFileName, parseError);
- }
+ for (const QQmlJS::DiagnosticMessage &diagnostic: diagnostics)
+ appendDiagnostic(inputFileName, diagnostic);
}
// Ensure that ListElement objects keep all property assignments in their string form
@@ -169,7 +176,7 @@ static bool checkArgumentsObjectUseInSignalHandlers(const QmlIR::Document &doc,
return true;
}
-using SaveFunction = std::function<bool (const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, QString *)>;
+using SaveFunction = std::function<bool(const QV4::CompiledData::SaveableUnitPointer &, QString *)>;
static bool compileQmlFile(const QString &inputFileName, SaveFunction saveFunction, Error *error)
{
@@ -200,10 +207,7 @@ static bool compileQmlFile(const QString &inputFileName, SaveFunction saveFuncti
annotateListElements(&irDocument);
{
- QmlIR::JSCodeGen v4CodeGen(irDocument.code,
- &irDocument.jsGenerator, &irDocument.jsModule,
- &irDocument.jsParserEngine, irDocument.program,
- &irDocument.jsGenerator.stringTable, illegalNames);
+ QmlIR::JSCodeGen v4CodeGen(&irDocument, illegalNames);
for (QmlIR::Object *object: qAsConst(irDocument.objects)) {
if (object->functionsAndExpressions->count == 0)
continue;
@@ -211,9 +215,8 @@ static bool compileQmlFile(const QString &inputFileName, SaveFunction saveFuncti
for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next)
functionsToCompile << *foe;
const QVector<int> runtimeFunctionIndices = v4CodeGen.generateJSCodeForFunctionsAndBindings(functionsToCompile);
- QList<QQmlJS::DiagnosticMessage> jsErrors = v4CodeGen.errors();
- if (!jsErrors.isEmpty()) {
- error->appendDiagnostics(inputFileName, jsErrors);
+ if (v4CodeGen.hasError()) {
+ error->appendDiagnostic(inputFileName, v4CodeGen.error());
return false;
}
@@ -229,11 +232,13 @@ static bool compileQmlFile(const QString &inputFileName, SaveFunction saveFuncti
QmlIR::QmlUnitGenerator generator;
irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(/*generate unit*/false);
generator.generate(irDocument);
- QV4::CompiledData::Unit *unit = const_cast<QV4::CompiledData::Unit*>(irDocument.javaScriptCompilationUnit->data);
- unit->flags |= QV4::CompiledData::Unit::StaticData;
- unit->flags |= QV4::CompiledData::Unit::PendingTypeCompilation;
- if (!saveFunction(irDocument.javaScriptCompilationUnit, &error->message))
+ const quint32 saveFlags
+ = QV4::CompiledData::Unit::StaticData
+ | QV4::CompiledData::Unit::PendingTypeCompilation;
+ QV4::CompiledData::SaveableUnitPointer saveable(irDocument.javaScriptCompilationUnit.data,
+ saveFlags);
+ if (!saveFunction(saveable, &error->message))
return false;
}
return true;
@@ -241,7 +246,7 @@ static bool compileQmlFile(const QString &inputFileName, SaveFunction saveFuncti
static bool compileJSFile(const QString &inputFileName, const QString &inputFileUrl, SaveFunction saveFunction, Error *error)
{
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit;
+ QV4::CompiledData::CompilationUnit unit;
QString sourceCode;
{
@@ -262,9 +267,10 @@ static bool compileJSFile(const QString &inputFileName, const QString &inputFile
QList<QQmlJS::DiagnosticMessage> diagnostics;
// Precompiled files are relocatable and the final location will be set when loading.
QString url;
- unit = QV4::ExecutionEngine::compileModule(/*debugMode*/false, url, sourceCode, QDateTime(), &diagnostics);
+ unit = QV4::Compiler::Codegen::compileModule(/*debugMode*/false, url, sourceCode,
+ QDateTime(), &diagnostics);
error->appendDiagnostics(inputFileName, diagnostics);
- if (!unit)
+ if (!unit.unitData())
return false;
} else {
QmlIR::Document irDocument(/*debugMode*/false);
@@ -302,14 +308,11 @@ static bool compileJSFile(const QString &inputFileName, const QString &inputFile
}
{
- QmlIR::JSCodeGen v4CodeGen(irDocument.code, &irDocument.jsGenerator,
- &irDocument.jsModule, &irDocument.jsParserEngine,
- irDocument.program, &irDocument.jsGenerator.stringTable, illegalNames);
+ QmlIR::JSCodeGen v4CodeGen(&irDocument, illegalNames);
v4CodeGen.generateFromProgram(inputFileName, inputFileUrl, sourceCode, program,
&irDocument.jsModule, QV4::Compiler::ContextType::ScriptImportedByQML);
- QList<QQmlJS::DiagnosticMessage> jsErrors = v4CodeGen.errors();
- if (!jsErrors.isEmpty()) {
- error->appendDiagnostics(inputFileName, jsErrors);
+ if (v4CodeGen.hasError()) {
+ error->appendDiagnostic(inputFileName, v4CodeGen.error());
return false;
}
@@ -320,19 +323,22 @@ static bool compileJSFile(const QString &inputFileName, const QString &inputFile
irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(/*generate unit*/false);
QmlIR::QmlUnitGenerator generator;
generator.generate(irDocument);
- QV4::CompiledData::Unit *unitData = const_cast<QV4::CompiledData::Unit*>(irDocument.javaScriptCompilationUnit->data);
- unitData->flags |= QV4::CompiledData::Unit::StaticData;
- unit = irDocument.javaScriptCompilationUnit;
+ unit = std::move(irDocument.javaScriptCompilationUnit);
}
}
- return saveFunction(unit, &error->message);
+ return saveFunction(QV4::CompiledData::SaveableUnitPointer(unit.data), &error->message);
}
static bool saveUnitAsCpp(const QString &inputFileName, const QString &outputFileName,
- const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, QString *errorString)
+ const QV4::CompiledData::SaveableUnitPointer &unit,
+ QString *errorString)
{
+#if QT_CONFIG(temporaryfile)
QSaveFile f(outputFileName);
+#else
+ QFile f(outputFileName);
+#endif
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
*errorString = f.errorString();
return false;
@@ -364,43 +370,38 @@ static bool saveUnitAsCpp(const QString &inputFileName, const QString &outputFil
if (!writeStr(QByteArrayLiteral(" {\nextern const unsigned char qmlData alignas(16) [] = {\n")))
return false;
- QByteArray hexifiedData;
- {
- QByteArray modifiedUnit;
- modifiedUnit.resize(unit->data->unitSize);
- memcpy(modifiedUnit.data(), unit->data, unit->data->unitSize);
- const char *dataPtr = modifiedUnit.data();
- QV4::CompiledData::Unit *unitPtr;
- memcpy(&unitPtr, &dataPtr, sizeof(unitPtr));
- unitPtr->flags |= QV4::CompiledData::Unit::StaticData;
-
- QTextStream stream(&hexifiedData);
- const uchar *begin = reinterpret_cast<const uchar *>(modifiedUnit.constData());
- const uchar *end = begin + unit->data->unitSize;
- stream << hex;
- int col = 0;
- for (const uchar *data = begin; data < end; ++data, ++col) {
- if (data > begin)
- stream << ',';
- if (col % 8 == 0) {
- stream << '\n';
- col = 0;
+ unit.saveToDisk<uchar>([&writeStr](const uchar *begin, quint32 size) {
+ QByteArray hexifiedData;
+ {
+ QTextStream stream(&hexifiedData);
+ const uchar *end = begin + size;
+ stream << hex;
+ int col = 0;
+ for (const uchar *data = begin; data < end; ++data, ++col) {
+ if (data > begin)
+ stream << ',';
+ if (col % 8 == 0) {
+ stream << '\n';
+ col = 0;
+ }
+ stream << "0x" << *data;
}
- stream << "0x" << *data;
+ stream << '\n';
}
- stream << '\n';
- };
+ return writeStr(hexifiedData);
+ });
+
- if (!writeStr(hexifiedData))
- return false;
if (!writeStr("};\n}\n}\n"))
return false;
+#if QT_CONFIG(temporaryfile)
if (!f.commit()) {
*errorString = f.errorString();
return false;
}
+#endif
return true;
}
@@ -524,13 +525,20 @@ int main(int argc, char **argv)
inputFileUrl = QStringLiteral("qrc://") + inputResourcePath;
- saveFunction = [inputResourcePath, outputFileName](const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, QString *errorString) {
+ saveFunction = [inputResourcePath, outputFileName](
+ const QV4::CompiledData::SaveableUnitPointer &unit,
+ QString *errorString) {
return saveUnitAsCpp(inputResourcePath, outputFileName, unit, errorString);
};
} else {
- saveFunction = [outputFileName](const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, QString *errorString) {
- return unit->saveToDisk(outputFileName, errorString);
+ saveFunction = [outputFileName](const QV4::CompiledData::SaveableUnitPointer &unit,
+ QString *errorString) {
+ return unit.saveToDisk<char>(
+ [&outputFileName, errorString](const char *data, quint32 size) {
+ return QV4::CompiledData::SaveableUnitPointer::writeDataToFile(
+ outputFileName, data, size, errorString);
+ });
};
}
diff --git a/tools/qmlcachegen/qmlcachegen.pro b/tools/qmlcachegen/qmlcachegen.pro
index bee0b9a37e..ec65cdb5e6 100644
--- a/tools/qmlcachegen/qmlcachegen.pro
+++ b/tools/qmlcachegen/qmlcachegen.pro
@@ -5,8 +5,10 @@ DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII
SOURCES = qmlcachegen.cpp \
resourcefilter.cpp \
- generateloader.cpp \
- resourcefilemapper.cpp
+ generateloader.cpp
+
+include(../shared/shared.pri)
+
TARGET = qmlcachegen
build_integration.files = qmlcache.prf qtquickcompiler.prf
@@ -38,5 +40,3 @@ QMAKE_TARGET_DESCRIPTION = QML Cache Generator
load(qt_tool)
-HEADERS += \
- resourcefilemapper.h
diff --git a/tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in b/tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in
new file mode 100644
index 0000000000..6cdfaf8f6f
--- /dev/null
+++ b/tools/qmlimportscanner/Qt5QmlImportScannerConfig.cmake.in
@@ -0,0 +1,184 @@
+include(CMakeParseArguments)
+
+function(QT5_IMPORT_QML_PLUGINS target)
+!!IF !isEmpty(CMAKE_STATIC_TYPE)
+ set(options)
+ set(oneValueArgs \"PATH_TO_SCAN\")
+ set(multiValueArgs)
+
+ cmake_parse_arguments(arg \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\" ${ARGN})
+ if(NOT arg_PATH_TO_SCAN)
+ set(arg_PATH_TO_SCAN \"${CMAKE_CURRENT_SOURCE_DIR}\")
+ endif()
+
+ # Find location of qmlimportscanner.
+ find_package(Qt5 COMPONENTS Core)
+!!IF isEmpty(CMAKE_BIN_DIR_IS_ABSOLUTE)
+ set(tool_path
+ \"${_qt5Core_install_prefix}/$${CMAKE_BIN_DIR}qmlimportscanner$$CMAKE_BIN_SUFFIX\")
+!!ELSE
+ set(tool_path \"$${CMAKE_BIN_DIR}qmlimportscanner$$CMAKE_BIN_SUFFIX\")
+!!ENDIF
+ if(NOT EXISTS \"${tool_path}\" )
+ message(FATAL_ERROR \"The package \\\"Qt5QmlImportScannerConfig\\\" references the file
+ \\\"${tool_path}\\\"
+but this file does not exist. Possible reasons include:
+* The file was deleted, renamed, or moved to another location.
+* An install or uninstall procedure did not complete successfully.
+* The installation package was faulty.
+\")
+ endif()
+
+ # Find location of qml dir.
+!!IF isEmpty(CMAKE_QML_DIR_IS_ABSOLUTE)
+ set(qml_path \"${_qt5Core_install_prefix}/$${CMAKE_QML_DIR}\")
+!!ELSE
+ set(qml_path \"$${CMAKE_QML_DIR}\")
+!!ENDIF
+
+ # Small macro to avoid duplicating code in two different loops.
+ macro(_qt5_QmlImportScanner_parse_entry)
+ set(entry_name \"qml_import_scanner_import_${idx}\")
+ cmake_parse_arguments(\"entry\"
+ \"\"
+ \"CLASSNAME;NAME;PATH;PLUGIN;RELATIVEPATH;TYPE;VERSION;\" \"\"
+ ${${entry_name}})
+ endmacro()
+
+ # Macro used to populate the dependency link flags for a certain configuriation (debug vs
+ # release) of a plugin.
+ macro(_qt5_link_to_QmlImportScanner_library_dependencies Plugin Configuration PluginLocation
+ IsDebugAndRelease)
+
+ set_property(TARGET \"${Plugin}\" APPEND PROPERTY IMPORTED_CONFIGURATIONS ${Configuration})
+ set(_imported_location \"${PluginLocation}\")
+ _qt5_Core_check_file_exists(\"${_imported_location}\")
+ set_target_properties(\"${Plugin}\" PROPERTIES
+ \"IMPORTED_LOCATION_${Configuration}\" \"${_imported_location}\"
+ )
+
+ set(_static_deps
+ ${_Qt5${entry_PLUGIN}_STATIC_${Configuration}_LIB_DEPENDENCIES}
+ )
+
+ if(NOT "${IsDebugAndRelease}")
+ set(_genex_condition \"1\")
+ else()
+ if("${Configuration}" STREQUAL "DEBUG")
+ set(_genex_condition \"$<CONFIG:Debug>\")
+ else()
+ set(_genex_condition \"$<NOT:$<CONFIG:Debug>>\")
+ endif()
+ endif()
+ if(_static_deps)
+ set(_static_deps_genex \"$<${_genex_condition}:${_static_deps}>\")
+ target_link_libraries(${imported_target} INTERFACE \"${_static_deps_genex}\")
+ endif()
+
+ set(_static_link_flags \"${_Qt5${entry_PLUGIN}_STATIC_${Configuration}_LINK_FLAGS}\")
+ if(NOT CMAKE_VERSION VERSION_LESS \"3.13\" AND _static_link_flags)
+ set(_static_link_flags_genex \"$<${_genex_condition}:${_static_link_flags}>\")
+ target_link_options(${imported_target} INTERFACE \"${_static_link_flags_genex}\")
+ endif()
+ endmacro()
+
+ # Run qmlimportscanner and include the generated cmake file.
+ set(qml_imports_file_path
+ \"${CMAKE_CURRENT_BINARY_DIR}/Qt5_QmlPlugins_Imports_${target}.cmake\")
+
+ message(STATUS \"Running qmlimportscanner to find used QML plugins. \")
+ execute_process(COMMAND
+ \"${tool_path}\" \"${arg_PATH_TO_SCAN}\" -importPath \"${qml_path}\"
+ -cmake-output
+ OUTPUT_FILE \"${qml_imports_file_path}\")
+
+ include(\"${qml_imports_file_path}\" OPTIONAL RESULT_VARIABLE qml_imports_file_path_found)
+ if(NOT qml_imports_file_path_found)
+ message(FATAL_ERROR \"Could not find ${qml_imports_file_path} which was supposed to be generated by qmlimportscanner.\")
+ endif()
+
+ # Parse the generate cmake file.
+ # It is possible for the scanner to find no usage of QML, in which case the import count is 0.
+ if(qml_import_scanner_imports_count)
+ set(added_plugins \"\")
+ foreach(idx RANGE \"${qml_import_scanner_imports_count}\")
+ _qt5_QmlImportScanner_parse_entry()
+ if(entry_PATH AND entry_PLUGIN)
+ # Sometimes a plugin appears multiple times with different versions.
+ # Make sure to process it only once.
+ list(FIND added_plugins \"${entry_PLUGIN}\" _index)
+ if(NOT _index EQUAL -1)
+ continue()
+ endif()
+ list(APPEND added_plugins \"${entry_PLUGIN}\")
+
+ # Add an imported target that will contain the link libraries and link options read
+ # from one plugin prl file. This target will point to the actual plugin and contain
+ # static dependency libraries and link flags.
+ # By creating a target for each qml plugin, CMake will take care of link flag
+ # deduplication.
+ set(imported_target \"${target}_QmlImport_${entry_PLUGIN}\")
+ add_library(\"${imported_target}\" MODULE IMPORTED)
+ target_link_libraries(\"${target}\" PRIVATE \"${imported_target}\")
+
+ # Read static library dependencies from the plugin .prl file.
+ # And then set the link flags to the library dependencies extracted from the .prl
+ # file.
+!!IF !isEmpty(CMAKE_RELEASE_TYPE)
+ _qt5_Core_process_prl_file(
+ \"${entry_PATH}/$$QMAKE_PREFIX_STATICLIB${entry_PLUGIN}$${CMAKE_QML_PLUGIN_SUFFIX_RELEASE}.prl\" RELEASE
+ _Qt5${entry_PLUGIN}_STATIC_RELEASE_LIB_DEPENDENCIES
+ _Qt5${entry_PLUGIN}_STATIC_RELEASE_LINK_FLAGS
+ )
+ _qt5_link_to_QmlImportScanner_library_dependencies(
+ \"${imported_target}\"
+ RELEASE
+ \"${entry_PATH}/$$QMAKE_PREFIX_STATICLIB${entry_PLUGIN}$${CMAKE_QML_PLUGIN_SUFFIX_RELEASE}.$$QMAKE_EXTENSION_STATICLIB\"
+ $${CMAKE_DEBUG_AND_RELEASE})
+!!ENDIF
+
+!!IF !isEmpty(CMAKE_DEBUG_TYPE)
+ _qt5_Core_process_prl_file(
+ \"${entry_PATH}/$$QMAKE_PREFIX_STATICLIB${entry_PLUGIN}$${CMAKE_QML_PLUGIN_SUFFIX_DEBUG}.prl\" DEBUG
+ _Qt5${entry_PLUGIN}_STATIC_DEBUG_LIB_DEPENDENCIES
+ _Qt5${entry_PLUGIN}_STATIC_DEBUG_LINK_FLAGS
+ )
+ _qt5_link_to_QmlImportScanner_library_dependencies(
+ \"${imported_target}\"
+ DEBUG
+ \"${entry_PATH}/$$QMAKE_PREFIX_STATICLIB${entry_PLUGIN}$${CMAKE_QML_PLUGIN_SUFFIX_DEBUG}.$$QMAKE_EXTENSION_STATICLIB\"
+ $${CMAKE_DEBUG_AND_RELEASE})
+!!ENDIF
+ endif()
+ endforeach()
+
+ # Generate content for plugin initialization cpp file.
+ set(added_imports \"\")
+ set(qt5_qml_import_cpp_file_content \"\")
+ foreach(idx RANGE \"${qml_import_scanner_imports_count}\")
+ _qt5_QmlImportScanner_parse_entry()
+ if(entry_PLUGIN)
+ if(entry_CLASSNAME)
+ list(FIND added_imports \"${entry_PLUGIN}\" _index)
+ if(_index EQUAL -1)
+ string(APPEND qt5_qml_import_cpp_file_content
+ \"Q_IMPORT_PLUGIN(${entry_CLASSNAME})\n\")
+ list(APPEND added_imports \"${entry_PLUGIN}\")
+ endif()
+ else()
+ message(FATAL_ERROR
+ \"Plugin ${entry_PLUGIN} is missing a classname entry, please add one to the qmldir file.\")
+ endif()
+ endif()
+ endforeach()
+
+ # Write to the generated file, and include it as a source for the given target.
+ set(generated_import_cpp_path
+ \"${CMAKE_CURRENT_BINARY_DIR}/Qt5_QmlPlugins_Imports_${target}.cpp\")
+ configure_file(\"${Qt5QmlImportScanner_DIR}/Qt5QmlImportScannerTemplate.cpp.in\"
+ \"${generated_import_cpp_path}\"
+ @ONLY)
+ target_sources(${target} PRIVATE \"${generated_import_cpp_path}\")
+ endif()
+!!ENDIF // !isEmpty(CMAKE_STATIC_TYPE)
+endfunction()
diff --git a/tools/qmlimportscanner/Qt5QmlImportScannerTemplate.cpp.in b/tools/qmlimportscanner/Qt5QmlImportScannerTemplate.cpp.in
new file mode 100644
index 0000000000..4ed747a555
--- /dev/null
+++ b/tools/qmlimportscanner/Qt5QmlImportScannerTemplate.cpp.in
@@ -0,0 +1,5 @@
+// This file is autogenerated by CMake. It imports static plugin classes for
+// static plugins used by QML imports.
+#include <QtPlugin>
+
+@qt5_qml_import_cpp_file_content@
diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp
index 616de9e80d..c37910bdaf 100644
--- a/tools/qmlimportscanner/main.cpp
+++ b/tools/qmlimportscanner/main.cpp
@@ -30,8 +30,9 @@
#include <private/qqmljsparser_p.h>
#include <private/qqmljsast_p.h>
#include <private/qv4codegen_p.h>
-#include <private/qv4value_p.h>
+#include <private/qv4staticvalue_p.h>
#include <private/qqmlirbuilder_p.h>
+#include <private/qqmljsdiagnosticmessage_p.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
@@ -48,6 +49,8 @@
#include <QtCore/QJsonDocument>
#include <QtCore/QLibraryInfo>
+#include <resourcefilemapper.h>
+
#include <iostream>
#include <algorithm>
@@ -79,13 +82,14 @@ void printUsage(const QString &appNameIn)
#endif
std::wcerr
<< "Usage: " << appName << " -rootPath path/to/app/qml/directory -importPath path/to/qt/qml/directory\n"
- " " << appName << " -qmlFiles file1 file2 -importPath path/to/qt/qml/directory\n\n"
+ " " << appName << " -qmlFiles file1 file2 -importPath path/to/qt/qml/directory\n"
+ " " << appName << " -qrcFiles file1.qrc file2.qrc -importPath path/to/qt/qml/directory\n\n"
"Example: " << appName << " -rootPath . -importPath "
<< QDir::toNativeSeparators(qmlPath).toStdWString()
<< '\n';
}
-QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, const QString &code, const QString &path)
+QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, const QString &path)
{
QVariantList imports;
@@ -119,7 +123,8 @@ QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, con
if (!name.isEmpty())
import[nameLiteral()] = name;
import[typeLiteral()] = moduleLiteral();
- import[versionLiteral()] = code.mid(importNode->versionToken.offset, importNode->versionToken.length);
+ auto versionString = importNode->version ? QString::number(importNode->version->majorVersion) + QLatin1Char('.') + QString::number(importNode->version->minorVersion) : QString();
+ import[versionLiteral()] = versionString;
}
imports.append(import);
@@ -272,11 +277,11 @@ QVariantList findQmlImportsInQmlCode(const QString &filePath, const QString &cod
const auto diagnosticMessages = parser.diagnosticMessages();
for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
std::cerr << QDir::toNativeSeparators(filePath).toStdString() << ':'
- << m.loc.startLine << ':' << m.message.toStdString() << std::endl;
+ << m.line << ':' << m.message.toStdString() << std::endl;
}
return QVariantList();
}
- return findImportsInAst(parser.ast()->headers, code, filePath);
+ return findImportsInAst(parser.ast()->headers, filePath);
}
// Scan a single qml file for import statements
@@ -494,6 +499,36 @@ QVariantList findQmlImportsRecursively(const QStringList &qmlDirs, const QString
return ret;
}
+
+QString generateCmakeIncludeFileContent(const QVariantList &importList) {
+ // The function assumes that "list" is a QVariantList with 0 or more QVariantMaps, where
+ // each map contains QString -> QVariant<QString> mappings. This matches with the structure
+ // that qmake parses for static qml plugin auto imporitng.
+ // So: [ {"a": "a","b": "b"}, {"c": "c"} ]
+ QString content;
+ QTextStream s(&content);
+ int importsCount = 0;
+ for (const QVariant &importVariant: importList) {
+ if (static_cast<QMetaType::Type>(importVariant.type()) == QMetaType::QVariantMap) {
+ s << QStringLiteral("set(qml_import_scanner_import_") << importsCount
+ << QStringLiteral(" \"");
+
+ const QMap<QString, QVariant> &importDict = importVariant.toMap();
+ for (auto it = importDict.cbegin(); it != importDict.cend(); ++it) {
+ s << it.key().toUpper() << QLatin1Char(';')
+ << it.value().toString() << QLatin1Char(';');
+ }
+ s << QStringLiteral("\")\n");
+ ++importsCount;
+ }
+ }
+ if (importsCount >= 0) {
+ content.prepend(QString(QStringLiteral("set(qml_import_scanner_imports_count %1)\n"))
+ .arg(importsCount));
+ }
+ return content;
+}
+
} // namespace
int main(int argc, char *argv[])
@@ -510,6 +545,8 @@ int main(int argc, char *argv[])
QStringList qmlRootPaths;
QStringList scanFiles;
QStringList qmlImportPaths;
+ QStringList qrcFiles;
+ bool generateCmakeContent = false;
int i = 1;
while (i < args.count()) {
@@ -534,6 +571,10 @@ int main(int argc, char *argv[])
if (i >= args.count())
std::cerr << "-importPath requires an argument\n";
argReceiver = &qmlImportPaths;
+ } else if (arg == QLatin1String("-cmake-output")) {
+ generateCmakeContent = true;
+ } else if (arg == QLatin1String("-qrcFiles")) {
+ argReceiver = &qrcFiles;
} else {
std::cerr << qPrintable(appName) << ": Invalid argument: \""
<< qPrintable(arg) << "\"\n";
@@ -555,13 +596,23 @@ int main(int argc, char *argv[])
}
}
+ if (!qrcFiles.isEmpty())
+ scanFiles << ResourceFileMapper(qrcFiles).qmlCompilerFiles(ResourceFileMapper::FileOutput::AbsoluteFilePath);
+
g_qmlImportPaths = qmlImportPaths;
// Find the imports!
QVariantList imports = findQmlImportsRecursively(qmlRootPaths, scanFiles);
- // Convert to JSON
- QByteArray json = QJsonDocument(QJsonArray::fromVariantList(imports)).toJson();
- std::cout << json.constData() << std::endl;
+ QByteArray content;
+ if (generateCmakeContent) {
+ // Convert to CMake code
+ content = generateCmakeIncludeFileContent(imports).toUtf8();
+ } else {
+ // Convert to JSON
+ content = QJsonDocument(QJsonArray::fromVariantList(imports)).toJson();
+ }
+
+ std::cout << content.constData() << std::endl;
return 0;
}
diff --git a/tools/qmlimportscanner/qmlimportscanner.pro b/tools/qmlimportscanner/qmlimportscanner.pro
index 0b3a03abf3..d69f1a3b0b 100644
--- a/tools/qmlimportscanner/qmlimportscanner.pro
+++ b/tools/qmlimportscanner/qmlimportscanner.pro
@@ -4,6 +4,52 @@ QT = core qmldevtools-private
DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII
SOURCES += main.cpp
+include(../shared/shared.pri)
+
+load(cmake_functions)
+
+CMAKE_BIN_DIR = $$cmakeRelativePath($$[QT_HOST_BINS], $$[QT_INSTALL_PREFIX])
+contains(CMAKE_BIN_DIR, "^\\.\\./.*") {
+ CMAKE_BIN_DIR = $$[QT_HOST_BINS]/
+ CMAKE_BIN_DIR_IS_ABSOLUTE = True
+}
+
+CMAKE_QML_DIR = $$cmakeRelativePath($$[QT_INSTALL_QML/get], $$[QT_INSTALL_PREFIX])
+contains(CMAKE_QML_DIR, "^\\.\\./.*") {
+ CMAKE_QML_DIR = $$[QT_INSTALL_QML/get]/
+ CMAKE_QML_DIR_IS_ABSOLUTE = True
+}
+load(qt_build_paths)
+
+static|staticlib:CMAKE_STATIC_TYPE = true
+
+# Compute the platform target suffix.
+CMAKE_QML_PLUGIN_SUFFIX_RELEASE =
+win32: CMAKE_QML_PLUGIN_SUFFIX_DEBUG = d
+else:darwin: CMAKE_QML_PLUGIN_SUFFIX_DEBUG = _debug
+else: CMAKE_QML_PLUGIN_SUFFIX_DEBUG =
+
+# Find out which configurations should be handled in the generated Config.cmake file.
+CMAKE_DEBUG_TYPE =
+CMAKE_RELEASE_TYPE =
+if(qtConfig(debug_and_release)|contains(QT_CONFIG, debug, debug|release)): CMAKE_DEBUG_TYPE = debug
+if(qtConfig(debug_and_release)|contains(QT_CONFIG, release, debug|release)): CMAKE_RELEASE_TYPE = release
+
+qtConfig(debug_and_release) {
+ CMAKE_DEBUG_AND_RELEASE = TRUE
+} else {
+ CMAKE_DEBUG_AND_RELEASE = FALSE
+}
+
+equals(QMAKE_HOST.os, Windows): CMAKE_BIN_SUFFIX = ".exe"
+cmake_config_file.input = $$PWD/Qt5QmlImportScannerConfig.cmake.in
+cmake_config_file.output = $$MODULE_BASE_OUTDIR/lib/cmake/Qt5QmlImportScanner/Qt5QmlImportScannerConfig.cmake
+QMAKE_SUBSTITUTES += cmake_config_file
+
+cmake_build_integration.files = $$cmake_config_file.output $$PWD/Qt5QmlImportScannerTemplate.cpp.in
+cmake_build_integration.path = $$[QT_INSTALL_LIBS]/cmake/Qt5QmlImportScanner
+prefix_build: INSTALLS += cmake_build_integration
+else: COPIES += cmake_build_integration
QMAKE_TARGET_DESCRIPTION = QML Import Scanner
diff --git a/tools/qmljs/qmljs.cpp b/tools/qmljs/qmljs.cpp
index 4b581fff05..ba5e5f553c 100644
--- a/tools/qmljs/qmljs.cpp
+++ b/tools/qmljs/qmljs.cpp
@@ -137,7 +137,8 @@ int main(int argc, char *argv[])
}
QScopedPointer<QV4::Script> script;
if (cache && QFile::exists(fn + QLatin1Char('c'))) {
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading();
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> unit
+ = QV4::ExecutableCompilationUnit::create();
QString error;
if (unit->loadFromDisk(QUrl::fromLocalFile(fn), QFileInfo(fn).lastModified(), &error)) {
script.reset(new QV4::Script(&vm, nullptr, unit));
diff --git a/tools/qmllint/componentversion.cpp b/tools/qmllint/componentversion.cpp
new file mode 100644
index 0000000000..3dc4ac37d0
--- /dev/null
+++ b/tools/qmllint/componentversion.cpp
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE: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 "componentversion.h"
+
+#include <QString>
+#include <QCryptographicHash>
+
+#include <limits>
+
+using namespace LanguageUtils;
+
+const int ComponentVersion::NoVersion = -1;
+const int ComponentVersion::MaxVersion = std::numeric_limits<int>::max();
+
+ComponentVersion::ComponentVersion()
+ : _major(NoVersion), _minor(NoVersion)
+{
+}
+
+ComponentVersion::ComponentVersion(int major, int minor)
+ : _major(major), _minor(minor)
+{
+}
+
+ComponentVersion::ComponentVersion(const QString &versionString)
+ : _major(NoVersion), _minor(NoVersion)
+{
+ int dotIdx = versionString.indexOf(QLatin1Char('.'));
+ if (dotIdx == -1)
+ return;
+ bool ok = false;
+ int maybeMajor = versionString.leftRef(dotIdx).toInt(&ok);
+ if (!ok)
+ return;
+ int maybeMinor = versionString.midRef(dotIdx + 1).toInt(&ok);
+ if (!ok)
+ return;
+ _major = maybeMajor;
+ _minor = maybeMinor;
+}
+
+ComponentVersion::~ComponentVersion()
+{
+}
+
+bool ComponentVersion::isValid() const
+{
+ return _major >= 0 && _minor >= 0;
+}
+
+QString ComponentVersion::toString() const
+{
+ return QString::fromLatin1("%1.%2").arg(QString::number(_major),
+ QString::number(_minor));
+}
+
+void ComponentVersion::addToHash(QCryptographicHash &hash) const
+{
+ hash.addData(reinterpret_cast<const char *>(&_major), sizeof(_major));
+ hash.addData(reinterpret_cast<const char *>(&_minor), sizeof(_minor));
+}
+
+namespace LanguageUtils {
+
+bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs)
+{
+ return lhs.majorVersion() < rhs.majorVersion()
+ || (lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() < rhs.minorVersion());
+}
+
+bool operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs)
+{
+ return lhs.majorVersion() < rhs.majorVersion()
+ || (lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() <= rhs.minorVersion());
+}
+
+bool operator>(const ComponentVersion &lhs, const ComponentVersion &rhs)
+{
+ return rhs < lhs;
+}
+
+bool operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs)
+{
+ return rhs <= lhs;
+}
+
+bool operator==(const ComponentVersion &lhs, const ComponentVersion &rhs)
+{
+ return lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() == rhs.minorVersion();
+}
+
+bool operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs)
+{
+ return !(lhs == rhs);
+}
+
+}
diff --git a/tools/qmllint/componentversion.h b/tools/qmllint/componentversion.h
new file mode 100644
index 0000000000..9d079f1d30
--- /dev/null
+++ b/tools/qmllint/componentversion.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE: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 COMPONENTVERSION_H
+#define COMPONENTVERSION_H
+
+#include <qglobal.h>
+
+QT_BEGIN_NAMESPACE
+class QCryptographicHash;
+QT_END_NAMESPACE
+
+namespace LanguageUtils {
+
+class ComponentVersion
+{
+ int _major;
+ int _minor;
+
+public:
+ static const int NoVersion;
+ static const int MaxVersion;
+
+ ComponentVersion();
+ ComponentVersion(int major, int minor);
+ explicit ComponentVersion(const QString &versionString);
+ ~ComponentVersion();
+
+ int majorVersion() const
+ { return _major; }
+ int minorVersion() const
+ { return _minor; }
+
+ bool isValid() const;
+ QString toString() const;
+ void addToHash(QCryptographicHash &hash) const;
+};
+
+bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs);
+bool operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs);
+bool operator>(const ComponentVersion &lhs, const ComponentVersion &rhs);
+bool operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs);
+bool operator==(const ComponentVersion &lhs, const ComponentVersion &rhs);
+bool operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs);
+
+} // namespace LanguageUtils
+
+#endif // COMPONENTVERSION_H
diff --git a/tools/qmllint/fakemetaobject.cpp b/tools/qmllint/fakemetaobject.cpp
new file mode 100644
index 0000000000..514bb2fe42
--- /dev/null
+++ b/tools/qmllint/fakemetaobject.cpp
@@ -0,0 +1,600 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE: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 "fakemetaobject.h"
+#include <QCryptographicHash>
+
+using namespace LanguageUtils;
+
+FakeMetaEnum::FakeMetaEnum()
+{}
+
+FakeMetaEnum::FakeMetaEnum(const QString &name)
+ : m_name(name)
+{}
+
+bool FakeMetaEnum::isValid() const
+{ return !m_name.isEmpty(); }
+
+QString FakeMetaEnum::name() const
+{ return m_name; }
+
+void FakeMetaEnum::setName(const QString &name)
+{ m_name = name; }
+
+void FakeMetaEnum::addKey(const QString &key, int value)
+{ m_keys.append(key); m_values.append(value); }
+
+QString FakeMetaEnum::key(int index) const
+{ return m_keys.at(index); }
+
+int FakeMetaEnum::keyCount() const
+{ return m_keys.size(); }
+
+QStringList FakeMetaEnum::keys() const
+{ return m_keys; }
+
+bool FakeMetaEnum::hasKey(const QString &key) const
+{ return m_keys.contains(key); }
+
+void FakeMetaEnum::addToHash(QCryptographicHash &hash) const
+{
+ int len = m_name.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ hash.addData(reinterpret_cast<const char *>(m_name.constData()), len * sizeof(QChar));
+ len = m_keys.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ foreach (const QString &key, m_keys) {
+ len = key.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar));
+ }
+ len = m_values.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ foreach (int value, m_values)
+ hash.addData(reinterpret_cast<const char *>(&value), sizeof(value));
+}
+
+QString FakeMetaEnum::describe(int baseIndent) const
+{
+ QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
+ QString res = QLatin1String("Enum ");
+ res += name();
+ res += QLatin1String(":{");
+ for (int i = 0; i < keyCount(); ++i) {
+ res += newLine;
+ res += QLatin1String(" ");
+ res += key(i);
+ res += QLatin1String(": ");
+ res += QString::number(m_values.value(i, -1));
+ }
+ res += newLine;
+ res += QLatin1Char('}');
+ return res;
+}
+
+QString FakeMetaEnum::toString() const
+{
+ return describe();
+}
+
+FakeMetaMethod::FakeMetaMethod(const QString &name, const QString &returnType)
+ : m_name(name)
+ , m_returnType(returnType)
+ , m_methodTy(FakeMetaMethod::Method)
+ , m_methodAccess(FakeMetaMethod::Public)
+ , m_revision(0)
+{}
+
+FakeMetaMethod::FakeMetaMethod()
+ : m_methodTy(FakeMetaMethod::Method)
+ , m_methodAccess(FakeMetaMethod::Public)
+ , m_revision(0)
+{}
+
+QString FakeMetaMethod::methodName() const
+{ return m_name; }
+
+void FakeMetaMethod::setMethodName(const QString &name)
+{ m_name = name; }
+
+void FakeMetaMethod::setReturnType(const QString &type)
+{ m_returnType = type; }
+
+QStringList FakeMetaMethod::parameterNames() const
+{ return m_paramNames; }
+
+QStringList FakeMetaMethod::parameterTypes() const
+{ return m_paramTypes; }
+
+void FakeMetaMethod::addParameter(const QString &name, const QString &type)
+{ m_paramNames.append(name); m_paramTypes.append(type); }
+
+int FakeMetaMethod::methodType() const
+{ return m_methodTy; }
+
+void FakeMetaMethod::setMethodType(int methodType)
+{ m_methodTy = methodType; }
+
+int FakeMetaMethod::access() const
+{ return m_methodAccess; }
+
+int FakeMetaMethod::revision() const
+{ return m_revision; }
+
+void FakeMetaMethod::setRevision(int r)
+{ m_revision = r; }
+
+void FakeMetaMethod::addToHash(QCryptographicHash &hash) const
+{
+ int len = m_name.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ hash.addData(reinterpret_cast<const char *>(m_name.constData()), len * sizeof(QChar));
+ hash.addData(reinterpret_cast<const char *>(&m_methodAccess), sizeof(m_methodAccess));
+ hash.addData(reinterpret_cast<const char *>(&m_methodTy), sizeof(m_methodTy));
+ hash.addData(reinterpret_cast<const char *>(&m_revision), sizeof(m_revision));
+ len = m_paramNames.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ foreach (const QString &pName, m_paramNames) {
+ len = pName.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ hash.addData(reinterpret_cast<const char *>(pName.constData()), len * sizeof(QChar));
+ }
+ len = m_paramTypes.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ foreach (const QString &pType, m_paramTypes) {
+ len = pType.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ hash.addData(reinterpret_cast<const char *>(pType.constData()), len * sizeof(QChar));
+ }
+ len = m_returnType.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ hash.addData(reinterpret_cast<const char *>(m_returnType.constData()), len * sizeof(QChar));
+}
+
+QString FakeMetaMethod::describe(int baseIndent) const
+{
+ QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
+ QString res = QLatin1String("Method {");
+ res += newLine;
+ res += QLatin1String(" methodName:");
+ res += methodName();
+ res += newLine;
+ res += QLatin1String(" methodType:");
+ res += methodType();
+ res += newLine;
+ res += QLatin1String(" parameterNames:[");
+ foreach (const QString &pName, parameterNames()) {
+ res += newLine;
+ res += QLatin1String(" ");
+ res += pName;
+ }
+ res += QLatin1Char(']');
+ res += newLine;
+ res += QLatin1String(" parameterTypes:[");
+ foreach (const QString &pType, parameterTypes()) {
+ res += newLine;
+ res += QLatin1String(" ");
+ res += pType;
+ }
+ res += QLatin1Char(']');
+ res += newLine;
+ res += QLatin1Char('}');
+ return res;
+}
+
+QString FakeMetaMethod::toString() const
+{
+ return describe();
+}
+
+
+FakeMetaProperty::FakeMetaProperty(const QString &name, const QString &type, bool isList,
+ bool isWritable, bool isPointer, int revision)
+ : m_propertyName(name)
+ , m_type(type)
+ , m_isList(isList)
+ , m_isWritable(isWritable)
+ , m_isPointer(isPointer)
+ , m_revision(revision)
+{}
+
+QString FakeMetaProperty::name() const
+{ return m_propertyName; }
+
+QString FakeMetaProperty::typeName() const
+{ return m_type; }
+
+bool FakeMetaProperty::isList() const
+{ return m_isList; }
+
+bool FakeMetaProperty::isWritable() const
+{ return m_isWritable; }
+
+bool FakeMetaProperty::isPointer() const
+{ return m_isPointer; }
+
+int FakeMetaProperty::revision() const
+{ return m_revision; }
+
+void FakeMetaProperty::addToHash(QCryptographicHash &hash) const
+{
+ int len = m_propertyName.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ hash.addData(reinterpret_cast<const char *>(m_propertyName.constData()), len * sizeof(QChar));
+ hash.addData(reinterpret_cast<const char *>(&m_revision), sizeof(m_revision));
+ int flags = (m_isList ? (1 << 0) : 0)
+ + (m_isPointer ? (1 << 1) : 0)
+ + (m_isWritable ? (1 << 2) : 0);
+ hash.addData(reinterpret_cast<const char *>(&flags), sizeof(flags));
+ len = m_type.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ hash.addData(reinterpret_cast<const char *>(m_type.constData()), len * sizeof(QChar));
+}
+
+QString FakeMetaProperty::describe(int baseIndent) const
+{
+ auto boolStr = [] (bool v) { return v ? QLatin1String("true") : QLatin1String("false"); };
+ QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
+ QString res = QLatin1String("Property {");
+ res += newLine;
+ res += QLatin1String(" name:");
+ res += name();
+ res += newLine;
+ res += QLatin1String(" typeName:");
+ res += typeName();
+ res += newLine;
+ res += QLatin1String(" typeName:");
+ res += QString::number(revision());
+ res += newLine;
+ res += QLatin1String(" isList:");
+ res += boolStr(isList());
+ res += newLine;
+ res += QLatin1String(" isPointer:");
+ res += boolStr(isPointer());
+ res += newLine;
+ res += QLatin1String(" isWritable:");
+ res += boolStr(isWritable());
+ res += newLine;
+ res += QLatin1Char('}');
+ return res;
+}
+
+QString FakeMetaProperty::toString() const
+{
+ return describe();
+}
+
+
+FakeMetaObject::FakeMetaObject() : m_isSingleton(false), m_isCreatable(true), m_isComposite(false)
+{
+}
+
+QString FakeMetaObject::className() const
+{ return m_className; }
+void FakeMetaObject::setClassName(const QString &name)
+{ m_className = name; }
+
+void FakeMetaObject::addExport(const QString &name, const QString &package, ComponentVersion version)
+{
+ Export exp;
+ exp.type = name;
+ exp.package = package;
+ exp.version = version;
+ m_exports.append(exp);
+}
+
+void FakeMetaObject::setExportMetaObjectRevision(int exportIndex, int metaObjectRevision)
+{
+ m_exports[exportIndex].metaObjectRevision = metaObjectRevision;
+}
+
+QList<FakeMetaObject::Export> FakeMetaObject::exports() const
+{ return m_exports; }
+FakeMetaObject::Export FakeMetaObject::exportInPackage(const QString &package) const
+{
+ foreach (const Export &exp, m_exports) {
+ if (exp.package == package)
+ return exp;
+ }
+ return Export();
+}
+
+void FakeMetaObject::setSuperclassName(const QString &superclass)
+{ m_superName = superclass; }
+QString FakeMetaObject::superclassName() const
+{ return m_superName; }
+
+void FakeMetaObject::addEnum(const FakeMetaEnum &fakeEnum)
+{ m_enumNameToIndex.insert(fakeEnum.name(), m_enums.size()); m_enums.append(fakeEnum); }
+int FakeMetaObject::enumeratorCount() const
+{ return m_enums.size(); }
+int FakeMetaObject::enumeratorOffset() const
+{ return 0; }
+FakeMetaEnum FakeMetaObject::enumerator(int index) const
+{ return m_enums.at(index); }
+int FakeMetaObject::enumeratorIndex(const QString &name) const
+{ return m_enumNameToIndex.value(name, -1); }
+
+void FakeMetaObject::addProperty(const FakeMetaProperty &property)
+{ m_propNameToIdx.insert(property.name(), m_props.size()); m_props.append(property); }
+int FakeMetaObject::propertyCount() const
+{ return m_props.size(); }
+int FakeMetaObject::propertyOffset() const
+{ return 0; }
+FakeMetaProperty FakeMetaObject::property(int index) const
+{ return m_props.at(index); }
+int FakeMetaObject::propertyIndex(const QString &name) const
+{ return m_propNameToIdx.value(name, -1); }
+
+void FakeMetaObject::addMethod(const FakeMetaMethod &method)
+{ m_methods.append(method); }
+int FakeMetaObject::methodCount() const
+{ return m_methods.size(); }
+int FakeMetaObject::methodOffset() const
+{ return 0; }
+FakeMetaMethod FakeMetaObject::method(int index) const
+{ return m_methods.at(index); }
+int FakeMetaObject::methodIndex(const QString &name) const //If performances becomes an issue, just use a nameToIdx hash
+{
+ for (int i=0; i<m_methods.count(); i++)
+ if (m_methods[i].methodName() == name)
+ return i;
+ return -1;
+}
+
+QString FakeMetaObject::defaultPropertyName() const
+{ return m_defaultPropertyName; }
+void FakeMetaObject::setDefaultPropertyName(const QString &defaultPropertyName)
+{ m_defaultPropertyName = defaultPropertyName; }
+
+QString FakeMetaObject::attachedTypeName() const
+{ return m_attachedTypeName; }
+void FakeMetaObject::setAttachedTypeName(const QString &name)
+{ m_attachedTypeName = name; }
+
+QByteArray FakeMetaObject::calculateFingerprint() const
+{
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ int len = m_className.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ hash.addData(reinterpret_cast<const char *>(m_className.constData()), len * sizeof(QChar));
+ len = m_attachedTypeName.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ hash.addData(reinterpret_cast<const char *>(m_attachedTypeName.constData()), len * sizeof(QChar));
+ len = m_defaultPropertyName.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ hash.addData(reinterpret_cast<const char *>(m_defaultPropertyName.constData()), len * sizeof(QChar));
+ len = m_enumNameToIndex.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ {
+ QStringList keys(m_enumNameToIndex.keys());
+ keys.sort();
+ foreach (const QString &key, keys) {
+ len = key.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar));
+ int value = m_enumNameToIndex.value(key);
+ hash.addData(reinterpret_cast<const char *>(&value), sizeof(value)); // avoid? this adds order dependency to fingerprint...
+ m_enums.at(value).addToHash(hash);
+ }
+ }
+ len = m_exports.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ foreach (const Export &e, m_exports)
+ e.addToHash(hash); // normalize order?
+ len = m_exports.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ foreach (const FakeMetaMethod &m, m_methods)
+ m.addToHash(hash); // normalize order?
+ {
+ QStringList keys(m_propNameToIdx.keys());
+ keys.sort();
+ foreach (const QString &key, keys) {
+ len = key.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar));
+ int value = m_propNameToIdx.value(key);
+ hash.addData(reinterpret_cast<const char *>(&value), sizeof(value)); // avoid? this adds order dependency to fingerprint...
+ m_props.at(value).addToHash(hash);
+ }
+ }
+ len = m_superName.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ hash.addData(reinterpret_cast<const char *>(m_superName.constData()), len * sizeof(QChar));
+
+ QByteArray res = hash.result();
+ res.append('F');
+ return res;
+}
+
+void FakeMetaObject::updateFingerprint()
+{
+ m_fingerprint = calculateFingerprint();
+}
+
+QByteArray FakeMetaObject::fingerprint() const
+{
+ return m_fingerprint;
+}
+
+bool FakeMetaObject::isSingleton() const
+{
+ return m_isSingleton;
+}
+
+bool FakeMetaObject::isCreatable() const
+{
+ return m_isCreatable;
+}
+
+bool FakeMetaObject::isComposite() const
+{
+ return m_isComposite;
+}
+
+void FakeMetaObject::setIsSingleton(bool value)
+{
+ m_isSingleton = value;
+}
+
+void FakeMetaObject::setIsCreatable(bool value)
+{
+ m_isCreatable = value;
+}
+
+void FakeMetaObject::setIsComposite(bool value)
+{
+ m_isSingleton = value;
+}
+
+QString FakeMetaObject::toString() const
+{
+ return describe();
+}
+
+QString FakeMetaObject::describe(bool printDetails, int baseIndent) const
+{
+ QString res = QString::fromLatin1("FakeMetaObject@%1")
+ .arg((quintptr)(void *)this, 0, 16);
+ if (!printDetails)
+ return res;
+ auto boolStr = [] (bool v) { return v ? QLatin1String("true") : QLatin1String("false"); };
+ QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
+ res += QLatin1Char('{');
+ res += newLine;
+ res += QLatin1String("className:");
+ res += className();
+ res += newLine;
+ res += QLatin1String("superClassName:");
+ res += superclassName();
+ res += newLine;
+ res += QLatin1String("isSingleton:");
+ res += boolStr(isSingleton());
+ res += newLine;
+ res += QLatin1String("isCreatable:");
+ res += boolStr(isCreatable());
+ res += newLine;
+ res += QLatin1String("isComposite:");
+ res += boolStr(isComposite());
+ res += newLine;
+ res += QLatin1String("defaultPropertyName:");
+ res += defaultPropertyName();
+ res += newLine;
+ res += QLatin1String("attachedTypeName:");
+ res += attachedTypeName();
+ res += newLine;
+ res += QLatin1String("fingerprint:");
+ res += QString::fromUtf8(fingerprint());
+
+ res += newLine;
+ res += QLatin1String("exports:[");
+ foreach (const Export &e, exports()) {
+ res += newLine;
+ res += QLatin1String(" ");
+ res += e.describe(baseIndent + 2);
+ }
+ res += QLatin1Char(']');
+
+ res += newLine;
+ res += QLatin1String("enums:[");
+ for (int iEnum = 0; iEnum < enumeratorCount() ; ++ iEnum) {
+ FakeMetaEnum e = enumerator(enumeratorOffset() + iEnum);
+ res += newLine;
+ res += QLatin1String(" ");
+ res += e.describe(baseIndent + 2);
+ }
+ res += QLatin1Char(']');
+
+ res += newLine;
+ res += QLatin1String("properties:[");
+ for (int iProp = 0; iProp < propertyCount() ; ++ iProp) {
+ FakeMetaProperty prop = property(propertyOffset() + iProp);
+ res += newLine;
+ res += QLatin1String(" ");
+ res += prop.describe(baseIndent + 2);
+ }
+ res += QLatin1Char(']');
+ res += QLatin1String("methods:[");
+ for (int iMethod = 0; iMethod < methodOffset() ; ++ iMethod) {
+ FakeMetaMethod m = method(methodOffset() + iMethod);
+ res += newLine;
+ res += QLatin1String(" ");
+ m.describe(baseIndent + 2);
+ }
+ res += QLatin1Char(']');
+ res += newLine;
+ res += QLatin1Char('}');
+ return res;
+}
+
+FakeMetaObject::Export::Export()
+ : metaObjectRevision(0)
+{}
+bool FakeMetaObject::Export::isValid() const
+{ return version.isValid() || !package.isEmpty() || !type.isEmpty(); }
+
+void FakeMetaObject::Export::addToHash(QCryptographicHash &hash) const
+{
+ int len = package.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ hash.addData(reinterpret_cast<const char *>(package.constData()), len * sizeof(QChar));
+ len = type.size();
+ hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ hash.addData(reinterpret_cast<const char *>(type.constData()), len * sizeof(QChar));
+ version.addToHash(hash);
+ hash.addData(reinterpret_cast<const char *>(&metaObjectRevision), sizeof(metaObjectRevision));
+}
+
+QString FakeMetaObject::Export::describe(int baseIndent) const
+{
+ QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
+ QString res = QLatin1String("Export {");
+ res += newLine;
+ res += QLatin1String(" package:");
+ res += package;
+ res += newLine;
+ res += QLatin1String(" type:");
+ res += type;
+ res += newLine;
+ res += QLatin1String(" version:");
+ res += version.toString();
+ res += newLine;
+ res += QLatin1String(" metaObjectRevision:");
+ res += QString::number(metaObjectRevision);
+ res += newLine;
+ res += QLatin1String(" isValid:");
+ res += QString::number(isValid());
+ res += newLine;
+ res += QLatin1Char('}');
+ return res;
+}
+
+QString FakeMetaObject::Export::toString() const
+{
+ return describe();
+}
diff --git a/tools/qmllint/fakemetaobject.h b/tools/qmllint/fakemetaobject.h
new file mode 100644
index 0000000000..4e0ea1f8b3
--- /dev/null
+++ b/tools/qmllint/fakemetaobject.h
@@ -0,0 +1,236 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE: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 FAKEMETAOBJECT_H
+#define FAKEMETAOBJECT_H
+
+#include <QString>
+#include <QStringList>
+#include <QList>
+#include <QHash>
+#include <QSharedPointer>
+
+#include "componentversion.h"
+
+QT_BEGIN_NAMESPACE
+class QCryptographicHash;
+QT_END_NAMESPACE
+
+namespace LanguageUtils {
+
+class FakeMetaEnum {
+ QString m_name;
+ QStringList m_keys;
+ QList<int> m_values;
+
+public:
+ FakeMetaEnum();
+ explicit FakeMetaEnum(const QString &name);
+
+ bool isValid() const;
+
+ QString name() const;
+ void setName(const QString &name);
+
+ void addKey(const QString &key, int value);
+ QString key(int index) const;
+ int keyCount() const;
+ QStringList keys() const;
+ bool hasKey(const QString &key) const;
+ void addToHash(QCryptographicHash &hash) const;
+
+ QString describe(int baseIndent = 0) const;
+ QString toString() const;
+};
+
+class FakeMetaMethod {
+public:
+ enum {
+ Signal,
+ Slot,
+ Method
+ };
+
+ enum {
+ Private,
+ Protected,
+ Public
+ };
+
+public:
+ FakeMetaMethod();
+ explicit FakeMetaMethod(const QString &name, const QString &returnType = QString());
+
+ QString methodName() const;
+ void setMethodName(const QString &name);
+
+ void setReturnType(const QString &type);
+
+ QStringList parameterNames() const;
+ QStringList parameterTypes() const;
+ void addParameter(const QString &name, const QString &type);
+
+ int methodType() const;
+ void setMethodType(int methodType);
+
+ int access() const;
+
+ int revision() const;
+ void setRevision(int r);
+ void addToHash(QCryptographicHash &hash) const;
+
+ QString describe(int baseIndent = 0) const;
+ QString toString() const;
+private:
+ QString m_name;
+ QString m_returnType;
+ QStringList m_paramNames;
+ QStringList m_paramTypes;
+ int m_methodTy;
+ int m_methodAccess;
+ int m_revision;
+};
+
+class FakeMetaProperty {
+ QString m_propertyName;
+ QString m_type;
+ bool m_isList;
+ bool m_isWritable;
+ bool m_isPointer;
+ int m_revision;
+
+public:
+ FakeMetaProperty(const QString &name, const QString &type, bool isList, bool isWritable, bool isPointer, int revision);
+
+ QString name() const;
+ QString typeName() const;
+
+ bool isList() const;
+ bool isWritable() const;
+ bool isPointer() const;
+ int revision() const;
+ void addToHash(QCryptographicHash &hash) const;
+
+ QString describe(int baseIndent = 0) const;
+ QString toString() const;
+};
+
+class FakeMetaObject {
+ Q_DISABLE_COPY(FakeMetaObject);
+
+public:
+ typedef QSharedPointer<FakeMetaObject> Ptr;
+ typedef QSharedPointer<const FakeMetaObject> ConstPtr;
+
+ class Export {
+ public:
+ Export();
+
+ QString package;
+ QString type;
+ ComponentVersion version;
+ int metaObjectRevision;
+
+ bool isValid() const;
+ void addToHash(QCryptographicHash &hash) const;
+
+ QString describe(int baseIndent = 0) const;
+ QString toString() const;
+ };
+
+private:
+ QString m_className;
+ QList<Export> m_exports;
+ QString m_superName;
+ QList<FakeMetaEnum> m_enums;
+ QHash<QString, int> m_enumNameToIndex;
+ QList<FakeMetaProperty> m_props;
+ QHash<QString, int> m_propNameToIdx;
+ QList<FakeMetaMethod> m_methods;
+ QString m_defaultPropertyName;
+ QString m_attachedTypeName;
+ QByteArray m_fingerprint;
+ bool m_isSingleton;
+ bool m_isCreatable;
+ bool m_isComposite;
+
+public:
+ FakeMetaObject();
+
+ QString className() const;
+ void setClassName(const QString &name);
+
+ void addExport(const QString &name, const QString &package, ComponentVersion version);
+ void setExportMetaObjectRevision(int exportIndex, int metaObjectRevision);
+ QList<Export> exports() const;
+ Export exportInPackage(const QString &package) const;
+
+ void setSuperclassName(const QString &superclass);
+ QString superclassName() const;
+
+ void addEnum(const FakeMetaEnum &fakeEnum);
+ int enumeratorCount() const;
+ int enumeratorOffset() const;
+ FakeMetaEnum enumerator(int index) const;
+ int enumeratorIndex(const QString &name) const;
+
+ void addProperty(const FakeMetaProperty &property);
+ int propertyCount() const;
+ int propertyOffset() const;
+ FakeMetaProperty property(int index) const;
+ int propertyIndex(const QString &name) const;
+
+ void addMethod(const FakeMetaMethod &method);
+ int methodCount() const;
+ int methodOffset() const;
+ FakeMetaMethod method(int index) const;
+ int methodIndex(const QString &name) const; // Note: Returns any method with that name in case of overloads
+
+ QString defaultPropertyName() const;
+ void setDefaultPropertyName(const QString &defaultPropertyName);
+
+ QString attachedTypeName() const;
+ void setAttachedTypeName(const QString &name);
+ QByteArray calculateFingerprint() const;
+ void updateFingerprint();
+ QByteArray fingerprint() const;
+
+ bool isSingleton() const;
+ bool isCreatable() const;
+ bool isComposite() const;
+ void setIsSingleton(bool value);
+ void setIsCreatable(bool value);
+ void setIsComposite(bool value);
+
+ QString describe(bool printDetails = true, int baseIndent = 0) const;
+ QString toString() const;
+};
+
+} // namespace LanguageUtils
+
+#endif // FAKEMETAOBJECT_H
diff --git a/tools/qmllint/findunqualified.cpp b/tools/qmllint/findunqualified.cpp
new file mode 100644
index 0000000000..49d64adb6e
--- /dev/null
+++ b/tools/qmllint/findunqualified.cpp
@@ -0,0 +1,783 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE: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 "findunqualified.h"
+#include "scopetree.h"
+
+#include "qmljstypedescriptionreader.h"
+
+#include <QFile>
+#include <QDirIterator>
+#include <QScopedValueRollback>
+
+#include <private/qqmljsast_p.h>
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsparser_p.h>
+#include <private/qv4codegen_p.h>
+
+QDebug operator<<(QDebug dbg, const QQmlJS::AST::SourceLocation &loc);
+
+static QQmlJS::TypeDescriptionReader createReaderForFile(QString const &filename)
+{
+ QFile f(filename);
+ f.open(QFile::ReadOnly);
+ QQmlJS::TypeDescriptionReader reader { filename, f.readAll() };
+ return reader;
+}
+
+void FindUnqualifiedIDVisitor::enterEnvironment(ScopeType type, QString name)
+{
+ m_currentScope = m_currentScope->createNewChildScope(type, name);
+}
+
+void FindUnqualifiedIDVisitor::leaveEnvironment()
+{
+ m_currentScope = m_currentScope->parentScope();
+}
+
+enum ImportVersion { FullyVersioned, PartiallyVersioned, Unversioned };
+
+QStringList completeQmltypesPaths(const QString &uri, const QStringList &basePaths, int vmaj, int vmin)
+{
+ static const QLatin1Char Slash('/');
+ static const QLatin1Char Backslash('\\');
+ static const QLatin1String SlashPluginsDotQmltypes("/plugins.qmltypes");
+
+ const QVector<QStringRef> parts = uri.splitRef(QLatin1Char('.'), QString::SkipEmptyParts);
+
+ QStringList qmlDirPathsPaths;
+ // fully & partially versioned parts + 1 unversioned for each base path
+ qmlDirPathsPaths.reserve(basePaths.count() * (2 * parts.count() + 1));
+
+ auto versionString = [](int vmaj, int vmin, ImportVersion version)
+ {
+ if (version == FullyVersioned) {
+ // extension with fully encoded version number (eg. MyModule.3.2)
+ return QString::asprintf(".%d.%d", vmaj, vmin);
+ } else if (version == PartiallyVersioned) {
+ // extension with encoded version major (eg. MyModule.3)
+ return QString::asprintf(".%d", vmaj);
+ } // else extension without version number (eg. MyModule)
+ return QString();
+ };
+ auto joinStringRefs = [](const QVector<QStringRef> &refs, const QChar &sep)
+ {
+ QString str;
+ for (auto it = refs.cbegin(); it != refs.cend(); ++it) {
+ if (it != refs.cbegin())
+ str += sep;
+ str += *it;
+ }
+ return str;
+ };
+
+ for (int version = FullyVersioned; version <= Unversioned; ++version) {
+ const QString ver = versionString(vmaj, vmin, static_cast<ImportVersion>(version));
+
+ for (const QString &path : basePaths) {
+ QString dir = path;
+ if (!dir.endsWith(Slash) && !dir.endsWith(Backslash))
+ dir += Slash;
+
+ // append to the end
+ qmlDirPathsPaths += dir + joinStringRefs(parts, Slash) + ver + SlashPluginsDotQmltypes;
+
+ if (version != Unversioned) {
+ // insert in the middle
+ for (int index = parts.count() - 2; index >= 0; --index) {
+ qmlDirPathsPaths += dir + joinStringRefs(parts.mid(0, index + 1), Slash)
+ + ver + Slash
+ + joinStringRefs(parts.mid(index + 1), Slash) + SlashPluginsDotQmltypes;
+ }
+ }
+ }
+ }
+
+ return qmlDirPathsPaths;
+}
+
+void FindUnqualifiedIDVisitor::importHelper(QString id, QString prefix, int major, int minor)
+{
+ QPair<QString, QString> importId { id, prefix };
+ if (m_alreadySeenImports.contains(importId)) {
+ return;
+ } else {
+ m_alreadySeenImports.insert(importId);
+ }
+ id = id.replace(QLatin1String("/"), QLatin1String("."));
+ auto qmltypesPaths = completeQmltypesPaths(id, m_qmltypeDirs, major, minor);
+
+ QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> objects;
+ QList<QQmlJS::ModuleApiInfo> moduleApis;
+ QStringList dependencies;
+ for (auto const &qmltypesPath : qmltypesPaths) {
+ if (QFile::exists(qmltypesPath)) {
+ auto reader = createReaderForFile(qmltypesPath);
+ auto succ = reader(&objects, &moduleApis, &dependencies);
+ if (!succ) {
+ qDebug() << reader.errorMessage();
+ }
+ break;
+ }
+ }
+ for (auto const &dependency : qAsConst(dependencies)) {
+ auto const split = dependency.split(" ");
+ auto const id = split.at(0);
+ auto const major = split.at(1).split('.').at(0).toInt();
+ auto const minor = split.at(1).split('.').at(1).toInt();
+ importHelper(id, QString(), major, minor);
+ }
+ // add objects
+ for (auto ob_it = objects.begin(); ob_it != objects.end(); ++ob_it) {
+ auto val = ob_it.value();
+ m_exportedName2MetaObject[prefix + val->className()] = val;
+ for (auto export_ : val->exports()) {
+ m_exportedName2MetaObject[prefix + export_.type] = val;
+ }
+ for (auto enumCount = 0; enumCount < val->enumeratorCount(); ++enumCount) {
+ m_currentScope->insertQMLIdentifier(val->enumerator(enumCount).name());
+ }
+ }
+}
+
+LanguageUtils::FakeMetaObject *
+FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath)
+{
+ using namespace QQmlJS::AST;
+ auto fake = new LanguageUtils::FakeMetaObject;
+ fake->setClassName(QFileInfo { filePath }.baseName());
+ QFile file(filePath);
+ if (!file.open(QFile::ReadOnly)) {
+ return fake;
+ }
+ QString code = file.readAll();
+ file.close();
+
+ QQmlJS::Engine engine;
+ QQmlJS::Lexer lexer(&engine);
+
+ lexer.setCode(code, 1, true);
+ QQmlJS::Parser parser(&engine);
+ if (!parser.parse()) {
+ return fake;
+ }
+ QQmlJS::AST::UiProgram *program = parser.ast();
+ auto header = program->headers;
+ while (header) {
+ if (auto import = cast<UiImport *>(header->headerItem)) {
+ if (import->version) {
+ QString path;
+ auto uri = import->importUri;
+ while (uri) {
+ path.append(uri->name);
+ path.append("/");
+ uri = uri->next;
+ }
+ path.chop(1);
+ QString prefix = QLatin1String("");
+ if (import->asToken.isValid()) {
+ prefix += import->importId + QLatin1Char('.');
+ }
+ importHelper(path, prefix, import->version->majorVersion, import->version->minorVersion);
+ }
+ }
+ header = header->next;
+ }
+ auto member = program->members;
+ // member should be the sole element
+ Q_ASSERT(!member->next);
+ Q_ASSERT(member && member->member->kind == UiObjectMember::Kind_UiObjectDefinition);
+ auto definition = static_cast<UiObjectDefinition *>(member->member);
+ auto qualifiedId = definition->qualifiedTypeNameId;
+ while (qualifiedId && qualifiedId->next) {
+ qualifiedId = qualifiedId->next;
+ }
+ fake->setSuperclassName(qualifiedId->name.toString());
+ UiObjectMemberList *initMembers = definition->initializer->members;
+ while (initMembers) {
+ switch (initMembers->member->kind) {
+ case UiObjectMember::Kind_UiArrayBinding: {
+ // nothing to do
+ break;
+ }
+ case UiObjectMember::Kind_UiEnumDeclaration: {
+ // nothing to do
+ break;
+ }
+ case UiObjectMember::Kind_UiObjectBinding: {
+ // nothing to do
+ break;
+ }
+ case UiObjectMember::Kind_UiObjectDefinition: {
+ // creates nothing accessible
+ break;
+ }
+ case UiObjectMember::Kind_UiPublicMember: {
+ auto publicMember = static_cast<UiPublicMember *>(initMembers->member);
+ switch (publicMember->type) {
+ case UiPublicMember::Signal: {
+ UiParameterList *param = publicMember->parameters;
+ LanguageUtils::FakeMetaMethod method;
+ method.setMethodType(LanguageUtils::FakeMetaMethod::Signal);
+ method.setMethodName(publicMember->name.toString());
+ while (param) {
+ method.addParameter(param->name.toString(), param->type->name.toString());
+ param = param->next;
+ }
+ fake->addMethod(method);
+ break;
+ }
+ case UiPublicMember::Property: {
+ LanguageUtils::FakeMetaProperty fakeprop { publicMember->name.toString(),
+ publicMember->typeModifier.toString(),
+ false,
+ false,
+ false,
+ 0 };
+ fake->addProperty(fakeprop);
+ break;
+ }
+ }
+ break;
+ }
+ case UiObjectMember::Kind_UiScriptBinding: {
+ // does not create anything new, ignore
+ break;
+ }
+ case UiObjectMember::Kind_UiSourceElement: {
+ auto sourceElement = static_cast<UiSourceElement *>(initMembers->member);
+ if (FunctionExpression *fexpr = sourceElement->sourceElement->asFunctionDefinition()) {
+ LanguageUtils::FakeMetaMethod method;
+ method.setMethodType(LanguageUtils::FakeMetaMethod::Method);
+ FormalParameterList *parameters = fexpr->formals;
+ while (parameters) {
+ method.addParameter(parameters->element->bindingIdentifier.toString(),
+ "");
+ parameters = parameters->next;
+ }
+ fake->addMethod(method);
+ } else if (ClassExpression *clexpr =
+ sourceElement->sourceElement->asClassDefinition()) {
+ LanguageUtils::FakeMetaProperty prop {
+ clexpr->name.toString(), "", false, false, false, 1
+ };
+ fake->addProperty(prop);
+ } else if (cast<VariableStatement *>(sourceElement->sourceElement)) {
+ // nothing to do
+ } else {
+ qDebug() << "unsupportedd sourceElement at" << sourceElement->firstSourceLocation()
+ << sourceElement->sourceElement->kind;
+ }
+ break;
+ }
+ default: {
+ qDebug() << "unsupported element of kind" << initMembers->member->kind;
+ }
+ }
+ initMembers = initMembers->next;
+ }
+ return fake;
+}
+
+void FindUnqualifiedIDVisitor::importExportedNames(QStringRef prefix, QString name)
+{
+ for (;;) {
+ auto metaObject = m_exportedName2MetaObject[m_exportedName2MetaObject.contains(name)
+ ? name
+ : prefix + QLatin1Char('.') + name];
+ if (metaObject) {
+ auto propertyCount = metaObject->propertyCount();
+ for (auto i = 0; i < propertyCount; ++i) {
+ m_currentScope->insertPropertyIdentifier(metaObject->property(i).name());
+ }
+
+ m_currentScope->addMethodsFromMetaObject(metaObject);
+
+ name = metaObject->superclassName();
+ if (name.isEmpty() || name == QLatin1String("QObject")) {
+ break;
+ }
+ } else {
+ m_colorOut.write(QLatin1String("warning: "), Warning);
+ m_colorOut.write(name + QLatin1String(" was not found. Did you add all import paths?\n"));
+ m_unknownImports.insert(name);
+ break;
+ }
+ }
+}
+
+void FindUnqualifiedIDVisitor::throwRecursionDepthError()
+{
+ m_colorOut.write(QStringLiteral("Error"), Error);
+ m_colorOut.write(QStringLiteral("Maximum statement or expression depth exceeded"), Error);
+ m_visitFailed = true;
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiProgram *)
+{
+ enterEnvironment(ScopeType::QMLScope, "program");
+ QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> objects;
+ QList<QQmlJS::ModuleApiInfo> moduleApis;
+ QStringList dependencies;
+ for (auto const &dir : m_qmltypeDirs) {
+ QDirIterator it { dir, QStringList() << QLatin1String("builtins.qmltypes"), QDir::NoFilter,
+ QDirIterator::Subdirectories };
+ while (it.hasNext()) {
+ auto reader = createReaderForFile(it.next());
+ auto succ = reader(&objects, &moduleApis, &dependencies);
+ if (!succ) {
+ qDebug() << reader.errorMessage();
+ }
+ }
+ }
+ // add builtins
+ for (auto ob_it = objects.begin(); ob_it != objects.end(); ++ob_it) {
+ auto val = ob_it.value();
+ for (auto export_ : val->exports()) {
+ m_exportedName2MetaObject[export_.type] = val;
+ }
+ for (auto enumCount = 0; enumCount < val->enumeratorCount(); ++enumCount) {
+ m_currentScope->insertQMLIdentifier(val->enumerator(enumCount).name());
+ }
+ }
+ // add "self" (as we only ever check the first part of a qualified identifier, we get away with
+ // using an empty FakeMetaObject
+ m_exportedName2MetaObject[QFileInfo { m_filePath }.baseName()] = {};
+
+ // add QML builtins
+ m_exportedName2MetaObject["QtObject"] = {}; // QtObject contains nothing of interest
+
+ LanguageUtils::FakeMetaObject *meta = new LanguageUtils::FakeMetaObject{};
+ meta->addProperty(LanguageUtils::FakeMetaProperty {"enabled", "bool", false, false, false, 0});
+ meta->addProperty(LanguageUtils::FakeMetaProperty {"ignoreUnknownSignals", "bool", false, false, false, 0});
+ meta->addProperty(LanguageUtils::FakeMetaProperty {"target", "QObject", false, false, false, 0});
+ m_exportedName2MetaObject["Connections"] = LanguageUtils::FakeMetaObject::ConstPtr { meta };
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::UiProgram *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::ClassExpression *ast)
+{
+ enterEnvironment(ScopeType::JSFunctionScope, ast->name.toString());
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::ClassExpression *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::ClassDeclaration *ast)
+{
+ enterEnvironment(ScopeType::JSFunctionScope, ast->name.toString());
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::ClassDeclaration *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::ForStatement *)
+{
+ enterEnvironment(ScopeType::JSLexicalScope, "forloop");
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::ForStatement *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::ForEachStatement *)
+{
+ enterEnvironment(ScopeType::JSLexicalScope, "foreachloop");
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::ForEachStatement *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::Block *)
+{
+ enterEnvironment(ScopeType::JSLexicalScope, "block");
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::Block *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::CaseBlock *)
+{
+ enterEnvironment(ScopeType::JSLexicalScope, "case");
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::CaseBlock *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::Catch *catchStatement)
+{
+ enterEnvironment(ScopeType::JSLexicalScope, "catch");
+ m_currentScope->insertJSIdentifier(catchStatement->patternElement->bindingIdentifier.toString(), QQmlJS::AST::VariableScope::Let);
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::Catch *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::WithStatement *withStatement)
+{
+ m_colorOut.write(QString::asprintf("Warning: "), Warning);
+ m_colorOut.write(QString::asprintf("%d:%d: with statements are strongly discouraged in QML and might cause false positives when analysing unqalified identifiers\n", withStatement->firstSourceLocation().startLine, withStatement->firstSourceLocation().startColumn), Normal);
+ enterEnvironment(ScopeType::JSLexicalScope, "with");
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::WithStatement *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiScriptBinding *uisb)
+{
+ using namespace QQmlJS::AST;
+ auto name = uisb->qualifiedId->name;
+ if (name == QLatin1String("id")) {
+ // found id
+ auto expstat = static_cast<ExpressionStatement *>(uisb->statement);
+ auto identexp = static_cast<IdentifierExpression *>(expstat->expression);
+ QString elementName = m_currentScope->name();
+ m_qmlid2meta.insert(identexp->name.toString(), m_exportedName2MetaObject[elementName]);
+ if (m_currentScope->isVisualRootScope()) {
+ m_rootId = identexp->name.toString();
+ }
+ } else if (name.startsWith("on") && name.size() > 2 && name.at(2).isUpper()) {
+ auto statement = uisb->statement;
+ if (statement->kind == Node::Kind::Kind_ExpressionStatement) {
+ if (static_cast<ExpressionStatement *>(statement)->expression->asFunctionDefinition()) {
+ // functions are already handled
+ // they do not get names inserted according to the signal, but access their formal
+ // parameters
+ return true;
+ }
+ }
+ QString signal = name.mid(2).toString();
+ signal[0] = signal[0].toLower();
+ if (!m_currentScope->methods().contains(signal)) {
+ qDebug() << "Info file does not contain signal" << signal;
+ } else {
+ auto method = m_currentScope->methods()[signal];
+ for (auto const &param : method.parameterNames()) {
+ auto firstSourceLocation = uisb->statement->firstSourceLocation();
+ bool hasMultilineStatementBody = uisb->statement->lastSourceLocation().startLine > firstSourceLocation.startLine;
+ m_currentScope->insertSignalIdentifier(param, method, firstSourceLocation, hasMultilineStatementBody);
+ }
+ }
+ return true;
+ }
+ return true;
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiPublicMember *uipm)
+{
+ // property bool inactive: !active
+ // extract name inactive
+ m_currentScope->insertPropertyIdentifier(uipm->name.toString());
+ return true;
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp)
+{
+ auto name = idexp->name;
+ if (!m_exportedName2MetaObject.contains(name.toString())) {
+ m_currentScope->addIdToAccssedIfNotInParentScopes(
+ { name.toString(), idexp->firstSourceLocation() }, m_unknownImports);
+ }
+ return true;
+}
+
+FindUnqualifiedIDVisitor::FindUnqualifiedIDVisitor(QStringList const &qmltypeDirs,
+ const QString &code, const QString &fileName)
+ : m_rootScope(new ScopeTree { ScopeType::JSFunctionScope, "global" }),
+ m_currentScope(m_rootScope.get()),
+ m_qmltypeDirs(qmltypeDirs),
+ m_code(code),
+ m_rootId(QLatin1String("<id>")),
+ m_filePath(fileName)
+{
+ // setup color output
+ m_colorOut.insertColorMapping(Error, ColorOutput::RedForeground);
+ m_colorOut.insertColorMapping(Warning, ColorOutput::PurpleForeground);
+ m_colorOut.insertColorMapping(Info, ColorOutput::BlueForeground);
+ m_colorOut.insertColorMapping(Normal, ColorOutput::DefaultColor);
+ m_colorOut.insertColorMapping(Hint, ColorOutput::GreenForeground);
+ QLatin1String jsGlobVars[] = {
+ /* Not listed on the MDN page; browser and QML extensions: */
+ // console/debug api
+ QLatin1String("console"), QLatin1String("print"),
+ // garbage collector
+ QLatin1String("gc"),
+ // i18n
+ QLatin1String("qsTr"), QLatin1String("qsTrId"), QLatin1String("QT_TR_NOOP"), QLatin1String("QT_TRANSLATE_NOOP"), QLatin1String("QT_TRID_NOOP"),
+ // XMLHttpRequest
+ QLatin1String("XMLHttpRequest")
+ };
+ for (const char **globalName = QV4::Compiler::Codegen::s_globalNames; *globalName != nullptr; ++globalName) {
+ m_currentScope->insertJSIdentifier(QString::fromLatin1(*globalName), QQmlJS::AST::VariableScope::Const);
+ }
+ for (const auto& jsGlobVar: jsGlobVars)
+ m_currentScope->insertJSIdentifier(jsGlobVar, QQmlJS::AST::VariableScope::Const);
+}
+
+FindUnqualifiedIDVisitor::~FindUnqualifiedIDVisitor() = default;
+
+bool FindUnqualifiedIDVisitor::check()
+{
+ if (m_visitFailed)
+ return false;
+
+ // now that all ids are known, revisit any Connections whose target were perviously unknown
+ for (auto const& outstandingConnection: m_outstandingConnections) {
+ auto metaObject = m_qmlid2meta[outstandingConnection.targetName];
+ outstandingConnection.scope->addMethodsFromMetaObject(metaObject);
+ QScopedValueRollback<ScopeTree*> rollback(m_currentScope, outstandingConnection.scope);
+ outstandingConnection.uiod->initializer->accept(this);
+ }
+ return m_rootScope->recheckIdentifiers(m_code, m_qmlid2meta, m_rootScope.get(), m_rootId, m_colorOut);
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::VariableDeclarationList *vdl)
+{
+ while (vdl) {
+ m_currentScope->insertJSIdentifier(vdl->declaration->bindingIdentifier.toString(),
+ vdl->declaration->scope);
+ vdl = vdl->next;
+ }
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr)
+{
+ using namespace QQmlJS::AST;
+ if (!fexpr->name.isEmpty()) {
+ auto name = fexpr->name.toString();
+ if (m_currentScope->scopeType() == ScopeType::QMLScope) {
+ m_currentScope->insertQMLIdentifier(name);
+ } else {
+ m_currentScope->insertJSIdentifier(name, VariableScope::Const);
+ }
+ }
+ QString name = fexpr->name.toString();
+ if (name.isEmpty())
+ name = "<anon>";
+ enterEnvironment(ScopeType::JSFunctionScope, name);
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::FunctionExpression *fexpr)
+{
+ visitFunctionExpressionHelper(fexpr);
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::FunctionExpression *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::FunctionDeclaration *fdecl)
+{
+ visitFunctionExpressionHelper(fdecl);
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::FunctionDeclaration *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::FormalParameterList *fpl)
+{
+ for (auto const &boundName : fpl->boundNames()) {
+ m_currentScope->insertJSIdentifier(boundName.id, QQmlJS::AST::VariableScope::Const);
+ }
+ return true;
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiImport *import)
+{
+ // construct path
+ QString prefix = QLatin1String("");
+ if (import->asToken.isValid()) {
+ prefix += import->importId + QLatin1Char('.');
+ }
+ auto dirname = import->fileName.toString();
+ if (!dirname.isEmpty()) {
+ QFileInfo info { dirname };
+ if (info.isRelative()) {
+ dirname = QDir(QFileInfo { m_filePath }.path()).filePath(dirname);
+ }
+ QDirIterator it { dirname, QStringList() << QLatin1String("*.qml"), QDir::NoFilter };
+ while (it.hasNext()) {
+ LanguageUtils::FakeMetaObject *fake = localQmlFile2FakeMetaObject(it.next());
+ m_exportedName2MetaObject.insert(
+ fake->className(), QSharedPointer<const LanguageUtils::FakeMetaObject>(fake));
+ }
+ }
+ QString path {};
+ if (!import->importId.isEmpty()) {
+ m_qmlid2meta.insert(import->importId.toString(), {}); // TODO: do not put imported ids into the same space as qml IDs
+ }
+ if (import->version) {
+ auto uri = import->importUri;
+ while (uri) {
+ path.append(uri->name);
+ path.append("/");
+ uri = uri->next;
+ }
+ path.chop(1);
+
+ importHelper(path, prefix, import->version->majorVersion, import->version->minorVersion);
+ }
+ return true;
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied)
+{
+ m_currentScope->insertQMLIdentifier(uied->name.toString());
+ return true;
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
+{
+ // property QtObject __styleData: QtObject {...}
+ m_currentScope->insertPropertyIdentifier(uiob->qualifiedId->name.toString());
+ QString name {};
+ auto id = uiob->qualifiedTypeNameId;
+ QStringRef prefix = uiob->qualifiedTypeNameId->name;
+ while (id) {
+ name += id->name.toString() + QLatin1Char('.');
+ id = id->next;
+ }
+ name.chop(1);
+ enterEnvironment(ScopeType::QMLScope, name);
+ if (name == QLatin1String("Component") || name == QLatin1String("QtObject")) // there is no typeinfo for Component and QtObject, but they also have no interesting properties
+ return true;
+ importExportedNames(prefix, name);
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::UiObjectBinding *)
+{
+ leaveEnvironment();
+}
+
+bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod)
+{
+ QString name {};
+ auto id = uiod->qualifiedTypeNameId;
+ QStringRef prefix = uiod->qualifiedTypeNameId->name;
+ while (id) {
+ name += id->name.toString() + QLatin1Char('.');
+ id = id->next;
+ }
+ name.chop(1);
+ enterEnvironment(ScopeType::QMLScope, name);
+ if (name.isLower())
+ return false; // Ignore grouped properties for now
+ if (name == QLatin1String("Component") || name == QLatin1String("QtObject")) // there is no typeinfo for Component
+ return true;
+ importExportedNames(prefix, name);
+ if (name.endsWith("Connections")) {
+ QString target;
+ auto member = uiod->initializer->members;
+ while (member) {
+ if (member->member->kind == QQmlJS::AST::Node::Kind_UiScriptBinding) {
+ auto asBinding = static_cast<QQmlJS::AST::UiScriptBinding*>(member->member);
+ if (asBinding->qualifiedId->name == QLatin1String("target")) {
+ if (asBinding->statement->kind == QQmlJS::AST::Node::Kind_ExpressionStatement) {
+ auto expr = static_cast<QQmlJS::AST::ExpressionStatement*>(asBinding->statement)->expression;
+ if (auto idexpr = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression*>(expr)) {
+ target = idexpr->name.toString();
+ } else {
+ // more complex expressions are not supported
+ }
+ }
+ break;
+ }
+ }
+ member = member->next;
+ }
+ LanguageUtils::FakeMetaObject::ConstPtr metaObject {};
+ if (target.isEmpty()) {
+ // no target set, connection comes from parentF
+ ScopeTree* scope = m_currentScope;
+ do {
+ scope = scope->parentScope(); // TODO: rename method
+ } while (scope->scopeType() != ScopeType::QMLScope);
+ auto metaObject = m_exportedName2MetaObject[scope->name()];
+ } else {
+ // there was a target, check if we already can find it
+ auto metaObjectIt = m_qmlid2meta.find(target);
+ if (metaObjectIt != m_qmlid2meta.end()) {
+ metaObject = *metaObjectIt;
+ } else {
+ m_outstandingConnections.push_back({target, m_currentScope, uiod});
+ return false; // visit children later once target is known
+ }
+ }
+ m_currentScope->addMethodsFromMetaObject(metaObject);
+ }
+ return true;
+}
+
+void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *)
+{
+ leaveEnvironment();
+}
+
+QDebug operator<<(QDebug dbg, const QQmlJS::AST::SourceLocation &loc)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << loc.startLine;
+ dbg.nospace() << ":";
+ dbg.nospace() << loc.startColumn;
+ return dbg.maybeSpace();
+}
diff --git a/tools/qmllint/findunqualified.h b/tools/qmllint/findunqualified.h
new file mode 100644
index 0000000000..181f42f265
--- /dev/null
+++ b/tools/qmllint/findunqualified.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE: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 FINDUNQUALIFIED_H
+#define FINDUNQUALIFIED_H
+
+#include "qmljstypedescriptionreader.h"
+#include "qcoloroutput_p.h"
+
+#include <private/qqmljsastvisitor_p.h>
+#include <private/qqmljsast_p.h>
+
+#include <QScopedPointer>
+
+class ScopeTree;
+enum class ScopeType;
+
+class FindUnqualifiedIDVisitor : public QQmlJS::AST::Visitor {
+
+public:
+ explicit FindUnqualifiedIDVisitor(QStringList const &qmltypeDirs, const QString& code, const QString& fileName);
+ ~FindUnqualifiedIDVisitor() override;
+ bool check();
+
+private:
+ QScopedPointer<ScopeTree> m_rootScope;
+ ScopeTree *m_currentScope;
+ QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> m_exportedName2MetaObject;
+ QStringList m_qmltypeDirs;
+ const QString& m_code;
+ QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> m_qmlid2meta;
+ QString m_rootId;
+ QString m_filePath;
+ QSet<QPair<QString, QString>> m_alreadySeenImports;
+ QSet<QString> m_unknownImports;
+ ColorOutput m_colorOut;
+ bool m_visitFailed = false;
+
+ struct OutstandingConnection {QString targetName; ScopeTree *scope; QQmlJS::AST::UiObjectDefinition *uiod;};
+
+ QVarLengthArray<OutstandingConnection, 3> m_outstandingConnections; // Connections whose target we have not encountered
+
+ void enterEnvironment(ScopeType type, QString name);
+ void leaveEnvironment();
+ void importHelper(QString id, QString prefix, int major, int minor);
+ LanguageUtils::FakeMetaObject* localQmlFile2FakeMetaObject(QString filePath);
+
+
+ void importExportedNames(QStringRef prefix, QString name);
+
+ void throwRecursionDepthError() override;
+
+ // start block/scope handling
+ bool visit(QQmlJS::AST::UiProgram *ast) override;
+ void endVisit(QQmlJS::AST::UiProgram *ast) override;
+
+ bool visit(QQmlJS::AST::ClassExpression *ast) override;
+ void endVisit(QQmlJS::AST::ClassExpression *ast) override;
+
+ bool visit(QQmlJS::AST::ClassDeclaration *ast) override;
+ void endVisit(QQmlJS::AST::ClassDeclaration *ast) override;
+
+ bool visit(QQmlJS::AST::ForStatement *ast) override;
+ void endVisit(QQmlJS::AST::ForStatement *ast) override;
+
+ bool visit(QQmlJS::AST::ForEachStatement *ast) override;
+ void endVisit(QQmlJS::AST::ForEachStatement *ast) override;
+
+ bool visit(QQmlJS::AST::Block *ast) override;
+ void endVisit(QQmlJS::AST::Block *ast) override;
+
+ bool visit(QQmlJS::AST::CaseBlock *ast) override;
+ void endVisit(QQmlJS::AST::CaseBlock *ast) override;
+
+ bool visit(QQmlJS::AST::Catch *ast) override;
+ void endVisit(QQmlJS::AST::Catch *ast) override;
+
+ bool visit(QQmlJS::AST::WithStatement *withStatement) override;
+ void endVisit(QQmlJS::AST::WithStatement *ast) override;
+
+ void visitFunctionExpressionHelper(QQmlJS::AST::FunctionExpression *fexpr);
+ bool visit(QQmlJS::AST::FunctionExpression *fexpr) override;
+ void endVisit(QQmlJS::AST::FunctionExpression *fexpr) override;
+
+ bool visit(QQmlJS::AST::FunctionDeclaration *fdecl) override;
+ void endVisit(QQmlJS::AST::FunctionDeclaration *fdecl) override;
+ /* --- end block handling --- */
+
+ bool visit(QQmlJS::AST::VariableDeclarationList *vdl) override;
+ bool visit(QQmlJS::AST::FormalParameterList *fpl) override;
+
+ bool visit(QQmlJS::AST::UiImport *import) override;
+ bool visit(QQmlJS::AST::UiEnumDeclaration *uied) override;
+ bool visit(QQmlJS::AST::UiObjectBinding *uiob) override;
+ void endVisit(QQmlJS::AST::UiObjectBinding *uiob) override;
+ bool visit(QQmlJS::AST::UiObjectDefinition *uiod) override;
+ void endVisit(QQmlJS::AST::UiObjectDefinition *) override;
+ bool visit(QQmlJS::AST::UiScriptBinding *uisb) override;
+ bool visit(QQmlJS::AST::UiPublicMember *uipm) override;
+
+ // expression handling
+ bool visit(QQmlJS::AST::IdentifierExpression *idexp) override;
+};
+
+
+#endif // FINDUNQUALIFIED_H
diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp
index 791fb71685..235ec16c6e 100644
--- a/tools/qmllint/main.cpp
+++ b/tools/qmllint/main.cpp
@@ -34,12 +34,19 @@
#endif
#include <QCoreApplication>
-#include <private/qv4value_p.h>
+#ifndef QT_BOOTSTRAPPED
+#include <QLibraryInfo>
+#endif
+
#include <private/qqmljslexer_p.h>
#include <private/qqmljsparser_p.h>
#include <private/qqmljsengine_p.h>
+#include <private/qqmljsastvisitor_p.h>
+#include <private/qqmljsast_p.h>
+
+#include "findunqualified.h"
-static bool lint_file(const QString &filename, bool silent)
+static bool lint_file(const QString &filename, const bool silent, const bool warnUnqualied, QStringList const &qmltypeDirs)
{
QFile file(filename);
if (!file.open(QFile::ReadOnly)) {
@@ -63,10 +70,17 @@ static bool lint_file(const QString &filename, bool silent)
if (!success && !silent) {
const auto diagnosticMessages = parser.diagnosticMessages();
for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
- qWarning("%s:%d : %s", qPrintable(filename), m.loc.startLine, qPrintable(m.message));
+ qWarning("%s:%d : %s", qPrintable(filename), m.line, qPrintable(m.message));
}
}
+ if (success && !isJavaScript && warnUnqualied) {
+ auto root = parser.rootNode();
+ FindUnqualifiedIDVisitor v { qmltypeDirs, code, filename};
+ root->accept(&v);
+ success = v.check();
+ }
+
return success;
}
@@ -80,8 +94,20 @@ int main(int argv, char *argc[])
parser.setApplicationDescription(QLatin1String("QML syntax verifier"));
parser.addHelpOption();
parser.addVersionOption();
+
QCommandLineOption silentOption(QStringList() << "s" << "silent", QLatin1String("Don't output syntax errors"));
parser.addOption(silentOption);
+
+ QCommandLineOption checkUnqualified(QStringList() << "U" << "check-unqualified", QLatin1String("Warn about unqualified identifiers"));
+ parser.addOption(checkUnqualified);
+
+ QCommandLineOption qmltypesDirsOption(
+ QStringList() << "I"
+ << "qmldirs",
+ QLatin1String("Look for qmltypes files in specified directory"),
+ QLatin1String("directory"));
+ parser.addOption(qmltypesDirsOption);
+
parser.addPositionalArgument(QLatin1String("files"), QLatin1String("list of qml or js files to verify"));
parser.process(app);
@@ -92,8 +118,18 @@ int main(int argv, char *argc[])
}
bool silent = parser.isSet(silentOption);
+ bool warnUnqualified = parser.isSet(checkUnqualified);
+ // use host qml import path as a sane default if nothing else has been provided
+ QStringList qmltypeDirs = parser.isSet(qmltypesDirsOption) ? parser.values(qmltypesDirsOption)
+#ifndef QT_BOOTSTRAPPED
+ : QStringList{QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath)};
+#else
+ : QStringList{};
+#endif
#else
bool silent = false;
+ bool warnUnqualified = false;
+ QStringList qmltypeDirs {};
#endif
bool success = true;
#if QT_CONFIG(commandlineparser)
@@ -102,7 +138,7 @@ int main(int argv, char *argc[])
const auto arguments = app.arguments();
for (const QString &filename : arguments)
#endif
- success &= lint_file(filename, silent);
+ success &= lint_file(filename, silent, warnUnqualified, qmltypeDirs);
return success ? 0 : -1;
}
diff --git a/tools/qmllint/qcoloroutput.cpp b/tools/qmllint/qcoloroutput.cpp
new file mode 100644
index 0000000000..d2e723700a
--- /dev/null
+++ b/tools/qmllint/qcoloroutput.cpp
@@ -0,0 +1,342 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE: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 <QFile>
+#include <QHash>
+#include <QTextCodec>
+
+#ifndef Q_OS_WIN
+#include <unistd.h>
+#endif
+
+#include "qcoloroutput_p.h"
+
+class ColorOutputPrivate
+{
+public:
+ ColorOutputPrivate() : currentColorID(-1)
+
+ {
+ /* - QIODevice::Unbuffered because we want it to appear when the user actually calls, performance
+ * is considered of lower priority.
+ */
+ m_out.open(stderr, QIODevice::WriteOnly | QIODevice::Unbuffered);
+
+ coloringEnabled = isColoringPossible();
+ }
+
+ ColorOutput::ColorMapping colorMapping;
+ int currentColorID;
+ bool coloringEnabled;
+
+ static const char *const foregrounds[];
+ static const char *const backgrounds[];
+
+ inline void write(const QString &msg)
+ {
+ m_out.write(msg.toLocal8Bit());
+ }
+
+ static QString escapeCode(const QString &in)
+ {
+ QString result;
+ result.append(QChar(0x1B));
+ result.append(QLatin1Char('['));
+ result.append(in);
+ result.append(QLatin1Char('m'));
+ return result;
+ }
+
+private:
+ QFile m_out;
+
+ /*!
+ Returns true if it's suitable to send colored output to \c stderr.
+ */
+ inline bool isColoringPossible() const
+ {
+# if defined(Q_OS_WIN)
+ /* Windows doesn't at all support ANSI escape codes, unless
+ * the user install a "device driver". See the Wikipedia links in the
+ * class documentation for details. */
+ return false;
+# else
+ /* We use QFile::handle() to get the file descriptor. It's a bit unsure
+ * whether it's 2 on all platforms and in all cases, so hopefully this layer
+ * of abstraction helps handle such cases. */
+ return isatty(m_out.handle());
+# endif
+ }
+};
+
+const char *const ColorOutputPrivate::foregrounds[] =
+{
+ "0;30",
+ "0;34",
+ "0;32",
+ "0;36",
+ "0;31",
+ "0;35",
+ "0;33",
+ "0;37",
+ "1;30",
+ "1;34",
+ "1;32",
+ "1;36",
+ "1;31",
+ "1;35",
+ "1;33",
+ "1;37"
+};
+
+const char *const ColorOutputPrivate::backgrounds[] =
+{
+ "0;40",
+ "0;44",
+ "0;42",
+ "0;46",
+ "0;41",
+ "0;45",
+ "0;43"
+};
+
+/*!
+ \class ColorOutput
+ \since 4.4
+ \nonreentrant
+ \brief Outputs colored messages to \c stderr.
+ \internal
+
+ ColorOutput is a convenience class for outputting messages to \c
+ stderr using color escape codes, as mandated in ECMA-48. ColorOutput
+ will only color output when it is detected to be suitable. For
+ instance, if \c stderr is detected to be attached to a file instead
+ of a TTY, no coloring will be done.
+
+ ColorOutput does its best attempt. but it is generally undefined
+ what coloring or effect the various coloring flags has. It depends
+ strongly on what terminal software that is being used.
+
+ When using `echo -e 'my escape sequence'`, \c{\033} works as an
+ initiator but not when printing from a C++ program, despite having
+ escaped the backslash. That's why we below use characters with
+ value 0x1B.
+
+ It can be convenient to subclass ColorOutput with a private scope,
+ such that the functions are directly available in the class using
+ it.
+
+ \section1 Usage
+
+ To output messages, call write() or writeUncolored(). write() takes
+ as second argument an integer, which ColorOutput uses as a lookup
+ key to find the color it should color the text in. The mapping from
+ keys to colors is done using insertMapping(). Typically this is used
+ by having enums for the various kinds of messages, which
+ subsequently are registered.
+
+ \code
+ enum MyMessage
+ {
+ Error,
+ Important
+ };
+
+ ColorOutput output;
+ output.insertMapping(Error, ColorOutput::RedForeground);
+ output.insertMapping(Import, ColorOutput::BlueForeground);
+
+ output.write("This is important", Important);
+ output.write("Jack, I'm only the selected official!", Error);
+ \endcode
+
+ \sa {http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html}{Bash Prompt HOWTO, 6.1. Colors},
+ {http://linuxgazette.net/issue51/livingston-blade.html}{Linux Gazette, Tweaking Eterm, Edward Livingston-Blade},
+ {http://www.ecma-international.org/publications/standards/Ecma-048.htm}{Standard ECMA-48, Control Functions for Coded Character Sets, ECMA International},
+ {http://en.wikipedia.org/wiki/ANSI_escape_code}{Wikipedia, ANSI escape code},
+ {http://linuxgazette.net/issue65/padala.html}{Linux Gazette, So You Like Color!, Pradeep Padala}
+ */
+
+/*!
+ \enum ColorOutput::ColorCodeComponent
+ \value BlackForeground
+ \value BlueForeground
+ \value GreenForeground
+ \value CyanForeground
+ \value RedForeground
+ \value PurpleForeground
+ \value BrownForeground
+ \value LightGrayForeground
+ \value DarkGrayForeground
+ \value LightBlueForeground
+ \value LightGreenForeground
+ \value LightCyanForeground
+ \value LightRedForeground
+ \value LightPurpleForeground
+ \value YellowForeground
+ \value WhiteForeground
+ \value BlackBackground
+ \value BlueBackground
+ \value GreenBackground
+ \value CyanBackground
+ \value RedBackground
+ \value PurpleBackground
+ \value BrownBackground
+
+ \value DefaultColor ColorOutput performs no coloring. This typically
+ means black on white or white on black, depending
+ on the settings of the user's terminal.
+ */
+
+/*!
+ Sets the color mapping to be \a cMapping.
+
+ Negative values are disallowed.
+
+ \sa colorMapping(), insertMapping()
+ */
+void ColorOutput::setColorMapping(const ColorMapping &cMapping)
+{
+ d->colorMapping = cMapping;
+}
+
+/*!
+ Returns the color mappings in use.
+
+ \sa setColorMapping(), insertMapping()
+ */
+ColorOutput::ColorMapping ColorOutput::colorMapping() const
+{
+ return d->colorMapping;
+}
+
+/*!
+ Constructs a ColorOutput instance, ready for use.
+ */
+ColorOutput::ColorOutput() : d(new ColorOutputPrivate())
+{
+}
+
+ColorOutput::~ColorOutput() = default; // must be here so that QScopedPointer has access to the complete type
+
+/*!
+ Sends \a message to \c stderr, using the color looked up in colorMapping() using \a colorID.
+
+ If \a color isn't available in colorMapping(), result and behavior is undefined.
+
+ If \a colorID is 0, which is the default value, the previously used coloring is used. ColorOutput
+ is initialized to not color at all.
+
+ If \a message is empty, effects are undefined.
+
+ \a message will be printed as is. For instance, no line endings will be inserted.
+ */
+void ColorOutput::write(const QString &message, int colorID)
+{
+ d->write(colorify(message, colorID));
+}
+
+/*!
+ Writes \a message to \c stderr as if for instance
+ QTextStream would have been used, and adds a line ending at the end.
+
+ This function can be practical to use such that one can use ColorOutput for all forms of writing.
+ */
+void ColorOutput::writeUncolored(const QString &message)
+{
+ d->write(message + QLatin1Char('\n'));
+}
+
+/*!
+ Treats \a message and \a colorID identically to write(), but instead of writing
+ \a message to \c stderr, it is prepared for being written to \c stderr, but is then
+ returned.
+
+ This is useful when the colored string is inserted into a translated string(dividing
+ the string into several small strings prevents proper translation).
+ */
+QString ColorOutput::colorify(const QString &message, int colorID) const
+{
+ Q_ASSERT_X(colorID == -1 || d->colorMapping.contains(colorID), Q_FUNC_INFO,
+ qPrintable(QString::fromLatin1("There is no color registered by id %1").arg(colorID)));
+ Q_ASSERT_X(!message.isEmpty(), Q_FUNC_INFO, "It makes no sense to attempt to print an empty string.");
+
+ if (colorID != -1)
+ d->currentColorID = colorID;
+
+ if (d->coloringEnabled && colorID != -1)
+ {
+ const int color(d->colorMapping.value(colorID));
+
+ /* If DefaultColor is set, we don't want to color it. */
+ if (color & DefaultColor)
+ return message;
+
+ const int foregroundCode = (int(color) & ForegroundMask) >> ForegroundShift;
+ const int backgroundCode = (int(color) & BackgroundMask) >> BackgroundShift;
+ QString finalMessage;
+ bool closureNeeded = false;
+
+ if (foregroundCode)
+ {
+ finalMessage.append(ColorOutputPrivate::escapeCode(QLatin1String(ColorOutputPrivate::foregrounds[foregroundCode - 1])));
+ closureNeeded = true;
+ }
+
+ if (backgroundCode)
+ {
+ finalMessage.append(ColorOutputPrivate::escapeCode(QLatin1String(ColorOutputPrivate::backgrounds[backgroundCode - 1])));
+ closureNeeded = true;
+ }
+
+ finalMessage.append(message);
+
+ if (closureNeeded)
+ {
+ finalMessage.append(QChar(0x1B));
+ finalMessage.append(QLatin1String("[0m"));
+ }
+
+ return finalMessage;
+ }
+ else
+ return message;
+}
+
+/*!
+ Adds a color mapping from \a colorID to \a colorCode, for this ColorOutput instance.
+
+ This is a convenience function for creating a ColorOutput::ColorMapping instance and
+ calling setColorMapping().
+
+ \sa colorMapping(), setColorMapping()
+ */
+void ColorOutput::insertColorMapping(int colorID, const ColorCode colorCode)
+{
+ d->colorMapping.insert(colorID, colorCode);
+}
diff --git a/tools/qmllint/qcoloroutput_p.h b/tools/qmllint/qcoloroutput_p.h
new file mode 100644
index 0000000000..710bf5db74
--- /dev/null
+++ b/tools/qmllint/qcoloroutput_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE: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$
+**
+****************************************************************************/
+
+//
+// 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.
+
+#ifndef QCOLOROUTPUT_P_H
+#define QCOLOROUTPUT_P_H
+
+#include <QtCore/QtGlobal>
+#include <QtCore/QHash>
+#include <QScopedPointer>
+
+class ColorOutputPrivate;
+
+class ColorOutput
+{
+ enum
+ {
+ ForegroundShift = 10,
+ BackgroundShift = 20,
+ SpecialShift = 20,
+ ForegroundMask = 0x1f << ForegroundShift,
+ BackgroundMask = 0x7 << BackgroundShift
+ };
+
+public:
+ enum ColorCodeComponent
+ {
+ BlackForeground = 1 << ForegroundShift,
+ BlueForeground = 2 << ForegroundShift,
+ GreenForeground = 3 << ForegroundShift,
+ CyanForeground = 4 << ForegroundShift,
+ RedForeground = 5 << ForegroundShift,
+ PurpleForeground = 6 << ForegroundShift,
+ BrownForeground = 7 << ForegroundShift,
+ LightGrayForeground = 8 << ForegroundShift,
+ DarkGrayForeground = 9 << ForegroundShift,
+ LightBlueForeground = 10 << ForegroundShift,
+ LightGreenForeground = 11 << ForegroundShift,
+ LightCyanForeground = 12 << ForegroundShift,
+ LightRedForeground = 13 << ForegroundShift,
+ LightPurpleForeground = 14 << ForegroundShift,
+ YellowForeground = 15 << ForegroundShift,
+ WhiteForeground = 16 << ForegroundShift,
+
+ BlackBackground = 1 << BackgroundShift,
+ BlueBackground = 2 << BackgroundShift,
+ GreenBackground = 3 << BackgroundShift,
+ CyanBackground = 4 << BackgroundShift,
+ RedBackground = 5 << BackgroundShift,
+ PurpleBackground = 6 << BackgroundShift,
+ BrownBackground = 7 << BackgroundShift,
+ DefaultColor = 1 << SpecialShift
+ };
+
+ typedef QFlags<ColorCodeComponent> ColorCode;
+ typedef QHash<int, ColorCode> ColorMapping;
+
+ ColorOutput();
+ ~ColorOutput();
+
+ void setColorMapping(const ColorMapping &cMapping);
+ ColorMapping colorMapping() const;
+ void insertColorMapping(int colorID, const ColorCode colorCode);
+
+ void writeUncolored(const QString &message);
+ void write(const QString &message, int color = -1);
+ QString colorify(const QString &message, int color = -1) const;
+
+private:
+ QScopedPointer<ColorOutputPrivate> d;
+ Q_DISABLE_COPY(ColorOutput)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(ColorOutput::ColorCode)
+
+#endif
diff --git a/tools/qmllint/qmljstypedescriptionreader.cpp b/tools/qmllint/qmljstypedescriptionreader.cpp
new file mode 100644
index 0000000000..542cdf99eb
--- /dev/null
+++ b/tools/qmllint/qmljstypedescriptionreader.cpp
@@ -0,0 +1,704 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE: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 "qmljstypedescriptionreader.h"
+
+#include <private/qqmljsparser_p.h>
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsengine_p.h>
+
+#include <QDir>
+
+#define QTC_ASSERT_STRINGIFY_HELPER(x) #x
+#define QTC_ASSERT_STRINGIFY(x) QTC_ASSERT_STRINGIFY_HELPER(x)
+#define QTC_ASSERT_STRING(cond) qDebug() << (\
+ "\"" cond"\" in file " __FILE__ ", line " QTC_ASSERT_STRINGIFY(__LINE__))
+#define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0)
+
+using namespace QQmlJS;
+using namespace QQmlJS::AST;
+using namespace LanguageUtils;
+
+QString toString(const AST::UiQualifiedId *qualifiedId, QChar delimiter = QLatin1Char('.'))
+{
+ QString result;
+
+ for (const UiQualifiedId *iter = qualifiedId; iter; iter = iter->next) {
+ if (iter != qualifiedId)
+ result += delimiter;
+
+ result += iter->name;
+ }
+
+ return result;
+}
+
+TypeDescriptionReader::TypeDescriptionReader(const QString &fileName, const QString &data)
+ : _fileName (fileName), _source(data), _objects(0)
+{
+}
+
+TypeDescriptionReader::~TypeDescriptionReader()
+{
+}
+
+bool TypeDescriptionReader::operator()(
+ QHash<QString, FakeMetaObject::ConstPtr> *objects,
+ QList<ModuleApiInfo> *moduleApis,
+ QStringList *dependencies)
+{
+ Engine engine;
+
+ Lexer lexer(&engine);
+ Parser parser(&engine);
+
+ lexer.setCode(_source, /*line = */ 1, /*qmlMode = */true);
+
+ if (!parser.parse()) {
+ _errorMessage = QString::fromLatin1("%1:%2: %3").arg(
+ QString::number(parser.errorLineNumber()),
+ QString::number(parser.errorColumnNumber()),
+ parser.errorMessage());
+ return false;
+ }
+
+ _objects = objects;
+ _moduleApis = moduleApis;
+ _dependencies = dependencies;
+ readDocument(parser.ast());
+
+ return _errorMessage.isEmpty();
+}
+
+QString TypeDescriptionReader::errorMessage() const
+{
+ return _errorMessage;
+}
+
+QString TypeDescriptionReader::warningMessage() const
+{
+ return _warningMessage;
+}
+
+void TypeDescriptionReader::readDocument(UiProgram *ast)
+{
+ if (!ast) {
+ addError(SourceLocation(), tr("Could not parse document."));
+ return;
+ }
+
+ if (!ast->headers || ast->headers->next || !AST::cast<AST::UiImport *>(ast->headers->headerItem)) {
+ addError(SourceLocation(), tr("Expected a single import."));
+ return;
+ }
+
+ UiImport *import = AST::cast<AST::UiImport *>(ast->headers->headerItem);
+ if (toString(import->importUri) != QLatin1String("QtQuick.tooling")) {
+ addError(import->importToken, tr("Expected import of QtQuick.tooling."));
+ return;
+ }
+
+ ComponentVersion version;
+ const QString versionString = _source.mid(import->versionToken.offset, import->versionToken.length);
+ const int dotIdx = versionString.indexOf(QLatin1Char('.'));
+ if (dotIdx != -1) {
+ version = ComponentVersion(versionString.leftRef(dotIdx).toInt(),
+ versionString.midRef(dotIdx + 1).toInt());
+ }
+ if (version.majorVersion() != 1) {
+ addError(import->versionToken, tr("Major version different from 1 not supported."));
+ return;
+ }
+
+ if (!ast->members || !ast->members->member || ast->members->next) {
+ addError(SourceLocation(), tr("Expected document to contain a single object definition."));
+ return;
+ }
+
+ UiObjectDefinition *module = AST::cast<UiObjectDefinition *>(ast->members->member);
+ if (!module) {
+ addError(SourceLocation(), tr("Expected document to contain a single object definition."));
+ return;
+ }
+
+ if (toString(module->qualifiedTypeNameId) != QLatin1String("Module")) {
+ addError(SourceLocation(), tr("Expected document to contain a Module {} member."));
+ return;
+ }
+
+ readModule(module);
+}
+
+void TypeDescriptionReader::readModule(UiObjectDefinition *ast)
+{
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiObjectDefinition *component = AST::cast<UiObjectDefinition *>(member);
+
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ if (script && (toString(script->qualifiedId) == QStringLiteral("dependencies"))) {
+ readDependencies(script);
+ continue;
+ }
+
+ QString typeName;
+ if (component)
+ typeName = toString(component->qualifiedTypeNameId);
+
+ if (!component || (typeName != QLatin1String("Component") && typeName != QLatin1String("ModuleApi"))) {
+ continue;
+ }
+
+ if (typeName == QLatin1String("Component"))
+ readComponent(component);
+ else if (typeName == QLatin1String("ModuleApi"))
+ readModuleApi(component);
+ }
+}
+
+void TypeDescriptionReader::addError(const SourceLocation &loc, const QString &message)
+{
+ _errorMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
+ QDir::toNativeSeparators(_fileName),
+ QString::number(loc.startLine),
+ QString::number(loc.startColumn),
+ message);
+}
+
+void TypeDescriptionReader::addWarning(const SourceLocation &loc, const QString &message)
+{
+ _warningMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
+ QDir::toNativeSeparators(_fileName),
+ QString::number(loc.startLine),
+ QString::number(loc.startColumn),
+ message);
+}
+
+void TypeDescriptionReader::readDependencies(UiScriptBinding *ast)
+{
+ ExpressionStatement *stmt = AST::cast<ExpressionStatement*>(ast->statement);
+ if (!stmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected dependency definitions"));
+ return;
+ }
+ ArrayPattern *exp = AST::cast<ArrayPattern *>(stmt->expression);
+ if (!exp) {
+ addError(stmt->expression->firstSourceLocation(), tr("Expected dependency definitions"));
+ return;
+ }
+ for (PatternElementList *l = exp->elements; l; l = l->next) {
+ //StringLiteral *str = AST::cast<StringLiteral *>(l->element->initializer);
+ StringLiteral *str = AST::cast<StringLiteral *>(l->element->initializer);
+ *_dependencies << str->value.toString();
+ }
+}
+
+void TypeDescriptionReader::readComponent(UiObjectDefinition *ast)
+{
+ FakeMetaObject::Ptr fmo(new FakeMetaObject);
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiObjectDefinition *component = AST::cast<UiObjectDefinition *>(member);
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ if (component) {
+ QString name = toString(component->qualifiedTypeNameId);
+ if (name == QLatin1String("Property"))
+ readProperty(component, fmo);
+ else if (name == QLatin1String("Method") || name == QLatin1String("Signal"))
+ readSignalOrMethod(component, name == QLatin1String("Method"), fmo);
+ else if (name == QLatin1String("Enum"))
+ readEnum(component, fmo);
+ else
+ addWarning(component->firstSourceLocation(),
+ tr("Expected only Property, Method, Signal and Enum object definitions, not \"%1\".")
+ .arg(name));
+ } else if (script) {
+ QString name = toString(script->qualifiedId);
+ if (name == QLatin1String("name")) {
+ fmo->setClassName(readStringBinding(script));
+ } else if (name == QLatin1String("prototype")) {
+ fmo->setSuperclassName(readStringBinding(script));
+ } else if (name == QLatin1String("defaultProperty")) {
+ fmo->setDefaultPropertyName(readStringBinding(script));
+ } else if (name == QLatin1String("exports")) {
+ readExports(script, fmo);
+ } else if (name == QLatin1String("exportMetaObjectRevisions")) {
+ readMetaObjectRevisions(script, fmo);
+ } else if (name == QLatin1String("attachedType")) {
+ fmo->setAttachedTypeName(readStringBinding(script));
+ } else if (name == QLatin1String("isSingleton")) {
+ fmo->setIsSingleton(readBoolBinding(script));
+ } else if (name == QLatin1String("isCreatable")) {
+ fmo->setIsCreatable(readBoolBinding(script));
+ } else if (name == QLatin1String("isComposite")) {
+ fmo->setIsComposite(readBoolBinding(script));
+ } else {
+ addWarning(script->firstSourceLocation(),
+ tr("Expected only name, prototype, defaultProperty, attachedType, exports, "
+ "isSingleton, isCreatable, isComposite and exportMetaObjectRevisions "
+ "script bindings, not \"%1\".").arg(name));
+ }
+ } else {
+ addWarning(member->firstSourceLocation(), tr("Expected only script bindings and object definitions."));
+ }
+ }
+
+ if (fmo->className().isEmpty()) {
+ addError(ast->firstSourceLocation(), tr("Component definition is missing a name binding."));
+ return;
+ }
+
+ // ### add implicit export into the package of c++ types
+ fmo->addExport(fmo->className(), QStringLiteral("<cpp>"), ComponentVersion());
+ fmo->updateFingerprint();
+ _objects->insert(fmo->className(), fmo);
+}
+
+void TypeDescriptionReader::readModuleApi(UiObjectDefinition *ast)
+{
+ ModuleApiInfo apiInfo;
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+
+ if (script) {
+ const QString name = toString(script->qualifiedId);
+ if (name == QLatin1String("uri")) {
+ apiInfo.uri = readStringBinding(script);
+ } else if (name == QLatin1String("version")) {
+ apiInfo.version = readNumericVersionBinding(script);
+ } else if (name == QLatin1String("name")) {
+ apiInfo.cppName = readStringBinding(script);
+ } else {
+ addWarning(script->firstSourceLocation(),
+ tr("Expected only uri, version and name script bindings."));
+ }
+ } else {
+ addWarning(member->firstSourceLocation(), tr("Expected only script bindings."));
+ }
+ }
+
+ if (!apiInfo.version.isValid()) {
+ addError(ast->firstSourceLocation(), tr("ModuleApi definition has no or invalid version binding."));
+ return;
+ }
+
+ if (_moduleApis)
+ _moduleApis->append(apiInfo);
+}
+
+void TypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bool isMethod, FakeMetaObject::Ptr fmo)
+{
+ FakeMetaMethod fmm;
+ // ### confusion between Method and Slot. Method should be removed.
+ if (isMethod)
+ fmm.setMethodType(FakeMetaMethod::Slot);
+ else
+ fmm.setMethodType(FakeMetaMethod::Signal);
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiObjectDefinition *component = AST::cast<UiObjectDefinition *>(member);
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ if (component) {
+ QString name = toString(component->qualifiedTypeNameId);
+ if (name == QLatin1String("Parameter"))
+ readParameter(component, &fmm);
+ else
+ addWarning(component->firstSourceLocation(), tr("Expected only Parameter object definitions."));
+ } else if (script) {
+ QString name = toString(script->qualifiedId);
+ if (name == QLatin1String("name"))
+ fmm.setMethodName(readStringBinding(script));
+ else if (name == QLatin1String("type"))
+ fmm.setReturnType(readStringBinding(script));
+ else if (name == QLatin1String("revision"))
+ fmm.setRevision(readIntBinding(script));
+ else
+ addWarning(script->firstSourceLocation(), tr("Expected only name and type script bindings."));
+
+ } else {
+ addWarning(member->firstSourceLocation(), tr("Expected only script bindings and object definitions."));
+ }
+ }
+
+ if (fmm.methodName().isEmpty()) {
+ addError(ast->firstSourceLocation(), tr("Method or signal is missing a name script binding."));
+ return;
+ }
+
+ fmo->addMethod(fmm);
+}
+
+void TypeDescriptionReader::readProperty(UiObjectDefinition *ast, FakeMetaObject::Ptr fmo)
+{
+ QString name;
+ QString type;
+ bool isPointer = false;
+ bool isReadonly = false;
+ bool isList = false;
+ int revision = 0;
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ if (!script) {
+ addWarning(member->firstSourceLocation(), tr("Expected script binding."));
+ continue;
+ }
+
+ QString id = toString(script->qualifiedId);
+ if (id == QLatin1String("name"))
+ name = readStringBinding(script);
+ else if (id == QLatin1String("type"))
+ type = readStringBinding(script);
+ else if (id == QLatin1String("isPointer"))
+ isPointer = readBoolBinding(script);
+ else if (id == QLatin1String("isReadonly"))
+ isReadonly = readBoolBinding(script);
+ else if (id == QLatin1String("isList"))
+ isList = readBoolBinding(script);
+ else if (id == QLatin1String("revision"))
+ revision = readIntBinding(script);
+ else
+ addWarning(script->firstSourceLocation(), tr("Expected only type, name, revision, isPointer, isReadonly and isList script bindings."));
+ }
+
+ if (name.isEmpty() || type.isEmpty()) {
+ addError(ast->firstSourceLocation(), tr("Property object is missing a name or type script binding."));
+ return;
+ }
+
+ fmo->addProperty(FakeMetaProperty(name, type, isList, !isReadonly, isPointer, revision));
+}
+
+void TypeDescriptionReader::readEnum(UiObjectDefinition *ast, FakeMetaObject::Ptr fmo)
+{
+ FakeMetaEnum fme;
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ if (!script) {
+ addWarning(member->firstSourceLocation(), tr("Expected script binding."));
+ continue;
+ }
+
+ QString name = toString(script->qualifiedId);
+ if (name == QLatin1String("name"))
+ fme.setName(readStringBinding(script));
+ else if (name == QLatin1String("values"))
+ readEnumValues(script, &fme);
+ else
+ addWarning(script->firstSourceLocation(), tr("Expected only name and values script bindings."));
+ }
+
+ fmo->addEnum(fme);
+}
+
+void TypeDescriptionReader::readParameter(UiObjectDefinition *ast, FakeMetaMethod *fmm)
+{
+ QString name;
+ QString type;
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ UiScriptBinding *script = AST::cast<UiScriptBinding *>(member);
+ if (!script) {
+ addWarning(member->firstSourceLocation(), tr("Expected script binding."));
+ continue;
+ }
+
+ const QString id = toString(script->qualifiedId);
+ if (id == QLatin1String("name")) {
+ name = readStringBinding(script);
+ } else if (id == QLatin1String("type")) {
+ type = readStringBinding(script);
+ } else if (id == QLatin1String("isPointer")) {
+ // ### unhandled
+ } else if (id == QLatin1String("isReadonly")) {
+ // ### unhandled
+ } else if (id == QLatin1String("isList")) {
+ // ### unhandled
+ } else {
+ addWarning(script->firstSourceLocation(), tr("Expected only name and type script bindings."));
+ }
+ }
+
+ fmm->addParameter(name, type);
+}
+
+QString TypeDescriptionReader::readStringBinding(UiScriptBinding *ast)
+{
+ QTC_ASSERT(ast, return QString());
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected string after colon."));
+ return QString();
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected string after colon."));
+ return QString();
+ }
+
+ StringLiteral *stringLit = AST::cast<StringLiteral *>(expStmt->expression);
+ if (!stringLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected string after colon."));
+ return QString();
+ }
+
+ return stringLit->value.toString();
+}
+
+bool TypeDescriptionReader::readBoolBinding(AST::UiScriptBinding *ast)
+{
+ QTC_ASSERT(ast, return false);
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected boolean after colon."));
+ return false;
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected boolean after colon."));
+ return false;
+ }
+
+ TrueLiteral *trueLit = AST::cast<TrueLiteral *>(expStmt->expression);
+ FalseLiteral *falseLit = AST::cast<FalseLiteral *>(expStmt->expression);
+ if (!trueLit && !falseLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected true or false after colon."));
+ return false;
+ }
+
+ return trueLit;
+}
+
+double TypeDescriptionReader::readNumericBinding(AST::UiScriptBinding *ast)
+{
+ QTC_ASSERT(ast, return qQNaN());
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected numeric literal after colon."));
+ return 0;
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected numeric literal after colon."));
+ return 0;
+ }
+
+ NumericLiteral *numericLit = AST::cast<NumericLiteral *>(expStmt->expression);
+ if (!numericLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
+ return 0;
+ }
+
+ return numericLit->value;
+}
+
+ComponentVersion TypeDescriptionReader::readNumericVersionBinding(UiScriptBinding *ast)
+{
+ ComponentVersion invalidVersion;
+
+ if (!ast || !ast->statement) {
+ addError((ast ? ast->colonToken : SourceLocation()), tr("Expected numeric literal after colon."));
+ return invalidVersion;
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected numeric literal after colon."));
+ return invalidVersion;
+ }
+
+ NumericLiteral *numericLit = AST::cast<NumericLiteral *>(expStmt->expression);
+ if (!numericLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
+ return invalidVersion;
+ }
+
+ return ComponentVersion(_source.mid(numericLit->literalToken.begin(), numericLit->literalToken.length));
+}
+
+int TypeDescriptionReader::readIntBinding(AST::UiScriptBinding *ast)
+{
+ double v = readNumericBinding(ast);
+ int i = static_cast<int>(v);
+
+ if (i != v) {
+ addError(ast->firstSourceLocation(), tr("Expected integer after colon."));
+ return 0;
+ }
+
+ return i;
+}
+
+void TypeDescriptionReader::readExports(UiScriptBinding *ast, FakeMetaObject::Ptr fmo)
+{
+ QTC_ASSERT(ast, return);
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected array of strings after colon."));
+ return;
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected array of strings after colon."));
+ return;
+ }
+
+ ArrayPattern *arrayLit = AST::cast<ArrayPattern *>(expStmt->expression);
+ if (!arrayLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected array of strings after colon."));
+ return;
+ }
+
+ for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
+ StringLiteral *stringLit = AST::cast<StringLiteral *>(it->element->initializer);
+ if (!stringLit) {
+ addError(arrayLit->firstSourceLocation(), tr("Expected array literal with only string literal members."));
+ return;
+ }
+ QString exp = stringLit->value.toString();
+ int slashIdx = exp.indexOf(QLatin1Char('/'));
+ int spaceIdx = exp.indexOf(QLatin1Char(' '));
+ ComponentVersion version(exp.mid(spaceIdx + 1));
+
+ if (spaceIdx == -1 || !version.isValid()) {
+ addError(stringLit->firstSourceLocation(), tr("Expected string literal to contain 'Package/Name major.minor' or 'Name major.minor'."));
+ continue;
+ }
+ QString package;
+ if (slashIdx != -1)
+ package = exp.left(slashIdx);
+ QString name = exp.mid(slashIdx + 1, spaceIdx - (slashIdx+1));
+
+ // ### relocatable exports where package is empty?
+ fmo->addExport(name, package, version);
+ }
+}
+
+void TypeDescriptionReader::readMetaObjectRevisions(UiScriptBinding *ast, FakeMetaObject::Ptr fmo)
+{
+ QTC_ASSERT(ast, return);
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected array of numbers after colon."));
+ return;
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected array of numbers after colon."));
+ return;
+ }
+
+ ArrayPattern *arrayLit = AST::cast<ArrayPattern *>(expStmt->expression);
+ if (!arrayLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected array of numbers after colon."));
+ return;
+ }
+
+ int exportIndex = 0;
+ const int exportCount = fmo->exports().size();
+ for (PatternElementList *it = arrayLit->elements; it; it = it->next, ++exportIndex) {
+ NumericLiteral *numberLit = cast<NumericLiteral *>(it->element->initializer);
+ if (!numberLit) {
+ addError(arrayLit->firstSourceLocation(), tr("Expected array literal with only number literal members."));
+ return;
+ }
+
+ if (exportIndex >= exportCount) {
+ addError(numberLit->firstSourceLocation(), tr("Meta object revision without matching export."));
+ return;
+ }
+
+ const double v = numberLit->value;
+ const int metaObjectRevision = static_cast<int>(v);
+ if (metaObjectRevision != v) {
+ addError(numberLit->firstSourceLocation(), tr("Expected integer."));
+ return;
+ }
+
+ fmo->setExportMetaObjectRevision(exportIndex, metaObjectRevision);
+ }
+}
+
+void TypeDescriptionReader::readEnumValues(AST::UiScriptBinding *ast, LanguageUtils::FakeMetaEnum *fme)
+{
+ if (!ast)
+ return;
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected object literal after colon."));
+ return;
+ }
+
+ ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected object literal after colon."));
+ return;
+ }
+
+ ObjectPattern *objectLit = AST::cast<ObjectPattern *>(expStmt->expression);
+ if (!objectLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected object literal after colon."));
+ return;
+ }
+
+ for (PatternPropertyList *it = objectLit->properties; it; it = it->next) {
+ PatternProperty *assignement = AST::cast<PatternProperty *>(it->property);
+ if (assignement) {
+ StringLiteralPropertyName *propName = AST::cast<StringLiteralPropertyName *>(assignement->name);
+ NumericLiteral *value = AST::cast<NumericLiteral *>(assignement->initializer);
+ UnaryMinusExpression *minus = AST::cast<UnaryMinusExpression *>(assignement->initializer);
+ if (minus)
+ value = AST::cast<NumericLiteral *>(minus->expression);
+ if (!propName || !value) {
+ addError(objectLit->firstSourceLocation(), tr("Expected object literal to contain only 'string: number' elements."));
+ continue;
+ }
+
+ double v = value->value;
+ if (minus)
+ v = -v;
+ fme->addKey(propName->id.toString(), v);
+ continue;
+ }
+ PatternPropertyList *getterSetter = AST::cast<PatternPropertyList *>(it->next);
+ if (getterSetter)
+ addError(objectLit->firstSourceLocation(), tr("Enum should not contain getter and setters, but only 'string: number' elements."));
+ }
+}
diff --git a/tools/qmllint/qmljstypedescriptionreader.h b/tools/qmllint/qmljstypedescriptionreader.h
new file mode 100644
index 0000000000..df215af8d2
--- /dev/null
+++ b/tools/qmllint/qmljstypedescriptionreader.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE: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 QMLJSTYPEDESCRIPTIONREADER_H
+#define QMLJSTYPEDESCRIPTIONREADER_H
+
+#include <private/qqmljsastfwd_p.h>
+#include "fakemetaobject.h"
+
+// for Q_DECLARE_TR_FUNCTIONS
+#include <QCoreApplication>
+
+QT_BEGIN_NAMESPACE
+class QIODevice;
+class QBuffer;
+
+namespace QQmlJS {
+
+class ModuleApiInfo
+{
+public:
+ QString uri;
+ LanguageUtils::ComponentVersion version;
+ QString cppName;
+};
+
+
+class TypeDescriptionReader
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlJS::TypeDescriptionReader)
+
+public:
+ explicit TypeDescriptionReader(const QString &fileName, const QString &data);
+ ~TypeDescriptionReader();
+
+ bool operator()(
+ QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> *objects,
+ QList<ModuleApiInfo> *moduleApis,
+ QStringList *dependencies);
+ QString errorMessage() const;
+ QString warningMessage() const;
+
+private:
+ void readDocument(AST::UiProgram *ast);
+ void readModule(AST::UiObjectDefinition *ast);
+ void readDependencies(AST::UiScriptBinding *ast);
+ void readComponent(AST::UiObjectDefinition *ast);
+ void readModuleApi(AST::UiObjectDefinition *ast);
+ void readSignalOrMethod(AST::UiObjectDefinition *ast, bool isMethod, LanguageUtils::FakeMetaObject::Ptr fmo);
+ void readProperty(AST::UiObjectDefinition *ast, LanguageUtils::FakeMetaObject::Ptr fmo);
+ void readEnum(AST::UiObjectDefinition *ast, LanguageUtils::FakeMetaObject::Ptr fmo);
+ void readParameter(AST::UiObjectDefinition *ast, LanguageUtils::FakeMetaMethod *fmm);
+
+ QString readStringBinding(AST::UiScriptBinding *ast);
+ bool readBoolBinding(AST::UiScriptBinding *ast);
+ double readNumericBinding(AST::UiScriptBinding *ast);
+ LanguageUtils::ComponentVersion readNumericVersionBinding(AST::UiScriptBinding *ast);
+ int readIntBinding(AST::UiScriptBinding *ast);
+ void readExports(AST::UiScriptBinding *ast, LanguageUtils::FakeMetaObject::Ptr fmo);
+ void readMetaObjectRevisions(AST::UiScriptBinding *ast, LanguageUtils::FakeMetaObject::Ptr fmo);
+ void readEnumValues(AST::UiScriptBinding *ast, LanguageUtils::FakeMetaEnum *fme);
+
+ void addError(const AST::SourceLocation &loc, const QString &message);
+ void addWarning(const AST::SourceLocation &loc, const QString &message);
+
+ QString _fileName;
+ QString _source;
+ QString _errorMessage;
+ QString _warningMessage;
+ QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> *_objects;
+ QList<ModuleApiInfo> *_moduleApis = nullptr;
+ QStringList *_dependencies = nullptr;
+};
+
+} // namespace QQmlJS
+QT_END_NAMESPACE
+
+#endif // QMLJSTYPEDESCRIPTIONREADER_H
diff --git a/tools/qmllint/qmllint.pro b/tools/qmllint/qmllint.pro
index 91ab2f8afc..76363a7cd8 100644
--- a/tools/qmllint/qmllint.pro
+++ b/tools/qmllint/qmllint.pro
@@ -2,8 +2,22 @@ option(host_build)
QT = core qmldevtools-private
-SOURCES += main.cpp
+SOURCES += main.cpp \
+ componentversion.cpp \
+ fakemetaobject.cpp \
+ findunqualified.cpp \
+ qmljstypedescriptionreader.cpp \
+ qcoloroutput.cpp \
+ scopetree.cpp
QMAKE_TARGET_DESCRIPTION = QML Syntax Verifier
load(qt_tool)
+
+HEADERS += \
+ componentversion.h \
+ fakemetaobject.h \
+ findunqualified.h \
+ qmljstypedescriptionreader.h \
+ qcoloroutput_p.h \
+ scopetree.h
diff --git a/tools/qmllint/scopetree.cpp b/tools/qmllint/scopetree.cpp
new file mode 100644
index 0000000000..2eff3fa319
--- /dev/null
+++ b/tools/qmllint/scopetree.cpp
@@ -0,0 +1,269 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE: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 "scopetree.h"
+
+#include "qcoloroutput_p.h"
+
+#include <algorithm>
+
+#include <QQueue>
+
+ScopeTree::ScopeTree(ScopeType type, QString name, ScopeTree *parentScope)
+ : m_parentScope(parentScope), m_name(name), m_scopeType(type) {}
+
+ScopeTree *ScopeTree::createNewChildScope(ScopeType type, QString name) {
+ Q_ASSERT(type != ScopeType::QMLScope|| !m_parentScope || m_parentScope->m_scopeType == ScopeType::QMLScope || m_parentScope->m_name == "global");
+ auto childScope = new ScopeTree{type, name, this};
+ m_childScopes.push_back(childScope);
+ return childScope;
+}
+
+ScopeTree *ScopeTree::parentScope() {
+ return m_parentScope;
+}
+
+void ScopeTree::insertJSIdentifier(QString id, QQmlJS::AST::VariableScope scope)
+{
+ Q_ASSERT(m_scopeType != ScopeType::QMLScope);
+ if (scope == QQmlJS::AST::VariableScope::Var) {
+ auto targetScope = this;
+ while (targetScope->scopeType() != ScopeType::JSFunctionScope) {
+ targetScope = targetScope->m_parentScope;
+ }
+ targetScope->m_currentScopeJSIdentifiers.insert(id);
+ } else {
+ m_currentScopeJSIdentifiers.insert(id);
+ }
+}
+
+void ScopeTree::insertQMLIdentifier(QString id)
+{
+ Q_ASSERT(m_scopeType == ScopeType::QMLScope);
+ m_currentScopeQMLIdentifiers.insert(id);
+}
+
+void ScopeTree::insertSignalIdentifier(QString id, LanguageUtils::FakeMetaMethod method, QQmlJS::AST::SourceLocation loc, bool hasMultilineHandlerBody)
+{
+ Q_ASSERT(m_scopeType == ScopeType::QMLScope);
+ m_injectedSignalIdentifiers.insert(id, {method, loc, hasMultilineHandlerBody});
+}
+
+void ScopeTree::insertPropertyIdentifier(QString id)
+{
+ this->insertQMLIdentifier(id);
+ LanguageUtils::FakeMetaMethod method( id + QLatin1String("Changed"), "void");
+ this->addMethod(method);
+}
+
+bool ScopeTree::isIdInCurrentScope(const QString &id) const
+{
+ return isIdInCurrentQMlScopes(id) || isIdInCurrentJSScopes(id);
+}
+
+void ScopeTree::addIdToAccssedIfNotInParentScopes(const QPair<QString, QQmlJS::AST::SourceLocation> &id_loc_pair, const QSet<QString>& unknownImports) {
+ // also do not add id if it is parent
+ // parent is almost always defined valid in QML, and if we could not find a definition for the current QML component
+ // not skipping "parent" will lead to many false positives
+ // Moreover, if the top level item is Item or inherits from it, it will have a parent property to which we would point the user
+ // which makes for a very nonsensical warning
+ auto qmlScope = getCurrentQMLScope();
+ if (!isIdInCurrentScope(id_loc_pair.first) && !(id_loc_pair.first == QLatin1String("parent") && qmlScope && unknownImports.contains(qmlScope->name()))) {
+ m_accessedIdentifiers.push_back(id_loc_pair);
+ }
+}
+
+bool ScopeTree::isVisualRootScope() const
+{
+ return m_parentScope && m_parentScope->m_parentScope && m_parentScope->m_parentScope->m_parentScope == nullptr;
+}
+
+QString ScopeTree::name() const
+{
+ return m_name;
+}
+
+struct IssueLocationWithContext
+{
+ IssueLocationWithContext(const QString& code, QQmlJS::AST::SourceLocation location) {
+ int before = std::max(0,code.lastIndexOf('\n', location.offset));
+ beforeText = code.midRef(before+1, location.offset - (before+1) );
+ issueText = code.midRef(location.offset, location.length);
+ int after = code.indexOf('\n', location.offset + location.length);
+ afterText = code.midRef(location.offset+location.length, after - (location.offset+location.length));
+ }
+
+ QStringRef beforeText;
+ QStringRef issueText;
+ QStringRef afterText;
+};
+
+bool ScopeTree::recheckIdentifiers(const QString& code, const QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> &qmlIDs, const ScopeTree *root, const QString& rootId, ColorOutput& colorOut) const
+{
+ bool noUnqualifiedIdentifier = true;
+
+ // revisit all scopes
+ QQueue<const ScopeTree*> workQueue;
+ workQueue.enqueue(this);
+ while (!workQueue.empty()) {
+ const ScopeTree* currentScope = workQueue.dequeue();
+ for (auto idLocationPair : currentScope->m_accessedIdentifiers) {
+ if (qmlIDs.contains(idLocationPair.first))
+ continue;
+ if (currentScope->isIdInCurrentScope(idLocationPair.first)) {
+ continue;
+ }
+ noUnqualifiedIdentifier = false;
+ colorOut.write("Warning: ", Warning);
+ auto location = idLocationPair.second;
+ colorOut.write(QString::asprintf("unqualified access at %d:%d\n", location.startLine, location.startColumn), Normal);
+ IssueLocationWithContext issueLocationWithContext {code, location};
+ colorOut.write(issueLocationWithContext.beforeText.toString(), Normal);
+ colorOut.write(issueLocationWithContext.issueText.toString(), Error);
+ colorOut.write(issueLocationWithContext.afterText.toString() + QLatin1Char('\n'), Normal);
+ int tabCount = issueLocationWithContext.beforeText.count(QLatin1Char('\t'));
+ colorOut.write(QString(" ").repeated(issueLocationWithContext.beforeText.length() - tabCount) + QString("\t").repeated(tabCount) + QString("^").repeated(location.length) + QLatin1Char('\n'), Normal);
+ // root(JS) --> program(qml) --> (first element)
+ if (root->m_childScopes[0]->m_childScopes[0]->m_currentScopeQMLIdentifiers.contains(idLocationPair.first)) {
+ ScopeTree *parentScope = currentScope->m_parentScope;
+ while (parentScope && parentScope->scopeType() != ScopeType::QMLScope) {
+ parentScope = parentScope->m_parentScope;
+ }
+ colorOut.write("Note: ", Info);
+ colorOut.write( idLocationPair.first + QLatin1String(" is a meber of the root element\n"), Normal );
+ colorOut.write(QLatin1String(" You can qualify the access with its id to avoid this warning:\n"), Normal);
+ if (rootId == QLatin1String("<id>")) {
+ colorOut.write("Note: ", Warning);
+ colorOut.write(("You first have to give the root element an id\n"));
+ }
+ colorOut.write(issueLocationWithContext.beforeText.toString(), Normal);
+ colorOut.write(rootId + QLatin1Char('.'), Hint);
+ colorOut.write(issueLocationWithContext.issueText.toString(), Normal);
+ colorOut.write(issueLocationWithContext.afterText + QLatin1Char('\n'), Normal);
+ } else if (currentScope->isIdInjectedFromSignal(idLocationPair.first)) {
+ auto qmlScope = currentScope->getCurrentQMLScope();
+ auto methodUsages = qmlScope->m_injectedSignalIdentifiers.values(idLocationPair.first);
+ auto location = idLocationPair.second;
+ // sort the list of signal handlers by their occurrence in the source code
+ // then, we select the first one whose location is after the unqualified id
+ // and go one step backwards to get the one which we actually need
+ std::sort(methodUsages.begin(), methodUsages.end(), [](const MethodUsage m1, const MethodUsage m2) {
+ return m1.loc.startLine < m2.loc.startLine || (m1.loc.startLine == m2.loc.startLine && m1.loc.startColumn < m2.loc.startColumn);
+ });
+ auto oneBehindIt = std::find_if(methodUsages.begin(), methodUsages.end(), [&location](MethodUsage methodUsage) {
+ return location.startLine < methodUsage.loc.startLine || (location.startLine == methodUsage.loc.startLine && location.startColumn < methodUsage.loc.startColumn);
+ });
+ auto methodUsage = *(--oneBehindIt);
+ colorOut.write("Note:", Info);
+ colorOut.write(idLocationPair.first + QString::asprintf(" is accessible in this scope because you are handling a signal at %d:%d\n", methodUsage.loc.startLine, methodUsage.loc.startColumn), Normal);
+ colorOut.write("Consider using a function instead\n", Normal);
+ IssueLocationWithContext context {code, methodUsage.loc};
+ colorOut.write(context.beforeText + QLatin1Char(' '));
+ colorOut.write(methodUsage.hasMultilineHandlerBody ? "function(" : "(", Hint);
+ const auto parameters = methodUsage.method.parameterNames();
+ for (int numParams = parameters.size(); numParams > 0; --numParams) {
+ colorOut.write(parameters.at(parameters.size() - numParams), Hint);
+ if (numParams > 1) {
+ colorOut.write(", ", Hint);
+ }
+ }
+ colorOut.write(methodUsage.hasMultilineHandlerBody ? ")" : ") => ", Hint);
+ colorOut.write(" {...", Normal);
+ }
+ colorOut.write("\n\n\n", Normal);
+ }
+ for (auto const& childScope: currentScope->m_childScopes) {
+ workQueue.enqueue(childScope);
+ }
+ }
+ return noUnqualifiedIdentifier;
+}
+
+QMap<QString, LanguageUtils::FakeMetaMethod>const &ScopeTree::methods() const
+{
+ return m_methods;
+}
+
+bool ScopeTree::isIdInCurrentQMlScopes(QString id) const
+{
+ auto qmlScope = getCurrentQMLScope();
+ return qmlScope->m_currentScopeQMLIdentifiers.contains(id);
+}
+
+bool ScopeTree::isIdInCurrentJSScopes(QString id) const
+{
+ auto jsScope = this;
+ while (jsScope) {
+ if (jsScope->m_scopeType != ScopeType::QMLScope && jsScope->m_currentScopeJSIdentifiers.contains(id))
+ return true;
+ jsScope = jsScope->m_parentScope;
+ }
+ return false;
+}
+
+bool ScopeTree::isIdInjectedFromSignal(QString id) const
+{
+ auto qmlScope = getCurrentQMLScope();
+ return qmlScope->m_injectedSignalIdentifiers.contains(id);
+}
+
+const ScopeTree *ScopeTree::getCurrentQMLScope() const
+{
+ auto qmlScope = this;
+ while (qmlScope && qmlScope->m_scopeType != ScopeType::QMLScope) {
+ qmlScope = qmlScope->m_parentScope;
+ }
+ return qmlScope;
+}
+
+ScopeTree *ScopeTree::getCurrentQMLScope()
+{
+ auto qmlScope = this;
+ while (qmlScope && qmlScope->m_scopeType != ScopeType::QMLScope) {
+ qmlScope = qmlScope->m_parentScope;
+ }
+ return qmlScope;
+}
+
+ScopeType ScopeTree::scopeType() {return m_scopeType;}
+
+void ScopeTree::addMethod(LanguageUtils::FakeMetaMethod method)
+{
+ m_methods.insert(method.methodName(), method);
+}
+
+void ScopeTree::addMethodsFromMetaObject(LanguageUtils::FakeMetaObject::ConstPtr metaObject)
+{
+ if (metaObject) {
+ auto methodCount = metaObject->methodCount();
+ for (auto i = 0; i < methodCount; ++i) {
+ auto method = metaObject->method(i);
+ this->addMethod(method);
+ }
+ }
+}
diff --git a/tools/qmllint/scopetree.h b/tools/qmllint/scopetree.h
new file mode 100644
index 0000000000..872a509123
--- /dev/null
+++ b/tools/qmllint/scopetree.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE: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 SCOPETREE_H
+#define SCOPETREE_H
+
+#include "fakemetaobject.h"
+#include "private/qqmljsast_p.h"
+#include "private/qqmljssourcelocation_p.h"
+
+#include <QSet>
+#include <QString>
+#include <QMap>
+
+enum MessageColors{
+ Error,
+ Warning,
+ Info,
+ Normal,
+ Hint
+};
+
+enum class ScopeType
+{
+ JSFunctionScope,
+ JSLexicalScope,
+ QMLScope
+};
+
+struct MethodUsage
+{
+ LanguageUtils::FakeMetaMethod method;
+ QQmlJS::AST::SourceLocation loc;
+ bool hasMultilineHandlerBody;
+};
+
+class ColorOutput;
+
+class ScopeTree {
+public:
+ ScopeTree(ScopeType type, QString name="<none given>", ScopeTree* parentScope=nullptr);
+ ~ScopeTree() {qDeleteAll(m_childScopes);}
+
+ ScopeTree* createNewChildScope(ScopeType type, QString name);
+ ScopeTree* parentScope();
+
+ void insertJSIdentifier(QString id, QQmlJS::AST::VariableScope scope);
+ void insertQMLIdentifier(QString id);
+ void insertSignalIdentifier(QString id, LanguageUtils::FakeMetaMethod method, QQmlJS::AST::SourceLocation loc, bool hasMultilineHandlerBody);
+ void insertPropertyIdentifier(QString id); // inserts property as qml identifier as well as the corresponding
+
+ bool isIdInCurrentScope(QString const &id) const;
+ void addIdToAccssedIfNotInParentScopes(QPair<QString, QQmlJS::AST::SourceLocation> const& id_loc_pair, const QSet<QString>& unknownImports);
+
+ bool isVisualRootScope() const;
+ QString name() const;
+
+ bool recheckIdentifiers(const QString &code, const QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr>& qmlIDs, const ScopeTree *root, const QString& rootId, ColorOutput &colorOut) const;
+ ScopeType scopeType();
+ void addMethod(LanguageUtils::FakeMetaMethod);
+ void addMethodsFromMetaObject(LanguageUtils::FakeMetaObject::ConstPtr metaObject);
+ QMap<QString, LanguageUtils::FakeMetaMethod>const & methods() const;
+
+private:
+ QSet<QString> m_currentScopeJSIdentifiers;
+ QSet<QString> m_currentScopeQMLIdentifiers;
+ QMultiHash<QString, MethodUsage> m_injectedSignalIdentifiers;
+ QMap<QString, LanguageUtils::FakeMetaMethod> m_methods;
+ QVector<QPair<QString, QQmlJS::AST::SourceLocation>> m_accessedIdentifiers;
+ QVector<ScopeTree*> m_childScopes;
+ ScopeTree *m_parentScope;
+ QString m_name;
+ ScopeType m_scopeType;
+
+ bool isIdInCurrentQMlScopes(QString id) const;
+ bool isIdInCurrentJSScopes(QString id) const;
+ bool isIdInjectedFromSignal(QString id) const;
+ const ScopeTree* getCurrentQMLScope() const;
+ ScopeTree* getCurrentQMLScope();
+};
+#endif // SCOPETREE_H
diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp
index 0a66aa419b..1556718471 100644
--- a/tools/qmlplugindump/main.cpp
+++ b/tools/qmlplugindump/main.cpp
@@ -100,21 +100,51 @@ static QString enquote(const QString &string)
.replace(QLatin1Char('"'),QLatin1String("\\\"")));
}
-void collectReachableMetaObjects(const QMetaObject *meta, QSet<const QMetaObject *> *metas, bool extended = false)
+struct QmlVersionInfo
{
+ QString pluginImportUri;
+ int majorVersion;
+ int minorVersion;
+ bool strict;
+};
+
+static bool matchingImportUri(const QQmlType &ty, const QmlVersionInfo& versionInfo) {
+ if (versionInfo.strict) {
+ return (versionInfo.pluginImportUri == ty.module()
+ && (ty.majorVersion() == versionInfo.majorVersion || ty.majorVersion() == -1))
+ || ty.module().isEmpty();
+ }
+ return ty.module().isEmpty()
+ || versionInfo.pluginImportUri == ty.module()
+ || ty.module().startsWith(versionInfo.pluginImportUri + QLatin1Char('.'));
+}
+
+void collectReachableMetaObjects(const QMetaObject *meta, QSet<const QMetaObject *> *metas, const QmlVersionInfo &info, bool extended = false, bool alreadyChangedModule = false)
+{
+ auto ty = QQmlMetaType::qmlType(meta);
if (! meta || metas->contains(meta))
return;
- // dynamic meta objects can break things badly
- // but extended types are usually fine
- const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate *>(meta->d.data);
- if (extended || !(mop->flags & DynamicMetaObject))
- metas->insert(meta);
+ if (matchingImportUri(ty, info)) {
+ if (!alreadyChangedModule) {
+ // dynamic meta objects can break things badly
+ // but extended types are usually fine
+ const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate *>(meta->d.data);
+ if (extended || !(mop->flags & DynamicMetaObject))
+ metas->insert(meta);
+ } else if (!ty.module().isEmpty()) { // empty module (e.g. from an attached property) would cause a (false) match; do not warn about them
+ qWarning() << "Circular module dependency cannot be expressed in plugin.qmltypes file"
+ << "Object was:" << meta->className()
+ << ty.module() << info.pluginImportUri;
+ }
+ } else if (!ty.module().isEmpty()) {
+ alreadyChangedModule = true;
+ }
- collectReachableMetaObjects(meta->superClass(), metas);
+ collectReachableMetaObjects(meta->superClass(), metas, info, /*extended=*/ false, alreadyChangedModule);
}
-void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *metas)
+void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *metas, const QmlVersionInfo &info)
{
if (! object)
return;
@@ -122,7 +152,7 @@ void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *met
const QMetaObject *meta = object->metaObject();
if (verbose)
std::cerr << "Processing object " << qPrintable( meta->className() ) << std::endl;
- collectReachableMetaObjects(meta, metas);
+ collectReachableMetaObjects(meta, metas, info);
for (int index = 0; index < meta->propertyCount(); ++index) {
QMetaProperty prop = meta->property(index);
@@ -135,17 +165,18 @@ void collectReachableMetaObjects(QObject *object, QSet<const QMetaObject *> *met
// accessing a member of oo is going to cause a segmentation fault
QObject *oo = QQmlMetaType::toQObject(prop.read(object));
if (oo && !metas->contains(oo->metaObject()))
- collectReachableMetaObjects(oo, metas);
+ collectReachableMetaObjects(oo, metas, info);
currentProperty.clear();
}
}
}
-void collectReachableMetaObjects(QQmlEnginePrivate *engine, const QQmlType &ty, QSet<const QMetaObject *> *metas)
+void collectReachableMetaObjects(QQmlEnginePrivate *engine, const QQmlType &ty, QSet<const QMetaObject *> *metas, const QmlVersionInfo& info)
{
- collectReachableMetaObjects(ty.baseMetaObject(), metas, ty.isExtendedType());
- if (ty.attachedPropertiesType(engine))
- collectReachableMetaObjects(ty.attachedPropertiesType(engine), metas);
+ collectReachableMetaObjects(ty.baseMetaObject(), metas, info, ty.isExtendedType());
+ if (ty.attachedPropertiesType(engine) && matchingImportUri(ty, info)) {
+ collectReachableMetaObjects(ty.attachedPropertiesType(engine), metas, info);
+ }
}
/* We want to add the MetaObject for 'Qt' to the list, this is a
@@ -205,14 +236,14 @@ QByteArray convertToId(const QMetaObject *mo)
// Collect all metaobjects for types registered with qmlRegisterType() without parameters
void collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate *engine, QSet<const QMetaObject *>& metas,
- QMap<QString, QSet<QQmlType>> &compositeTypes) {
+ QMap<QString, QList<QQmlType>> &compositeTypes, const QmlVersionInfo &info) {
const auto qmlAllTypes = QQmlMetaType::qmlAllTypes();
for (const QQmlType &ty : qmlAllTypes) {
if (!metas.contains(ty.baseMetaObject())) {
if (!ty.isComposite()) {
- collectReachableMetaObjects(engine, ty, &metas);
- } else {
- compositeTypes[ty.elementName()].insert(ty);
+ collectReachableMetaObjects(engine, ty, &metas, info);
+ } else if (matchingImportUri(ty, info)) {
+ compositeTypes[ty.elementName()].append(ty);
}
}
}
@@ -221,23 +252,27 @@ void collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate *engine, QSet<c
QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine,
QSet<const QMetaObject *> &noncreatables,
QSet<const QMetaObject *> &singletons,
- QMap<QString, QSet<QQmlType>> &compositeTypes,
- const QList<QQmlType> &skip = QList<QQmlType>())
+ QMap<QString, QList<QQmlType>> &compositeTypes,
+ const QmlVersionInfo &info,
+ const QList<QQmlType> &skip = QList<QQmlType>()
+ )
{
QSet<const QMetaObject *> metas;
metas.insert(FriendlyQObject::qtMeta());
const auto qmlTypes = QQmlMetaType::qmlTypes();
for (const QQmlType &ty : qmlTypes) {
+ if (!matchingImportUri(ty,info))
+ continue;
if (!ty.isCreatable())
noncreatables.insert(ty.baseMetaObject());
if (ty.isSingleton())
singletons.insert(ty.baseMetaObject());
if (!ty.isComposite()) {
qmlTypesByCppName[ty.baseMetaObject()->className()].insert(ty);
- collectReachableMetaObjects(QQmlEnginePrivate::get(engine), ty, &metas);
+ collectReachableMetaObjects(QQmlEnginePrivate::get(engine), ty, &metas, info);
} else {
- compositeTypes[ty.elementName()].insert(ty);
+ compositeTypes[ty.elementName()].append(ty);
}
}
@@ -245,6 +280,8 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine,
// find even more QMetaObjects by instantiating QML types and running
// over the instances
for (const QQmlType &ty : qmlTypes) {
+ if (!matchingImportUri(ty, info))
+ continue;
if (skip.contains(ty))
continue;
if (ty.isExtendedType())
@@ -270,13 +307,12 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine,
<< " is singleton, but has no singletonInstanceInfo" << std::endl;
continue;
}
- if (siinfo->qobjectCallback) {
+ if (ty.isQObjectSingleton()) {
if (verbose)
std::cerr << "Trying to get singleton for " << qPrintable(tyName)
<< " (" << qPrintable( siinfo->typeName ) << ")" << std::endl;
- siinfo->init(engine);
- collectReachableMetaObjects(object, &metas);
- object = siinfo->qobjectApi(engine);
+ collectReachableMetaObjects(object, &metas, info);
+ object = QQmlEnginePrivate::get(engine)->singletonInstance<QObject*>(ty);
} else {
inObjectInstantiation.clear();
continue; // we don't handle QJSValue singleton types.
@@ -294,7 +330,7 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine,
if (verbose)
std::cerr << "Got " << qPrintable( tyName )
<< " (" << qPrintable( QString::fromUtf8(ty.typeName()) ) << ")" << std::endl;
- collectReachableMetaObjects(object, &metas);
+ collectReachableMetaObjects(object, &metas, info);
object->deleteLater();
} else {
std::cerr << "Could not create " << qPrintable(tyName) << std::endl;
@@ -302,7 +338,7 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine,
}
}
- collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate::get(engine), metas, compositeTypes);
+ collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate::get(engine), metas, compositeTypes, info);
return metas;
}
@@ -344,23 +380,21 @@ public:
relocatableModuleUri = uri;
}
- const QString getExportString(QString qmlTyName, int majorVersion, int minorVersion)
+ QString getExportString(const QQmlType &type, const QmlVersionInfo &versionInfo)
{
- if (qmlTyName.startsWith(relocatableModuleUri + QLatin1Char('/'))) {
- qmlTyName.remove(0, relocatableModuleUri.size() + 1);
- }
- if (qmlTyName.startsWith("./")) {
- qmlTyName.remove(0, 2);
- }
- if (qmlTyName.startsWith(QLatin1Char('/'))) {
- qmlTyName.remove(0, 1);
- }
- const QString exportString = enquote(
- QString("%1 %2.%3").arg(
- qmlTyName,
- QString::number(majorVersion),
- QString::number(minorVersion)));
- return exportString;
+ const QString module = type.module().isEmpty() ? versionInfo.pluginImportUri
+ : type.module();
+ const int majorVersion = type.majorVersion() >= 0 ? type.majorVersion()
+ : versionInfo.majorVersion;
+ const int minorVersion = type.minorVersion() >= 0 ? type.minorVersion()
+ : versionInfo.minorVersion;
+
+ const QString versionedElement = type.elementName()
+ + QString::fromLatin1(" %1.%2").arg(majorVersion).arg(minorVersion);
+
+ return enquote((module == relocatableModuleUri)
+ ? versionedElement
+ : module + QLatin1Char('/') + versionedElement);
}
void writeMetaContent(const QMetaObject *meta, KnownAttributes *knownAttributes = nullptr)
@@ -405,11 +439,13 @@ public:
}
}
- QString getPrototypeNameForCompositeType(const QMetaObject *metaObject, QSet<QByteArray> &defaultReachableNames,
- QList<const QMetaObject *> *objectsToMerge)
+ QString getPrototypeNameForCompositeType(
+ const QMetaObject *metaObject, QList<const QMetaObject *> *objectsToMerge,
+ const QmlVersionInfo &versionInfo)
{
+ auto ty = QQmlMetaType::qmlType(metaObject);
QString prototypeName;
- if (!defaultReachableNames.contains(metaObject->className())) {
+ if (matchingImportUri(ty, versionInfo)) {
// dynamic meta objects can break things badly
// but extended types are usually fine
const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate *>(metaObject->d.data);
@@ -417,24 +453,28 @@ public:
&& !objectsToMerge->contains(metaObject))
objectsToMerge->append(metaObject);
const QMetaObject *superMetaObject = metaObject->superClass();
- if (!superMetaObject)
+ if (!superMetaObject) {
prototypeName = "QObject";
- else
+ } else {
+ QQmlType superType = QQmlMetaType::qmlType(superMetaObject);
+ if (superType.isValid() && !superType.isComposite())
+ return convertToId(superMetaObject->className());
prototypeName = getPrototypeNameForCompositeType(
- superMetaObject, defaultReachableNames, objectsToMerge);
+ superMetaObject, objectsToMerge, versionInfo);
+ }
} else {
prototypeName = convertToId(metaObject->className());
}
return prototypeName;
}
- void dumpComposite(QQmlEngine *engine, const QSet<QQmlType> &compositeType, QSet<QByteArray> &defaultReachableNames)
+ void dumpComposite(QQmlEngine *engine, const QList<QQmlType> &compositeType, const QmlVersionInfo &versionInfo)
{
for (const QQmlType &type : compositeType)
- dumpCompositeItem(engine, type, defaultReachableNames);
+ dumpCompositeItem(engine, type, versionInfo);
}
- void dumpCompositeItem(QQmlEngine *engine, const QQmlType &compositeType, QSet<QByteArray> &defaultReachableNames)
+ void dumpCompositeItem(QQmlEngine *engine, const QQmlType &compositeType, const QmlVersionInfo &versionInfo)
{
QQmlComponent e(engine, compositeType.sourceUrl());
if (!e.isReady()) {
@@ -455,13 +495,17 @@ public:
QList<const QMetaObject *> objectsToMerge;
KnownAttributes knownAttributes;
// Get C++ base class name for the composite type
- QString prototypeName = getPrototypeNameForCompositeType(mainMeta, defaultReachableNames,
- &objectsToMerge);
+ QString prototypeName = getPrototypeNameForCompositeType(mainMeta, &objectsToMerge,
+ versionInfo);
qml->writeScriptBinding(QLatin1String("prototype"), enquote(prototypeName));
QString qmlTyName = compositeType.qmlTypeName();
- const QString exportString = getExportString(qmlTyName, compositeType.majorVersion(), compositeType.minorVersion());
+ const QString exportString = getExportString(compositeType, versionInfo);
+
+ // TODO: why don't we simply output the compositeType.elementName() here?
+ // That would make more sense, but it would change the format quite a bit.
qml->writeScriptBinding(QLatin1String("name"), exportString);
+
qml->writeArrayBinding(QLatin1String("exports"), QStringList() << exportString);
qml->writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), QStringList() << QString::number(compositeType.minorVersion()));
qml->writeBooleanBinding(QLatin1String("isComposite"), true);
@@ -528,7 +572,7 @@ public:
if (attachedType != meta)
attachedTypeId = convertToId(attachedType);
}
- const QString exportString = getExportString(type.qmlTypeName(), type.majorVersion(), type.minorVersion());
+ const QString exportString = getExportString(type, { QString(), -1, -1, false });
int metaObjectRevision = type.metaObjectRevision();
if (extendedObject) {
// emulate custom metaobjectrevision out of import
@@ -961,6 +1005,16 @@ void printDebugMessage(QtMsgType, const QMessageLogContext &, const QString &msg
// In case of QtFatalMsg the calling code will abort() when appropriate.
}
+QT_BEGIN_NAMESPACE
+static bool operator<(const QQmlType &a, const QQmlType &b)
+{
+ return a.qmlTypeName() < b.qmlTypeName()
+ || (a.qmlTypeName() == b.qmlTypeName()
+ && ((a.majorVersion() < b.majorVersion())
+ || (a.majorVersion() == b.majorVersion()
+ && a.minorVersion() < b.minorVersion())));
+}
+QT_END_NAMESPACE
int main(int argc, char *argv[])
{
@@ -1026,6 +1080,7 @@ int main(int argc, char *argv[])
QString dependenciesFile;
QString mergeFile;
bool forceQtQuickDependency = true;
+ bool strict = false;
enum Action { Uri, Path, Builtins };
Action action = Uri;
{
@@ -1090,6 +1145,10 @@ int main(int argc, char *argv[])
} else if (arg == QLatin1String("--qapp")
|| arg == QLatin1String("-qapp")) {
continue;
+ } else if (arg == QLatin1String("--strict")
+ || arg == QLatin1String("-strict")) {
+ strict = true;
+ continue;
} else {
std::cerr << "Invalid argument: " << qPrintable(arg) << std::endl;
return EXIT_INVALIDARGUMENTS;
@@ -1186,50 +1245,34 @@ int main(int argc, char *argv[])
// find all QMetaObjects reachable from the builtin module
QSet<const QMetaObject *> uncreatableMetas;
QSet<const QMetaObject *> singletonMetas;
- QMap<QString, QSet<QQmlType>> defaultCompositeTypes;
- QSet<const QMetaObject *> defaultReachable = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, defaultCompositeTypes);
- QList<QQmlType> defaultTypes = QQmlMetaType::qmlTypes();
-
- // add some otherwise unreachable QMetaObjects
- defaultReachable.insert(&QQuickMouseEvent::staticMetaObject);
- // QQuickKeyEvent, QQuickPinchEvent, QQuickDropEvent are not exported
- QSet<QByteArray> defaultReachableNames;
// this will hold the meta objects we want to dump information of
QSet<const QMetaObject *> metas;
// composite types we want to dump information of
- QMap<QString, QSet<QQmlType>> compositeTypes;
+ QMap<QString, QList<QQmlType>> compositeTypes;
+ int majorVersion = qtQmlMajorVersion, minorVersion = qtQmlMinorVersion;
+ QmlVersionInfo info;
if (action == Builtins) {
- for (const QMetaObject *m : qAsConst(defaultReachable)) {
- if (m->className() == QLatin1String("Qt")) {
- metas.insert(m);
- break;
- }
- }
- } else if (pluginImportUri == QLatin1String("QtQml")) {
- bool ok = false;
- const uint major = pluginImportVersion.splitRef('.').at(0).toUInt(&ok, 10);
- if (!ok) {
- std::cerr << "Malformed version string \""<< qPrintable(pluginImportVersion) << "\"."
- << std::endl;
- return EXIT_INVALIDARGUMENTS;
- }
- if (major != qtQmlMajorVersion) {
- std::cerr << "Unsupported version \"" << qPrintable(pluginImportVersion)
- << "\": Major version number must be \"" << qtQmlMajorVersion << "\"."
- << std::endl;
- return EXIT_INVALIDARGUMENTS;
- }
- metas = defaultReachable;
- for (const QMetaObject *m : qAsConst(defaultReachable)) {
- if (m->className() == QLatin1String("Qt")) {
- metas.remove(m);
- break;
- }
- }
+ QMap<QString, QList<QQmlType>> defaultCompositeTypes;
+ QSet<const QMetaObject *> builtins = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, defaultCompositeTypes, {QLatin1String("Qt"), majorVersion, minorVersion, strict});
+ Q_ASSERT(builtins.size() == 1);
+ metas.insert(*builtins.begin());
} else {
+ auto versionSplitted = pluginImportVersion.split(".");
+ bool ok = versionSplitted.size() == 2;
+ if (!ok)
+ qCritical("Invalid version number");
+ else {
+ majorVersion = versionSplitted.at(0).toInt(&ok);
+ if (!ok)
+ qCritical("Invalid major version");
+ minorVersion = versionSplitted.at(1).toInt(&ok);
+ if (!ok)
+ qCritical("Invalid minor version");
+ }
+ QList<QQmlType> defaultTypes = QQmlMetaType::qmlTypes();
// find a valid QtQuick import
QByteArray importCode;
QQmlType qtObjectType = QQmlMetaType::qmlType(&QObject::staticMetaObject);
@@ -1273,25 +1316,16 @@ int main(int argc, char *argv[])
return EXIT_IMPORTERROR;
}
}
+ info = {pluginImportUri, majorVersion, minorVersion, strict};
+ QSet<const QMetaObject *> candidates = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, compositeTypes, info, defaultTypes);
- QSet<const QMetaObject *> candidates = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, compositeTypes, defaultTypes);
- candidates.subtract(defaultReachable);
-
- for (QString iter: compositeTypes.keys()) {
- if (defaultCompositeTypes.contains(iter)) {
- QSet<QQmlType> compositeTypesByName = compositeTypes.value(iter);
- compositeTypesByName.subtract(defaultCompositeTypes.value(iter));
- compositeTypes[iter] = compositeTypesByName;
- }
+ for (auto it = compositeTypes.begin(), end = compositeTypes.end(); it != end; ++it) {
+ std::sort(it->begin(), it->end());
+ it->erase(std::unique(it->begin(), it->end()), it->end());
}
- // Also eliminate meta objects with the same classname.
- // This is required because extended objects seem not to share
- // a single meta object instance.
- for (const QMetaObject *mo : qAsConst(defaultReachable))
- defaultReachableNames.insert(QByteArray(mo->className()));
for (const QMetaObject *mo : qAsConst(candidates)) {
- if (!defaultReachableNames.contains(mo->className()))
+ if (mo->className() != QLatin1String("Qt"))
metas.insert(mo);
}
}
@@ -1338,9 +1372,9 @@ int main(int argc, char *argv[])
dumper.dump(QQmlEnginePrivate::get(&engine), meta, uncreatableMetas.contains(meta), singletonMetas.contains(meta));
}
- QMap<QString, QSet<QQmlType> >::const_iterator iter = compositeTypes.constBegin();
+ QMap<QString, QList<QQmlType>>::const_iterator iter = compositeTypes.constBegin();
for (; iter != compositeTypes.constEnd(); ++iter)
- dumper.dumpComposite(&engine, iter.value(), defaultReachableNames);
+ dumper.dumpComposite(&engine, iter.value(), info);
// define QEasingCurve as an extension of QQmlEasingValueType, this way
// properties using the QEasingCurve type get useful type information.
diff --git a/tools/qmlprofiler/qmlprofilerapplication.cpp b/tools/qmlprofiler/qmlprofilerapplication.cpp
index f92ffa9ff5..7b010546c3 100644
--- a/tools/qmlprofiler/qmlprofilerapplication.cpp
+++ b/tools/qmlprofiler/qmlprofilerapplication.cpp
@@ -302,7 +302,7 @@ void QmlProfilerApplication::flush()
{
if (m_recording) {
m_pendingRequest = REQUEST_FLUSH;
- m_qmlProfilerClient->sendRecordingStatus(false);
+ m_qmlProfilerClient->setRecording(false);
} else {
if (m_profilerData->save(m_interactiveOutputFile)) {
m_profilerData->clear();
@@ -392,7 +392,7 @@ void QmlProfilerApplication::userCommand(const QString &command)
if (cmd == Constants::CMD_RECORD || cmd == Constants::CMD_RECORD2) {
m_pendingRequest = REQUEST_TOGGLE_RECORDING;
- m_qmlProfilerClient->sendRecordingStatus(!m_recording);
+ m_qmlProfilerClient->setRecording(!m_recording);
} else if (cmd == Constants::CMD_QUIT || cmd == Constants::CMD_QUIT2) {
m_pendingRequest = REQUEST_QUIT;
if (m_recording) {
diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp
index d5662a0182..9ec143975e 100644
--- a/tools/qmlprofiler/qmlprofilerdata.cpp
+++ b/tools/qmlprofiler/qmlprofilerdata.cpp
@@ -101,7 +101,6 @@ QmlProfilerData::~QmlProfilerData()
void QmlProfilerData::clear()
{
- d->eventTypes.clear();
d->events.clear();
d->traceEndTime = std::numeric_limits<qint64>::min();
diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp
index af50acaa5a..260c5bb7d1 100644
--- a/tools/qmlscene/main.cpp
+++ b/tools/qmlscene/main.cpp
@@ -29,7 +29,7 @@
#include <QtCore/qabstractanimation.h>
#include <QtCore/qdir.h>
#include <QtCore/qmath.h>
-#include <QtCore/qdatetime.h>
+#include <QtCore/qelapsedtimer.h>
#include <QtCore/qpointer.h>
#include <QtCore/qscopedpointer.h>
#include <QtCore/qtextstream.h>
@@ -76,7 +76,7 @@ QVector<int> RenderStatistics::timesPerFrames;
void RenderStatistics::updateStats()
{
- static QTime time;
+ static QElapsedTimer time;
static int frames;
static int lastTime;
@@ -169,10 +169,13 @@ struct Options
bool multisample = false;
bool coreProfile = false;
bool verbose = false;
+ bool rhi = false;
+ bool rhiBackendSet = false;
QVector<Qt::ApplicationAttribute> applicationAttributes;
QString translationFile;
QmlApplicationType applicationType = DefaultQmlApplicationType;
QQuickWindow::TextRenderType textRenderType;
+ QString rhiBackend;
};
#if defined(QMLSCENE_BUNDLE)
@@ -271,6 +274,10 @@ static bool checkVersion(const QUrl &url)
QTextStream stream(&f);
bool codeFound= false;
while (!codeFound) {
+ if (stream.atEnd()) {
+ fprintf(stderr, "qmlscene: no code found in file '%s'.\n", qPrintable(fileName));
+ return false;
+ }
QString line = stream.readLine();
if (line.contains(QLatin1Char('{'))) {
codeFound = true;
@@ -345,24 +352,26 @@ static void usage()
puts(" --transparent .................... Make the window transparent");
puts(" --multisample .................... Enable multisampling (OpenGL anti-aliasing)");
puts(" --core-profile ................... Request a core profile OpenGL context");
+ puts(" --rhi [vulkan|metal|d3d11|gl] .... Use the Qt graphics abstraction (RHI) instead of OpenGL directly.\n"
+ " .... Backend has platform specific defaults. Specify to override.");
puts(" --no-version-detection ........... Do not try to detect the version of the .qml file");
puts(" --slow-animations ................ Run all animations in slow motion");
puts(" --resize-to-root ................. Resize the window to the size of the root item");
puts(" --quit ........................... Quit immediately after starting");
puts(" --disable-context-sharing ........ Disable the use of a shared GL context for QtQuick Windows\n"
- " .........(remove AA_ShareOpenGLContexts)");
- puts(" --desktop..........................Force use of desktop GL (AA_UseDesktopOpenGL)");
- puts(" --gles.............................Force use of GLES (AA_UseOpenGLES)");
- puts(" --software.........................Force use of software rendering (AA_UseOpenGLES)");
- puts(" --scaling..........................Enable High DPI scaling (AA_EnableHighDpiScaling)");
- puts(" --no-scaling.......................Disable High DPI scaling (AA_DisableHighDpiScaling)");
- puts(" --verbose..........................Print version and graphical diagnostics for the run-time");
+ " ........ (remove AA_ShareOpenGLContexts)");
+ puts(" --desktop......................... Force use of desktop GL (AA_UseDesktopOpenGL)");
+ puts(" --gles............................ Force use of GLES (AA_UseOpenGLES)");
+ puts(" --software........................ Force use of software rendering (AA_UseOpenGLES)");
+ puts(" --scaling......................... Enable High DPI scaling (AA_EnableHighDpiScaling)");
+ puts(" --no-scaling...................... Disable High DPI scaling (AA_DisableHighDpiScaling)");
+ puts(" --verbose......................... Print version and graphical diagnostics for the run-time");
#ifdef QT_WIDGETS_LIB
- puts(" --apptype [gui|widgets] ...........Select which application class to use. Default is widgets.");
+ puts(" --apptype [gui|widgets] .......... Select which application class to use. Default is widgets.");
#endif
- puts(" --textrendertype [qt|native].......Select the default render type for text-like elements.");
+ puts(" --textrendertype [qt|native]...... Select the default render type for text-like elements.");
puts(" -I <path> ........................ Add <path> to the list of import paths");
- puts(" -S <selector> .....................Add <selector> to the list of QQmlFileSelector selectors");
+ puts(" -S <selector> .................... Add <selector> to the list of QQmlFileSelector selectors");
puts(" -P <path> ........................ Add <path> to the list of plugin paths");
puts(" -translation <translationfile> ... Set the language to run in");
@@ -546,7 +555,13 @@ int main(int argc, char ** argv)
options.resizeViewToRootItem = true;
else if (lowerArgument == QLatin1String("--verbose"))
options.verbose = true;
- else if (lowerArgument == QLatin1String("-i") && i + 1 < size)
+ else if (lowerArgument == QLatin1String("--rhi")) {
+ options.rhi = true;
+ if (i + 1 < size && !arguments.at(i + 1).startsWith(QLatin1Char('-'))) {
+ options.rhiBackendSet = true;
+ options.rhiBackend = arguments.at(++i);
+ }
+ } else if (lowerArgument == QLatin1String("-i") && i + 1 < size)
imports.append(arguments.at(++i));
else if (lowerArgument == QLatin1String("-s") && i + 1 < size)
customSelectors.append(arguments.at(++i));
@@ -588,6 +603,14 @@ int main(int argc, char ** argv)
QUnifiedTimer::instance()->setSlowModeEnabled(options.slowAnimations);
+ if (options.rhi) {
+ qputenv("QSG_RHI", "1");
+ if (options.rhiBackendSet)
+ qputenv("QSG_RHI_BACKEND", options.rhiBackend.toLatin1());
+ else
+ qunsetenv("QSG_RHI_BACKEND");
+ }
+
if (options.url.isEmpty())
#if defined(QMLSCENE_BUNDLE)
displayOptionsDialog(&options);
@@ -692,6 +715,8 @@ int main(int argc, char ** argv)
// QQuickView if one was created. That case is tracked by
// QPointer, so it is safe to delete the component here.
delete component;
+ } else {
+ exitCode = 1;
}
}
diff --git a/tools/qmltime/qmltime.cpp b/tools/qmltime/qmltime.cpp
index b897d304fc..7baedff611 100644
--- a/tools/qmltime/qmltime.cpp
+++ b/tools/qmltime/qmltime.cpp
@@ -29,7 +29,7 @@
#include <QQmlComponent>
#include <QDebug>
#include <QGuiApplication>
-#include <QTime>
+#include <QElapsedTimer>
#include <QQmlContext>
#include <QQuickView>
#include <QQuickItem>
@@ -119,7 +119,7 @@ void Timer::setWillParent(bool p)
void Timer::runTest(QQmlContext *context, uint iterations)
{
- QTime t;
+ QElapsedTimer t;
t.start();
for (uint ii = 0; ii < iterations; ++ii) {
QObject *o = m_component->create(context);
diff --git a/tools/qmlcachegen/resourcefilemapper.cpp b/tools/shared/resourcefilemapper.cpp
index 6a00b39f2e..b9cf463575 100644
--- a/tools/qmlcachegen/resourcefilemapper.cpp
+++ b/tools/shared/resourcefilemapper.cpp
@@ -50,17 +50,15 @@ bool ResourceFileMapper::isEmpty() const
QStringList ResourceFileMapper::resourcePaths(const QString &fileName)
{
const QString absPath = QDir::cleanPath(QDir::current().absoluteFilePath(fileName));
- QHashIterator<QString, QString> it(qrcPathToFileSystemPath);
QStringList resourcePaths;
- while (it.hasNext()) {
- it.next();
+ for (auto it = qrcPathToFileSystemPath.cbegin(), end = qrcPathToFileSystemPath.cend(); it != end; ++it) {
if (QFileInfo(it.value()) == QFileInfo(absPath))
resourcePaths.append(it.key());
}
return resourcePaths;
}
-QStringList ResourceFileMapper::qmlCompilerFiles() const
+QStringList ResourceFileMapper::qmlCompilerFiles(FileOutput fo) const
{
QStringList files;
for (auto it = qrcPathToFileSystemPath.constBegin(), end = qrcPathToFileSystemPath.constEnd();
@@ -69,7 +67,10 @@ QStringList ResourceFileMapper::qmlCompilerFiles() const
const QString suffix = QFileInfo(qrcPath).suffix();
if (suffix != QStringLiteral("qml") && suffix != QStringLiteral("js") && suffix != QStringLiteral("mjs"))
continue;
- files << qrcPath;
+ if (fo == FileOutput::AbsoluteFilePath)
+ files << it.value();
+ else
+ files << qrcPath;
}
return files;
}
diff --git a/tools/qmlcachegen/resourcefilemapper.h b/tools/shared/resourcefilemapper.h
index 2e0ab45171..ed3e486149 100644
--- a/tools/qmlcachegen/resourcefilemapper.h
+++ b/tools/shared/resourcefilemapper.h
@@ -34,12 +34,16 @@
struct ResourceFileMapper
{
+ enum class FileOutput {
+ RelativeFilePath,
+ AbsoluteFilePath
+ };
ResourceFileMapper(const QStringList &resourceFiles);
bool isEmpty() const;
QStringList resourcePaths(const QString &fileName);
- QStringList qmlCompilerFiles() const;
+ QStringList qmlCompilerFiles(FileOutput fo = FileOutput::RelativeFilePath) const;
private:
void populateFromQrcFile(QFile &file);
diff --git a/tools/shared/shared.pri b/tools/shared/shared.pri
new file mode 100644
index 0000000000..c094b51d5f
--- /dev/null
+++ b/tools/shared/shared.pri
@@ -0,0 +1,3 @@
+INCLUDEPATH += $$PWD
+SOURCES += $$PWD/resourcefilemapper.cpp
+HEADERS += $$PWD/resourcefilemapper.h
diff --git a/tools/tools.pro b/tools/tools.pro
index 73cb6e2293..25ed760903 100644
--- a/tools/tools.pro
+++ b/tools/tools.pro
@@ -10,7 +10,7 @@ qtConfig(qml-devtools) {
qtConfig(commandlineparser):qtConfig(xmlstreamwriter): SUBDIRS += qmlcachegen
}
-qtConfig(thread):!android|android_app:!wasm {
+qtConfig(thread):!android|android_app:!wasm:!rtems {
SUBDIRS += \
qml