aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dist/changes-5.12.2101
-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/LinkBuffer.h8
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerARM64.h8
-rw-r--r--src/3rdparty/masm/wtf/Platform.h1
-rw-r--r--src/3rdparty/masm/yarr/YarrPattern.h1
-rw-r--r--src/imports/layouts/qquickstacklayout.cpp4
-rw-r--r--src/imports/layouts/qquickstacklayout_p.h4
-rw-r--r--src/imports/localstorage/plugin.cpp1
-rw-r--r--src/imports/qtqml/plugin.cpp91
-rw-r--r--src/imports/qtqml/qmldir2
-rw-r--r--src/imports/qtqml/qtqml.pro15
-rw-r--r--src/imports/qtquick2/plugin.cpp9
-rw-r--r--src/imports/statemachine/signaltransition.cpp1
-rw-r--r--src/imports/testlib/main.cpp16
-rw-r--r--src/particles/qquickcustomaffector.cpp23
-rw-r--r--src/particles/qquickcustomaffector_p.h2
-rw-r--r--src/particles/qquickcustomparticle.cpp2
-rw-r--r--src/particles/qquickparticleemitter.cpp7
-rw-r--r--src/particles/qquickparticleemitter_p.h2
-rw-r--r--src/particles/qquickparticlesystem.cpp2
-rw-r--r--src/particles/qquickparticlesystem_p.h3
-rw-r--r--src/particles/qquicktrailemitter.cpp10
-rw-r--r--src/particles/qquicktrailemitter_p.h2
-rw-r--r--src/particles/qquickv4particledata.cpp4
-rw-r--r--src/particles/qquickv4particledata_p.h3
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp1
-rw-r--r--src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp1
-rw-r--r--src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.h1
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp66
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h2
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp146
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.h37
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvghelpers.cpp8
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp413
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h40
-rw-r--r--src/qml/compiler/qqmlpropertycachecreator_p.h42
-rw-r--r--src/qml/compiler/qqmlpropertyvalidator.cpp5
-rw-r--r--src/qml/compiler/qqmltypecompiler.cpp62
-rw-r--r--src/qml/compiler/qv4codegen.cpp465
-rw-r--r--src/qml/compiler/qv4codegen_p.h181
-rw-r--r--src/qml/compiler/qv4compileddata.cpp40
-rw-r--r--src/qml/compiler/qv4compileddata_p.h49
-rw-r--r--src/qml/compiler/qv4compiler.cpp57
-rw-r--r--src/qml/compiler/qv4compiler_p.h2
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp7
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h7
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp54
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h7
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp45
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h22
-rw-r--r--src/qml/doc/snippets/qml/tablemodel/fruit-example-complex.qml134
-rw-r--r--src/qml/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.qml61
-rw-r--r--src/qml/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.qml54
-rw-r--r--src/qml/doc/snippets/qml/tablemodel/roleDataProvider.qml83
-rw-r--r--src/qml/doc/src/cppintegration/definetypes.qdoc7
-rw-r--r--src/qml/doc/src/javascript/functionlist.qdoc61
-rw-r--r--src/qml/doc/src/javascript/hostenvironment.qdoc5
-rw-r--r--src/qml/doc/src/javascript/number.qdoc2
-rw-r--r--src/qml/doc/src/statemachine.qdoc4
-rw-r--r--src/qml/jit/jit.pri13
-rw-r--r--src/qml/jit/qv4baselineassembler.cpp32
-rw-r--r--src/qml/jit/qv4baselinejit.cpp108
-rw-r--r--src/qml/jit/qv4baselinejit_p.h17
-rw-r--r--src/qml/jit/qv4blockscheduler.cpp208
-rw-r--r--src/qml/jit/qv4blockscheduler_p.h155
-rw-r--r--src/qml/jit/qv4domtree.cpp436
-rw-r--r--src/qml/jit/qv4domtree_p.h136
-rw-r--r--src/qml/jit/qv4graphbuilder.cpp72
-rw-r--r--src/qml/jit/qv4graphbuilder_p.h19
-rw-r--r--src/qml/jit/qv4ir.cpp4
-rw-r--r--src/qml/jit/qv4loopinfo.cpp199
-rw-r--r--src/qml/jit/qv4loopinfo_p.h159
-rw-r--r--src/qml/jit/qv4lowering.cpp200
-rw-r--r--src/qml/jit/qv4lowering_p.h107
-rw-r--r--src/qml/jit/qv4mi.cpp251
-rw-r--r--src/qml/jit/qv4mi_p.h627
-rw-r--r--src/qml/jit/qv4miblockset_p.h291
-rw-r--r--src/qml/jit/qv4node_p.h22
-rw-r--r--src/qml/jit/qv4operation.cpp18
-rw-r--r--src/qml/jit/qv4operation_p.h15
-rw-r--r--src/qml/jit/qv4schedulers.cpp912
-rw-r--r--src/qml/jit/qv4schedulers_p.h162
-rw-r--r--src/qml/jit/qv4tracingjit.cpp12
-rw-r--r--src/qml/jsapi/qjsvalue.cpp3
-rw-r--r--src/qml/jsruntime/qv4argumentsobject.cpp58
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp4
-rw-r--r--src/qml/jsruntime/qv4engine.cpp43
-rw-r--r--src/qml/jsruntime/qv4engine_p.h1
-rw-r--r--src/qml/jsruntime/qv4function.cpp1
-rw-r--r--src/qml/jsruntime/qv4function_p.h1
-rw-r--r--src/qml/jsruntime/qv4generatorobject.cpp4
-rw-r--r--src/qml/jsruntime/qv4global_p.h3
-rw-r--r--src/qml/jsruntime/qv4identifiertable.cpp7
-rw-r--r--src/qml/jsruntime/qv4include.cpp13
-rw-r--r--src/qml/jsruntime/qv4internalclass.cpp89
-rw-r--r--src/qml/jsruntime/qv4internalclass_p.h69
-rw-r--r--src/qml/jsruntime/qv4lookup.cpp87
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h35
-rw-r--r--src/qml/jsruntime/qv4memberdata.cpp21
-rw-r--r--src/qml/jsruntime/qv4object.cpp115
-rw-r--r--src/qml/jsruntime/qv4object_p.h8
-rw-r--r--src/qml/jsruntime/qv4propertykey_p.h4
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp422
-rw-r--r--src/qml/jsruntime/qv4qmlcontext_p.h11
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp223
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h55
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp149
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h48
-rw-r--r--src/qml/jsruntime/qv4runtimecodegen_p.h1
-rw-r--r--src/qml/jsruntime/qv4script.cpp1
-rw-r--r--src/qml/jsruntime/qv4serialize.cpp3
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp20
-rw-r--r--src/qml/jsruntime/qv4typedarray.cpp40
-rw-r--r--src/qml/jsruntime/qv4value_p.h16
-rw-r--r--src/qml/jsruntime/qv4variantobject.cpp1
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp103
-rw-r--r--src/qml/jsruntime/qv4vtable_p.h14
-rw-r--r--src/qml/memory/qv4mm.cpp78
-rw-r--r--src/qml/memory/qv4mm_p.h44
-rw-r--r--src/qml/parser/qqmljs.g18
-rw-r--r--src/qml/parser/qqmljsast.cpp19
-rw-r--r--src/qml/parser/qqmljsast_p.h28
-rw-r--r--src/qml/parser/qqmljsastfwd_p.h4
-rw-r--r--src/qml/parser/qqmljsastvisitor.cpp6
-rw-r--r--src/qml/parser/qqmljsastvisitor_p.h40
-rw-r--r--src/qml/parser/qqmljsengine_p.cpp4
-rw-r--r--src/qml/parser/qqmljsengine_p.h4
-rw-r--r--src/qml/parser/qqmljsglobal_p.h4
-rw-r--r--src/qml/parser/qqmljskeywords_p.h4
-rw-r--r--src/qml/parser/qqmljslexer_p.h4
-rw-r--r--src/qml/parser/qqmljsmemorypool_p.h4
-rw-r--r--src/qml/parser/qqmljssourcelocation_p.h4
-rw-r--r--src/qml/qml.pro1
-rw-r--r--src/qml/qml/qml.pri1
-rw-r--r--src/qml/qml/qqml.h5
-rw-r--r--src/qml/qml/qqmlbinding.cpp32
-rw-r--r--src/qml/qml/qqmlboundsignal.cpp2
-rw-r--r--src/qml/qml/qqmlcomponent.cpp2
-rw-r--r--src/qml/qml/qqmldelayedcallqueue.cpp1
-rw-r--r--src/qml/qml/qqmlengine.cpp104
-rw-r--r--src/qml/qml/qqmlengine_p.h7
-rw-r--r--src/qml/qml/qqmlexpression.cpp1
-rw-r--r--src/qml/qml/qqmlglobal.cpp69
-rw-r--r--src/qml/qml/qqmlglobal_p.h4
-rw-r--r--src/qml/qml/qqmlimport.cpp13
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp82
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h21
-rw-r--r--src/qml/qml/qqmllistwrapper.cpp1
-rw-r--r--src/qml/qml/qqmlmetatype.cpp3
-rw-r--r--src/qml/qml/qqmlmetatypedata.cpp31
-rw-r--r--src/qml/qml/qqmlmetatypedata_p.h6
-rw-r--r--src/qml/qml/qqmlopenmetaobject.cpp1
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp99
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h24
-rw-r--r--src/qml/qml/qqmlpropertydata_p.h293
-rw-r--r--src/qml/qml/qqmlpropertyrawdata_p.h341
-rw-r--r--src/qml/qml/qqmltype.cpp43
-rw-r--r--src/qml/qml/qqmltype_p.h1
-rw-r--r--src/qml/qml/qqmltype_p_p.h14
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp62
-rw-r--r--src/qml/qml/qqmltypewrapper_p.h3
-rw-r--r--src/qml/qml/qqmlvaluetype.cpp29
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp153
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h3
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp2
-rw-r--r--src/qml/qml/qqmlvmemetaobject_p.h1
-rw-r--r--src/qml/qml/qqmlxmlhttprequest.cpp6
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp4
-rw-r--r--src/qml/qml/v8/qv8engine_p.h15
-rw-r--r--src/qml/qtqmlglobal_p.h2
-rw-r--r--src/qml/types/qqmldelegatemodel.cpp38
-rw-r--r--src/qml/types/qqmldelegatemodel_p.h7
-rw-r--r--src/qml/types/qqmldelegatemodel_p_p.h4
-rw-r--r--src/qml/types/qqmllistmodel.cpp18
-rw-r--r--src/qml/types/qqmllistmodel_p.h4
-rw-r--r--src/qml/types/qqmllistmodel_p_p.h2
-rw-r--r--src/qml/types/qqmllistmodelworkeragent.cpp4
-rw-r--r--src/qml/types/qqmllistmodelworkeragent_p.h4
-rw-r--r--src/qml/types/qqmlmodelsmodule.cpp41
-rw-r--r--src/qml/types/qqmlmodelsmodule_p.h5
-rw-r--r--src/qml/types/qqmlobjectmodel.cpp4
-rw-r--r--src/qml/types/qqmlobjectmodel_p.h5
-rw-r--r--src/qml/types/qqmltableinstancemodel_p.h2
-rw-r--r--src/qml/types/qqmltablemodel.cpp731
-rw-r--r--src/qml/types/qqmltablemodel_p.h56
-rw-r--r--src/qml/types/qqmltablemodelcolumn.cpp200
-rw-r--r--src/qml/types/qqmltablemodelcolumn_p.h224
-rw-r--r--src/qml/types/qquickworkerscript.cpp12
-rw-r--r--src/qml/types/qquickworkerscript_p.h3
-rw-r--r--src/qml/types/types.pri6
-rw-r--r--src/qml/util/qqmladaptormodel.cpp7
-rw-r--r--src/qmldebug/qqmlenginedebugclient.cpp2
-rw-r--r--src/qmltest/doc/src/qtquicktest-index.qdoc21
-rw-r--r--src/qmltest/quicktest.cpp12
-rw-r--r--src/qmltest/quicktestresult_p.h1
-rw-r--r--src/quick/doc/qtquick.qdocconf1
-rw-r--r--src/quick/doc/src/concepts/statesanimations/states.qdoc2
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc12
-rw-r--r--src/quick/handlers/handlers.pri1
-rw-r--r--src/quick/handlers/qquickdragaxis.cpp3
-rw-r--r--src/quick/handlers/qquickdragaxis_p.h11
-rw-r--r--src/quick/handlers/qquickdraghandler.cpp15
-rw-r--r--src/quick/handlers/qquickdraghandler_p.h2
-rw-r--r--src/quick/handlers/qquickhoverhandler_p.h2
-rw-r--r--src/quick/handlers/qquickmultipointhandler.cpp168
-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.h3
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler_p.h2
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler_p_p.h2
-rw-r--r--src/quick/handlers/qquickpointhandler_p.h2
-rw-r--r--src/quick/handlers/qquicktaphandler_p.h2
-rw-r--r--src/quick/items/context2d/qquickcanvascontext_p.h3
-rw-r--r--src/quick/items/context2d/qquickcanvasitem.cpp7
-rw-r--r--src/quick/items/context2d/qquickcanvasitem_p.h6
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp6
-rw-r--r--src/quick/items/context2d/qquickcontext2d_p.h2
-rw-r--r--src/quick/items/qquickclipnode.cpp3
-rw-r--r--src/quick/items/qquickclipnode_p.h4
-rw-r--r--src/quick/items/qquickdrag.cpp1
-rw-r--r--src/quick/items/qquickdrag_p.h2
-rw-r--r--src/quick/items/qquickgenericshadereffect.cpp4
-rw-r--r--src/quick/items/qquickitem.cpp38
-rw-r--r--src/quick/items/qquickitem_p.h5
-rw-r--r--src/quick/items/qquickopenglshadereffect.cpp4
-rw-r--r--src/quick/items/qquickpathview.cpp4
-rw-r--r--src/quick/items/qquickrectangle_p_p.h2
-rw-r--r--src/quick/items/qquicktableview.cpp251
-rw-r--r--src/quick/items/qquicktableview_p_p.h13
-rw-r--r--src/quick/items/qquicktext.cpp7
-rw-r--r--src/quick/items/qquickwindow.cpp16
-rw-r--r--src/quick/items/qquickwindow.h1
-rw-r--r--src/quick/qtquick2.cpp1
-rw-r--r--src/quick/scenegraph/coreapi/qsgnode.h2
-rw-r--r--src/quick/scenegraph/qsgdefaultcontext.cpp10
-rw-r--r--src/quick/util/qquickglobal.cpp7
-rw-r--r--src/quickwidgets/qquickwidget.cpp31
-rw-r--r--tests/auto/particles/shared/particlestestsshared.h11
-rw-r--r--tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp46
-rw-r--r--tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp14
-rw-r--r--tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp1
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp55
-rw-r--r--tests/auto/qml/qjsvalue/tst_qjsvalue.cpp4
-rw-r--r--tests/auto/qml/qmlcachegen/data/Enums.qml (renamed from tests/auto/qml/qmlcachegen/Enums.qml)0
-rw-r--r--tests/auto/qml/qmlcachegen/data/Retain.qml (renamed from tests/auto/qml/qmlcachegen/Retain.qml)0
-rw-r--r--tests/auto/qml/qmlcachegen/data/componentInItem.qml15
-rw-r--r--tests/auto/qml/qmlcachegen/data/jsimport.qml (renamed from tests/auto/qml/qmlcachegen/jsimport.qml)0
-rw-r--r--tests/auto/qml/qmlcachegen/data/jsmoduleimport.qml (renamed from tests/auto/qml/qmlcachegen/jsmoduleimport.qml)0
-rw-r--r--tests/auto/qml/qmlcachegen/data/library.js (renamed from tests/auto/qml/qmlcachegen/library.js)0
-rw-r--r--tests/auto/qml/qmlcachegen/data/script.js (renamed from tests/auto/qml/qmlcachegen/script.js)0
-rw-r--r--tests/auto/qml/qmlcachegen/data/script.mjs (renamed from tests/auto/qml/qmlcachegen/script.mjs)0
-rw-r--r--tests/auto/qml/qmlcachegen/data/trickypaths.qml (renamed from tests/auto/qml/qmlcachegen/trickypaths.qml)0
-rw-r--r--tests/auto/qml/qmlcachegen/data/umlaut.qml (renamed from tests/auto/qml/qmlcachegen/umlaut.qml)0
-rw-r--r--tests/auto/qml/qmlcachegen/data/versionStyleSuffix-1.2-core-yc.qml (renamed from tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-core-yc.qml)0
-rw-r--r--tests/auto/qml/qmlcachegen/data/versionStyleSuffix-1.2-more.qml (renamed from tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-more.qml)0
-rw-r--r--tests/auto/qml/qmlcachegen/data/versionchecks.qml (renamed from tests/auto/qml/qmlcachegen/versionchecks.qml)0
-rw-r--r--tests/auto/qml/qmlcachegen/data/worker.js (renamed from tests/auto/qml/qmlcachegen/worker.js)0
-rw-r--r--tests/auto/qml/qmlcachegen/data/worker.qml (renamed from tests/auto/qml/qmlcachegen/worker.qml)0
-rw-r--r--tests/auto/qml/qmlcachegen/qmlcachegen.pro32
-rw-r--r--tests/auto/qml/qmlcachegen/retain.qrc2
-rw-r--r--tests/auto/qml/qmlcachegen/trickypaths.qrc6
-rw-r--r--tests/auto/qml/qmlcachegen/trickypaths_umlaut.qrc2
-rw-r--r--tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp86
-rw-r--r--tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp1
-rw-r--r--tests/auto/qml/qmlmin/tst_qmlmin.cpp1
-rw-r--r--tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp1
-rw-r--r--tests/auto/qml/qqmlecmascript/data/signalHandlers.qml2
-rw-r--r--tests/auto/qml/qqmlecmascript/testtypes.h9
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp71
-rw-r--r--tests/auto/qml/qqmlengine/tst_qqmlengine.cpp13
-rw-r--r--tests/auto/qml/qqmlimport/data/interceptQmldir.qml7
-rw-r--r--tests/auto/qml/qqmlimport/data/intercepted/View.qml5
-rw-r--r--tests/auto/qml/qqmlimport/data/intercepted/qmldir2
-rw-r--r--tests/auto/qml/qqmlimport/tst_qqmlimport.cpp28
-rw-r--r--tests/auto/qml/qqmllanguage/data/EdgeObject.qml20
-rw-r--r--tests/auto/qml/qqmllanguage/data/anchorsToParentInPropertyChagnes.qml10
-rw-r--r--tests/auto/qml/qqmllanguage/data/cyclicAlias.errors.txt1
-rw-r--r--tests/auto/qml/qqmllanguage/data/cyclicAlias.qml5
-rw-r--r--tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt2
-rw-r--r--tests/auto/qml/qqmllanguage/data/fuzzed.1.qml2
-rw-r--r--tests/auto/qml/qqmllanguage/data/fuzzed.2.errors.txt2
-rw-r--r--tests/auto/qml/qqmllanguage/data/fuzzed.2.qmlbin0 -> 404 bytes
-rw-r--r--tests/auto/qml/qqmllanguage/data/objectValueTypeProperty.errors.txt2
-rw-r--r--tests/auto/qml/qqmllanguage/data/thisInQmlScope.qml13
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp28
-rw-r--r--tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp4
-rw-r--r--tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp2
-rw-r--r--tests/auto/qml/qqmlmetatype/data/Components/App.qml (renamed from tests/auto/qml/qqmltablemodel/data/builtInRoles.qml)42
-rw-r--r--tests/auto/qml/qqmlmetatype/data/Components/qmldir3
-rw-r--r--tests/auto/qml/qqmlmetatype/data/enumsInRecursiveImport.qml11
-rw-r--r--tests/auto/qml/qqmlmetatype/qqmlmetatype.pro7
-rw-r--r--tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp32
-rw-r--r--tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp2
-rw-r--r--tests/auto/qml/qqmlparser/tst_qqmlparser.cpp15
-rw-r--r--tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp6
-rw-r--r--tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp1
-rw-r--r--tests/auto/qml/qqmltablemodel/data/TestModel.qml21
-rw-r--r--tests/auto/qml/qqmltablemodel/data/common.qml95
-rw-r--r--tests/auto/qml/qqmltablemodel/data/complex.qml (renamed from tests/auto/qml/qqmltablemodel/data/roleDataProvider.qml)59
-rw-r--r--tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml19
-rw-r--r--tests/auto/qml/qqmltablemodel/data/empty.qml36
-rw-r--r--tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml (renamed from tests/auto/qml/qqmltablemodel/data/explicitDisplayRole.qml)20
-rw-r--r--tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml25
-rw-r--r--tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml48
-rw-r--r--tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp713
-rw-r--r--tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp23
-rw-r--r--tests/auto/qml/qquickfolderlistmodel/BLACKLIST3
-rw-r--r--tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp28
-rw-r--r--tests/auto/qml/qv4assembler/tst_qv4assembler.cpp23
-rw-r--r--tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp4
-rw-r--r--tests/auto/qml/qv4mm/tst_qv4mm.cpp40
-rw-r--r--tests/auto/qmltest/animatedimage/BLACKLIST2
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragMargin.qml36
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp33
-rw-r--r--tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp2
-rw-r--r--tests/auto/quick/qquickanchors/tst_qquickanchors.cpp4
-rw-r--r--tests/auto/quick/qquickgridview/tst_qquickgridview.cpp2
-rw-r--r--tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp13
-rw-r--r--tests/auto/quick/qquicklistview/BLACKLIST5
-rw-r--r--tests/auto/quick/qquicklistview/data/delegateWithMouseArea.qml29
-rw-r--r--tests/auto/quick/qquicklistview/tst_qquicklistview.cpp34
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/BLACKLIST6
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp10
-rw-r--r--tests/auto/quick/qquickpathview/data/pathview5.qml65
-rw-r--r--tests/auto/quick/qquickpathview/tst_qquickpathview.cpp23
-rw-r--r--tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp12
-rw-r--r--tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp10
-rw-r--r--tests/auto/quick/qquicktableview/tst_qquicktableview.cpp12
-rw-r--r--tests/auto/quick/qquickwindow/tst_qquickwindow.cpp18
-rw-r--r--tests/auto/quick/touchmouse/BLACKLIST4
-rw-r--r--tests/auto/quicktest/quicktestmainwithsetup/data/tst_setup.qml6
-rw-r--r--tests/auto/quicktest/quicktestmainwithsetup/imports/ImportPathQmlModule/ImportPathQmlType.qml3
-rw-r--r--tests/auto/quicktest/quicktestmainwithsetup/imports/ImportPathQmlModule/qmldir2
-rw-r--r--tests/auto/quicktest/quicktestmainwithsetup/tst_quicktestmainwithsetup.cpp14
-rw-r--r--tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp4
-rw-r--r--tests/manual/scenegraph_lancelot/data/text/text_wrap_elide_maxlines.qml133
-rw-r--r--tests/manual/tableview/tablemodel/form/RowForm.qml36
-rw-r--r--tests/manual/tableview/tablemodel/form/main.qml60
-rw-r--r--tools/qmlcachegen/qmlcachegen.cpp28
-rw-r--r--tools/qmlimportscanner/main.cpp21
-rw-r--r--tools/qmlscene/main.cpp31
-rw-r--r--tools/tools.pro2
361 files changed, 13252 insertions, 4543 deletions
diff --git a/dist/changes-5.12.2 b/dist/changes-5.12.2
new file mode 100644
index 0000000000..b092aed80d
--- /dev/null
+++ b/dist/changes-5.12.2
@@ -0,0 +1,101 @@
+Qt 5.12.2 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.12.0 through 5.12.1.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.12 series is binary compatible with the 5.11.x series.
+Applications compiled for 5.11 will continue to run with 5.12.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* QtQml *
+****************************************************************************
+
+ - Important Behavior Changes:
+ * The parameters passed to C++ functions from QML are now checked for
+ compatibility with the expected arguments. If they cannot be
+ converted, a warning is printed. In future versions of Qt a type error
+ will be thrown in JavaScript and the function will not be invoked.
+
+ * [QTBUG-73239] Removed revisions from the new Qt.labs.settings methods
+ and properties that were introduced in 5.12. Qt.labs plugins are
+ intended to always have revision 1.0 until they graduate.
+
+ - QQmlApplicationEngine:
+ * [QTBUG-73649] QQmlApplicationEngine connects quit() and exit() signals
+ with queued connections to avoid problems with AutoConnection, when
+ connecting to QCoreApplication slots.
+
+ - [QTBUG-69340] QML cache files are not unnecessarily re-generated.
+ - [QTBUG-71325] Fixed a crash in V4 string to number conversion on 32-bit
+ platforms.
+ - [QTBUG-72137] Fixed a crash in QML garbage collector when accessing
+ other items from Component.onDestruction.
+ - [QTBUG-72352] QML can be built with -no-feature-translation.
+ - [QTBUG-72407] We now annotate stack traces when frames are elided
+ through tail calls.
+ - [QTBUG-72734] Fixed a crash in the parser on certain kinds of bad input.
+ - [QTBUG-72858] Exception handlers are correctly scoped for try blocks and
+ for "for ... in" loops.
+ - [QTBUG-72908] QML can be built with -c++std=c++11 again.
+ - [QTBUG-72972] QQmlMetaType deletes attached properties in its destructor
+ to avoid a crash.
+ - [QTBUG-73009] Fixed a crash with qt.qml.binding.removal.info=true.
+ - [QTBUG-73013] If a signal sender is deleted during the handling of the
+ signal in QML, the QML engine won't crash anymore.
+ - [QTBUG-73152] Brought behavior of String.replace() in line with other
+ JS engines: "x".replace("x", "$1") gives "$1" in both JSC and V8, as there
+ are no captures that could be used as a replacement for $1. Two-digit
+ captures ($nm) get applied if $nm captures exist. If there are less than nm
+ but more than n captures available $n is replaced by the n'th capture and m
+ is copied over verbatim.
+ - [QTBUG-73425] Fixed allocation of large arrays at startup.
+ - [QTBUG-73733] Fixed an access-after-delete crash in DelegateModel.
+ - [QTBUG-73734] When a Q_GADGET type, marked as a primitive type with
+ Q_DECLARE_METATYPE, is emitted with a signal, it can now be accessed in QML.
+ - [QTBUG-73750] Fixed undefined Q_ENUM value in QML Connections object.
+ - [QTBUG-73821] Fixed a failing assert on 32bit platforms.
+ - The JIT compiler is disabled for the IPL32 (or X32) ABI. It did not work
+ before.
+ - The tail call optimization correctly counts method arguments on 32bit
+ platforms now.
+ - The JavaScript engine now tolerates UINT_MAX as array index.
+
+****************************************************************************
+* QtQuick *
+****************************************************************************
+
+ - TextInput/security:
+ * When the TextInput is used for password input, preallocate a buffer
+ for the string that stores the entered value and zero-out the string
+ on TextInput destruction to avoid leaking sensitive data to process
+ memory
+
+ - [QTBUG-63271] If a MouseArea sets itself invisible or disabled while
+ handling a mouse press, it does not acquire the exclusive grab
+ - [QTBUG-71887] TapHandler now consistently forgets touchpoints that occur
+ outside its parent's bounds. This eliminates the warning "pointId is
+ missing from current event, but was neither canceled nor released" when
+ tapping quickly and having some of the taps fall out of bounds.
+ (The warning still exists though, in case there are other scenarios where
+ Handlers remember "wanting" certain touchpoints and then they go missing.)
+ - [QTBUG-72822] PinchHandler now correctly holds its target in place when
+ axes are disabled.
+ - [QTBUG-71918] PointerHandlers are declared as direct children of
+ Flickable (ListView, TableView etc.) now get the pointer events properly.
+ - [QTBUG-42155] Canvas now handles switching between object and string
+ based colors
+ - [QTBUG-73113] Fixed a crash when reparenting QML Canvas items
+ - [QTBUG-73013] Fixed a crash in QuickView on setSource while deleting
+ the sender.
+ - [QT3DS-1419] Improved quality of Qt 3D Studio text labels.
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/LinkBuffer.h b/src/3rdparty/masm/assembler/LinkBuffer.h
index 4dfd051797..a1bb046d43 100644
--- a/src/3rdparty/masm/assembler/LinkBuffer.h
+++ b/src/3rdparty/masm/assembler/LinkBuffer.h
@@ -245,7 +245,7 @@ protected:
inline void linkCode(void* ownerUID, JITCompilationEffort);
- inline void performFinalization();
+ virtual void performFinalization();
#if DUMP_LINK_STATISTICS
static void dumpLinkStatistics(void* code, size_t initialSize, size_t finalSize);
@@ -344,7 +344,7 @@ inline void LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::linkCode
}
template <typename MacroAssembler, template <typename T> class ExecutableOffsetCalculator>
-inline void LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::performFinalization()
+void LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::performFinalization()
{
// NOTE: This function is specialized in LinkBuffer<MacroAssemblerARMv7>
#ifndef NDEBUG
@@ -395,7 +395,7 @@ public:
linkCode(ownerUID, effort);
}
- inline void performFinalization();
+ virtual void performFinalization() override final;
inline void makeExecutable();
inline void linkCode(void* ownerUID, JITCompilationEffort);
@@ -420,7 +420,7 @@ private:
};
template <typename MacroAssembler>
-inline void BranchCompactingLinkBuffer<MacroAssembler>::performFinalization()
+void BranchCompactingLinkBuffer<MacroAssembler>::performFinalization()
{
#ifndef NDEBUG
ASSERT(!m_completed);
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARM64.h b/src/3rdparty/masm/assembler/MacroAssemblerARM64.h
index e5a704292d..3e6dfcf635 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerARM64.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerARM64.h
@@ -1126,6 +1126,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);
diff --git a/src/3rdparty/masm/wtf/Platform.h b/src/3rdparty/masm/wtf/Platform.h
index d5f69927db..bd66af7fe6 100644
--- a/src/3rdparty/masm/wtf/Platform.h
+++ b/src/3rdparty/masm/wtf/Platform.h
@@ -1051,6 +1051,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/YarrPattern.h b/src/3rdparty/masm/yarr/YarrPattern.h
index 1417ff1549..10ea2c5b94 100644
--- a/src/3rdparty/masm/yarr/YarrPattern.h
+++ b/src/3rdparty/masm/yarr/YarrPattern.h
@@ -31,6 +31,7 @@
#include "YarrUnicodeProperties.h"
#include <wtf/CheckedArithmetic.h>
#include <wtf/HashMap.h>
+#include <wtf/Optional.h>
#include <wtf/PrintStream.h>
#include <wtf/Vector.h>
#include <wtf/text/WTFString.h>
diff --git a/src/imports/layouts/qquickstacklayout.cpp b/src/imports/layouts/qquickstacklayout.cpp
index 0b51d79bef..116e162aa9 100644
--- a/src/imports/layouts/qquickstacklayout.cpp
+++ b/src/imports/layouts/qquickstacklayout.cpp
@@ -97,6 +97,8 @@
\sa StackView
*/
+QT_BEGIN_NAMESPACE
+
QQuickStackLayout::QQuickStackLayout(QQuickItem *parent) :
QQuickLayout(*new QQuickStackLayoutPrivate, parent)
{
@@ -345,4 +347,6 @@ bool QQuickStackLayout::shouldIgnoreItem(QQuickItem *item) const
return ignored;
}
+QT_END_NAMESPACE
+
#include "moc_qquickstacklayout_p.cpp"
diff --git a/src/imports/layouts/qquickstacklayout_p.h b/src/imports/layouts/qquickstacklayout_p.h
index 8ba41720aa..46181c6f50 100644
--- a/src/imports/layouts/qquickstacklayout_p.h
+++ b/src/imports/layouts/qquickstacklayout_p.h
@@ -42,6 +42,8 @@
#include <qquicklayout_p.h>
+QT_BEGIN_NAMESPACE
+
class QQuickStackLayoutPrivate;
class QQuickStackLayout : public QQuickLayout
@@ -105,4 +107,6 @@ private:
bool explicitCurrentIndex;
};
+QT_END_NAMESPACE
+
#endif // QQUICKSTACKLAYOUT_H
diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp
index 3c34d8e45a..0aad0d7c16 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>
diff --git a/src/imports/qtqml/plugin.cpp b/src/imports/qtqml/plugin.cpp
new file mode 100644
index 0000000000..eb8c0ffc2f
--- /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 <QtQml/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/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..d5bb313d0c 100644
--- a/src/imports/qtqml/qtqml.pro
+++ b/src/imports/qtqml/qtqml.pro
@@ -1,12 +1,11 @@
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)
+QT = qml-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..4dc6fee916 100644
--- a/src/imports/qtquick2/plugin.cpp
+++ b/src/imports/qtquick2/plugin.cpp
@@ -38,6 +38,8 @@
****************************************************************************/
#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/private/qqmlengine_p.h>
+#include <QtQml/private/qqmlmodelsmodule_p.h>
#include <private/qtquick2_p.h>
@@ -55,7 +57,14 @@ 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();
+#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/statemachine/signaltransition.cpp b/src/imports/statemachine/signaltransition.cpp
index d4ea25cc4b..52dffe9004 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>
diff --git a/src/imports/testlib/main.cpp b/src/imports/testlib/main.cpp
index c625c87db7..7b931c25d2 100644
--- a/src/imports/testlib/main.cpp
+++ b/src/imports/testlib/main.cpp
@@ -80,7 +80,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 +97,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
{
diff --git a/src/particles/qquickcustomaffector.cpp b/src/particles/qquickcustomaffector.cpp
index ccb00eeba2..cadd2992b1 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)
diff --git a/src/particles/qquickcustomaffector_p.h b/src/particles/qquickcustomaffector_p.h
index c1745798c3..10db70d71d 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);
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/qquickparticleemitter.cpp b/src/particles/qquickparticleemitter.cpp
index 5ec834a463..4e36ad149a 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
@@ -257,7 +258,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
@@ -496,7 +497,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/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..81cdb0e6da 100644
--- a/src/particles/qquickparticlesystem_p.h
+++ b/src/particles/qquickparticlesystem_p.h
@@ -59,7 +59,6 @@
#include <private/qquicksprite_p.h>
#include <QAbstractAnimation>
#include <QtQml/qqml.h>
-#include <private/qv8engine_p.h> //For QQmlV4Handle
#include <private/qv4util_p.h>
#include "qtquickparticlesglobal_p.h"
@@ -333,7 +332,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..383444a808 100644
--- a/src/particles/qquickv4particledata.cpp
+++ b/src/particles/qquickv4particledata.cpp
@@ -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/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_nativedebugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
index 43a48e9d0d..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>
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/qqmlpreviewhandler.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp
index 5bd96af582..5d2684b510 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp
@@ -106,16 +106,9 @@ static void closeAllWindows()
bool QQmlPreviewHandler::eventFilter(QObject *obj, QEvent *event)
{
- if (event->type() == QEvent::Show) {
- if (QWindow *window = qobject_cast<QQuickWindow*>(obj)) {
- m_lastPosition.initLastSavedWindowPosition(window);
- }
- }
- if (m_currentWindow && (event->type() == QEvent::Move || event->type() == QEvent::Resize) &&
+ if (m_currentWindow && (event->type() == QEvent::Move) &&
qobject_cast<QQuickWindow*>(obj) == m_currentWindow) {
- // we always start with factor 1 so calculate and save the origin as it would be not scaled
- m_lastPosition.setPosition(m_currentWindow->framePosition() *
- QHighDpiScaling::factor(m_currentWindow));
+ m_lastPosition.takePosition(m_currentWindow);
}
return QObject::eventFilter(obj, event);
@@ -196,49 +189,38 @@ void QQmlPreviewHandler::rerun()
void QQmlPreviewHandler::zoom(qreal newFactor)
{
+ m_zoomFactor = newFactor;
+ QTimer::singleShot(0, this, &QQmlPreviewHandler::doZoom);
+}
+
+void QQmlPreviewHandler::doZoom()
+{
if (!m_currentWindow)
return;
- if (qFuzzyIsNull(newFactor)) {
+ if (qFuzzyIsNull(m_zoomFactor)) {
emit error(QString::fromLatin1("Zooming with factor: %1 will result in nothing " \
- "so it will be ignored.").arg(newFactor));
+ "so it will be ignored.").arg(m_zoomFactor));
return;
}
- QString errorMessage;
- bool resetZoom = false;
- if (newFactor < 0) {
+ bool resetZoom = false;
+ if (m_zoomFactor < 0) {
resetZoom = true;
- newFactor = 1.0;
+ m_zoomFactor = 1.0;
}
- // On single-window devices we allow any scale factor as the window will adapt to the screen.
- if (m_supportsMultipleWindows) {
- const QSize newAvailableScreenSize = QQmlPreviewPosition::currentScreenSize(m_currentWindow)
- * QHighDpiScaling::factor(m_currentWindow) / newFactor;
- if (m_currentWindow->size().width() > newAvailableScreenSize.width()) {
- errorMessage = QString::fromLatin1(
- "Zooming with factor: "
- "%1 will result in a too wide preview.").arg(newFactor);
- }
- if (m_currentWindow->size().height() > newAvailableScreenSize.height()) {
- errorMessage = QString::fromLatin1(
- "Zooming with factor: "
- "%1 will result in a too heigh preview.").arg(newFactor);
- }
- }
+ m_currentWindow->setGeometry(m_currentWindow->geometry());
- if (errorMessage.isEmpty()) {
- const QPoint newToOriginMappedPosition = m_currentWindow->position() *
- QHighDpiScaling::factor(m_currentWindow) / newFactor;
- m_currentWindow->destroy();
- QHighDpiScaling::setScreenFactor(m_currentWindow->screen(), newFactor);
- if (resetZoom)
- QHighDpiScaling::updateHighDpiScaling();
- m_currentWindow->setPosition(newToOriginMappedPosition);
- m_currentWindow->show();
- } else {
- emit error(errorMessage);
- }
+ m_lastPosition.takePosition(m_currentWindow, QQmlPreviewPosition::InitializePosition);
+ m_currentWindow->destroy();
+
+ for (QScreen *screen : QGuiApplication::screens())
+ QHighDpiScaling::setScreenFactor(screen, m_zoomFactor);
+ if (resetZoom)
+ QHighDpiScaling::updateHighDpiScaling();
+
+ m_currentWindow->show();
+ m_lastPosition.initLastSavedWindowPosition(m_currentWindow);
}
void QQmlPreviewHandler::removeTranslators()
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h
index 21ea672580..47491b9d8f 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h
@@ -104,6 +104,7 @@ signals:
protected:
bool eventFilter(QObject *obj, QEvent *event);
private:
+ void doZoom();
void tryCreateObject();
void showObject(QObject *object);
void setCurrentWindow(QQuickWindow *window);
@@ -121,6 +122,7 @@ private:
QVector<QPointer<QObject>> m_createdObjects;
QScopedPointer<QQmlComponent> m_component;
QPointer<QQuickWindow> m_currentWindow;
+ qreal m_zoomFactor = 1.0;
bool m_supportsMultipleWindows;
QQmlPreviewPosition m_lastPosition;
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp
index 3edcbac0a9..d4acd24da5 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp
@@ -42,14 +42,47 @@
#include <QtGui/qwindow.h>
#include <QtGui/qscreen.h>
#include <QtGui/qguiapplication.h>
+#include <private/qhighdpiscaling_p.h>
QT_BEGIN_NAMESPACE
-static const QSize availableScreenSize(const QPoint &point)
+static QVector<QQmlPreviewPosition::ScreenData> initScreensData()
{
- if (const QScreen *screen = QGuiApplication::screenAt(point))
- return screen->availableGeometry().size();
- return QSize();
+ QVector<QQmlPreviewPosition::ScreenData> screensData;
+
+ for (QScreen *screen : QGuiApplication::screens()) {
+ QQmlPreviewPosition::ScreenData sd{screen->name(), screen->size()};
+ screensData.append(sd);
+ }
+ return screensData;
+}
+
+static QScreen *findScreen(const QString &nameOfScreen)
+{
+ for (QScreen *screen : QGuiApplication::screens()) {
+ if (screen->name() == nameOfScreen)
+ return screen;
+ }
+ return nullptr;
+}
+
+static QDataStream &operator<<(QDataStream &out, const QQmlPreviewPosition::ScreenData &screenData)
+{
+ out << screenData.name;
+ out << screenData.size;
+ return out;
+}
+
+static QDataStream &operator>>(QDataStream &in, QQmlPreviewPosition::ScreenData &screenData)
+{
+ in >> screenData.name;
+ in >> screenData.size;
+ return in;
+}
+
+bool QQmlPreviewPosition::ScreenData::operator==(const QQmlPreviewPosition::ScreenData &other) const
+{
+ return other.size == size && other.name == name;
}
QQmlPreviewPosition::QQmlPreviewPosition()
@@ -62,20 +95,36 @@ QQmlPreviewPosition::QQmlPreviewPosition()
});
}
-void QQmlPreviewPosition::setPosition(const QPoint &point)
+QQmlPreviewPosition::~QQmlPreviewPosition()
+{
+ saveWindowPosition();
+}
+
+void QQmlPreviewPosition::takePosition(QWindow *window, InitializeState state)
{
- m_hasPosition = true;
- m_lastWindowPosition = point;
- m_savePositionTimer.start();
+ Q_ASSERT(window);
+ // only save the position if we already tried to get the last saved position
+ if (m_initializeState == PositionInitialized) {
+ m_hasPosition = true;
+ auto screen = window->screen();
+ auto nativePosition = QHighDpiScaling::mapPositionToNative(window->framePosition(),
+ screen->handle());
+ m_lastWindowPosition = {screen->name(), nativePosition};
+
+ m_savePositionTimer.start();
+ }
+ if (state == InitializePosition)
+ m_initializeState = InitializePosition;
}
void QQmlPreviewPosition::saveWindowPosition()
{
if (m_hasPosition) {
+ const QByteArray positionAsByteArray = fromPositionToByteArray(m_lastWindowPosition);
if (!m_settingsKey.isNull())
- m_settings.setValue(m_settingsKey, m_lastWindowPosition);
+ m_settings.setValue(m_settingsKey, positionAsByteArray);
- m_settings.setValue(QLatin1String("global_lastpostion"), m_lastWindowPosition);
+ m_settings.setValue(QLatin1String("global_lastpostion"), positionAsByteArray);
}
}
@@ -85,29 +134,86 @@ void QQmlPreviewPosition::loadWindowPositionSettings(const QUrl &url)
if (m_settings.contains(m_settingsKey)) {
m_hasPosition = true;
- m_lastWindowPosition = m_settings.value(m_settingsKey).toPoint();
+ readLastPositionFromByteArray(m_settings.value(m_settingsKey).toByteArray());
}
}
void QQmlPreviewPosition::initLastSavedWindowPosition(QWindow *window)
{
- if (m_positionedWindows.contains(window))
- return;
+ Q_ASSERT(window);
+ m_initializeState = PositionInitialized;
+ if (m_currentInitScreensData.isEmpty())
+ m_currentInitScreensData = initScreensData();
+ // if it is the first time we just use the fall back from a last shown qml file
if (!m_hasPosition) {
- // in case there was nothing saved, we do not want to set anything
if (!m_settings.contains(QLatin1String("global_lastpostion")))
return;
- m_lastWindowPosition = m_settings.value(QLatin1String("global_lastpostion")).toPoint();
+ readLastPositionFromByteArray(m_settings.value(QLatin1String("global_lastpostion"))
+ .toByteArray());
}
- if (QGuiApplication::screenAt(m_lastWindowPosition))
- window->setFramePosition(m_lastWindowPosition);
+ setPosition(m_lastWindowPosition, window);
+}
+
+QByteArray QQmlPreviewPosition::fromPositionToByteArray(
+ const QQmlPreviewPosition::Position &position)
+{
+ QByteArray array;
+ QDataStream stream(&array, QIODevice::WriteOnly);
+ stream.setVersion(QDataStream::Qt_5_12);
+
+ const quint16 majorVersion = 1;
+ const quint16 minorVersion = 0;
+
+ stream << majorVersion
+ << minorVersion
+ << m_currentInitScreensData
+ << position.screenName
+ << position.nativePosition;
+ return array;
+}
+
+void QQmlPreviewPosition::readLastPositionFromByteArray(const QByteArray &array)
+{
+ QDataStream stream(array);
+ stream.setVersion(QDataStream::Qt_5_12);
+
+ // no version check for 1.0
+ //const quint16 currentMajorVersion = 1;
+ quint16 majorVersion = 0;
+ quint16 minorVersion = 0;
- m_positionedWindows.append(window);
+ stream >> majorVersion >> minorVersion;
+
+ QVector<ScreenData> initScreensData;
+ stream >> initScreensData;
+
+ if (m_currentInitScreensData != initScreensData)
+ return;
+
+ QString nameOfScreen;
+ stream >> nameOfScreen;
+
+ QScreen *screen = findScreen(nameOfScreen);
+ if (!screen)
+ return;
+
+ QPoint nativePosition;
+ stream >> nativePosition;
+ if (nativePosition.isNull())
+ return;
+ m_lastWindowPosition = {nameOfScreen, nativePosition};
}
-const QSize QQmlPreviewPosition::currentScreenSize(QWindow *window)
+void QQmlPreviewPosition::setPosition(const QQmlPreviewPosition::Position &position,
+ QWindow *window)
{
- return availableScreenSize(window->position());
+ if (position.nativePosition.isNull())
+ return;
+ if (QScreen *screen = findScreen(position.screenName)) {
+ window->setScreen(screen);
+ window->setFramePosition(QHighDpiScaling::mapPositionFromNative(position.nativePosition,
+ screen->handle()));
+ }
}
QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.h
index 3d4ca9dc67..f403917f8c 100644
--- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.h
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.h
@@ -56,6 +56,8 @@
#include <QtCore/qurl.h>
#include <QtCore/qtimer.h>
#include <QtCore/qsettings.h>
+#include <QtCore/qsize.h>
+#include <QtCore/qdatastream.h>
QT_BEGIN_NAMESPACE
@@ -64,23 +66,46 @@ class QWindow;
class QQmlPreviewPosition
{
public:
+ class ScreenData {
+ public:
+ bool operator==(const QQmlPreviewPosition::ScreenData &other) const;
+ QString name;
+ QSize size;
+ };
+ class Position {
+ public:
+ QString screenName;
+ QPoint nativePosition;
+ };
+ enum InitializeState {
+ InitializePosition,
+ PositionInitialized
+ };
+
QQmlPreviewPosition();
+ ~QQmlPreviewPosition();
- void setPosition(const QPoint &point);
- void saveWindowPosition();
- void loadWindowPositionSettings(const QUrl &url);
+
+ void takePosition(QWindow *window, InitializeState state = PositionInitialized);
void initLastSavedWindowPosition(QWindow *window);
- static const QSize currentScreenSize(QWindow *window);
+ void loadWindowPositionSettings(const QUrl &url);
private:
+ void setPosition(const QQmlPreviewPosition::Position &position, QWindow *window);
+ QByteArray fromPositionToByteArray(const Position &position);
+ void readLastPositionFromByteArray(const QByteArray &array);
+ void saveWindowPosition();
+
bool m_hasPosition = false;
- QPoint m_lastWindowPosition;
+ InitializeState m_initializeState = InitializePosition;
QSettings m_settings;
QString m_settingsKey;
QTimer m_savePositionTimer;
+ Position m_lastWindowPosition;
QVector<QWindow *> m_positionedWindows;
-};
+ QVector<ScreenData> m_currentInitScreensData;
+};
QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/openvg/qsgopenvghelpers.cpp b/src/plugins/scenegraph/openvg/qsgopenvghelpers.cpp
index 6bc99d32a1..ab5cfb48b8 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvghelpers.cpp
+++ b/src/plugins/scenegraph/openvg/qsgopenvghelpers.cpp
@@ -220,10 +220,10 @@ void qDrawTiled(VGImage image, const QSize imageSize, const QRectF &targetRect,
void qDrawBorderImage(VGImage image, const QSizeF &textureSize, const QRectF &targetRect, const QRectF &innerTargetRect, const QRectF &subSourceRect)
{
// Create normalized margins
- QMarginsF margins(qMax(innerTargetRect.left() - targetRect.left(), 0.0),
- qMax(innerTargetRect.top() - targetRect.top(), 0.0),
- qMax(targetRect.right() - innerTargetRect.right(), 0.0),
- qMax(targetRect.bottom() - innerTargetRect.bottom(), 0.0));
+ QMarginsF margins(qMax(innerTargetRect.left() - targetRect.left(), qreal(0.0)),
+ qMax(innerTargetRect.top() - targetRect.top(), qreal(0.0)),
+ qMax(targetRect.right() - innerTargetRect.right(), qreal(0.0)),
+ qMax(targetRect.bottom() - innerTargetRect.bottom(), qreal(0.0)));
QRectF sourceRect(0, 0, textureSize.width(), textureSize.height());
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index ea5efcfc66..6e077ec44c 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -566,7 +566,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiQualifiedId *id)
void IRBuilder::accept(QQmlJS::AST::Node *node)
{
- QQmlJS::AST::Node::acceptChild(node, this);
+ QQmlJS::AST::Node::accept(node, this);
}
bool IRBuilder::defineQMLObject(int *objectIndex, QQmlJS::AST::UiQualifiedId *qualifiedTypeNameId, const QQmlJS::AST::SourceLocation &location, QQmlJS::AST::UiObjectInitializer *initializer, Object *declarationsOverride)
@@ -974,7 +974,6 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node)
foe->node = funDecl;
foe->parentNode = funDecl;
foe->nameIndex = registerString(funDecl->name.toString());
- foe->disableAcceleratedLookups = false;
const int index = _object->functionsAndExpressions->append(foe);
Function *f = New<Function>();
@@ -1098,7 +1097,6 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST
expr->parentNode = parentNode;
expr->nameIndex = registerString(QLatin1String("expression for ")
+ stringAt(binding->propertyNameIndex));
- expr->disableAcceleratedLookups = false;
const int index = bindingsTarget()->functionsAndExpressions->append(expr);
binding->value.compiledScriptIndex = index;
// We don't need to store the binding script as string, except for script strings
@@ -1113,6 +1111,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg
QV4::CompiledData::TranslationData translationData;
translationData.number = -1;
translationData.commentIndex = 0; // empty string
+ translationData.padding = 0;
if (!args || !args->expression)
return; // no arguments, stop
@@ -1153,6 +1152,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg
QV4::CompiledData::TranslationData translationData;
translationData.number = -1;
translationData.commentIndex = 0; // empty string, but unused
+ translationData.padding = 0;
if (!args || !args->expression)
return; // no arguments, stop
@@ -1825,19 +1825,13 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding
JSCodeGen::JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
QV4::Compiler::Module *jsModule, QQmlJS::Engine *jsEngine,
- QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports,
+ 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)
- , imports(imports)
, stringPool(stringPool)
- , _disableAcceleratedLookups(false)
- , _contextObject(nullptr)
- , _scopeObject(nullptr)
- , _qmlContextSlot(-1)
- , _importedScriptsSlot(-1)
{
m_globalNames = globalNames;
@@ -1845,18 +1839,6 @@ JSCodeGen::JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *
_fileNameIsUrl = true;
}
-void JSCodeGen::beginContextScope(const JSCodeGen::ObjectIdMapping &objectIds, QQmlPropertyCache *contextObject)
-{
- _idObjects = objectIds;
- _contextObject = contextObject;
- _scopeObject = nullptr;
-}
-
-void JSCodeGen::beginObjectScope(QQmlPropertyCache *scopeObject)
-{
- _scopeObject = scopeObject;
-}
-
QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions)
{
auto qmlName = [&](const CompiledFunctionOrExpression &c) {
@@ -1921,7 +1903,6 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
body = body->finish();
}
- _disableAcceleratedLookups = qmlFunction.disableAcceleratedLookups;
int idx = defineFunction(name, function ? function : qmlFunction.parentNode,
function ? function->formals : nullptr,
body);
@@ -1931,391 +1912,6 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
return runtimeFunctionIndices;
}
-int JSCodeGen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::StatementList *body)
-{
- int qmlContextTemp = -1;
- int importedScriptsTemp = -1;
- qSwap(_qmlContextSlot, qmlContextTemp);
- qSwap(_importedScriptsSlot, importedScriptsTemp);
-
- int result = Codegen::defineFunction(name, ast, formals, body);
-
- qSwap(_importedScriptsSlot, importedScriptsTemp);
- qSwap(_qmlContextSlot, qmlContextTemp);
-
- return result;
-}
-
-#ifndef V4_BOOTSTRAP
-QQmlPropertyData *JSCodeGen::lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name)
-{
- QQmlPropertyData *pd = cache->property(name, /*object*/nullptr, /*context*/nullptr);
-
- if (pd && !cache->isAllowedInRevision(pd))
- return nullptr;
-
- return pd;
-}
-
-enum MetaObjectResolverFlags {
- AllPropertiesAreFinal = 0x1,
- LookupsIncludeEnums = 0x2,
- LookupsExcludeProperties = 0x4,
- ResolveTypeInformationOnly = 0x8
-};
-
-#if 0
-static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject);
-
-static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType, int index);
-
-static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine,
- const QV4::IR::MemberExpressionResolver *resolver,
- QV4::IR::Member *member)
-{
- QV4::IR::Type result = QV4::IR::VarType;
-
- QQmlType type = resolver->qmlType;
-
- if (member->name->constData()->isUpper()) {
- bool ok = false;
- int value = type.enumValue(qmlEngine, *member->name, &ok);
- if (ok) {
- member->setEnumValue(value);
- return QV4::IR::SInt32Type;
- } else {
- int index = type.scopedEnumIndex(qmlEngine, *member->name, &ok);
- if (ok) {
- auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
- newResolver->owner = resolver->owner;
- initScopedEnumResolver(newResolver, type, index);
- return QV4::IR::DiscoveredType(newResolver);
- }
- }
- }
-
- if (type.isCompositeSingleton()) {
- QQmlRefPointer<QQmlTypeData> tdata = qmlEngine->typeLoader.getType(type.singletonInstanceInfo()->url);
- Q_ASSERT(tdata);
- tdata->release(); // Decrease the reference count added from QQmlTypeLoader::getType()
- // When a singleton tries to reference itself, it may not be complete yet.
- if (tdata->isComplete()) {
- auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
- newResolver->owner = resolver->owner;
- initMetaObjectResolver(newResolver, qmlEngine->propertyCacheForType(tdata->compilationUnit()->metaTypeId));
- newResolver->flags |= AllPropertiesAreFinal;
- return newResolver->resolveMember(qmlEngine, newResolver, member);
- }
- } else if (type.isSingleton()) {
- const QMetaObject *singletonMeta = type.singletonInstanceInfo()->instanceMetaObject;
- if (singletonMeta) { // QJSValue-based singletons cannot be accelerated
- auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
- newResolver->owner = resolver->owner;
- initMetaObjectResolver(newResolver, qmlEngine->cache(singletonMeta));
- member->kind = QV4::IR::Member::MemberOfSingletonObject;
- return newResolver->resolveMember(qmlEngine, newResolver, member);
- }
- }
-#if 0
- else if (const QMetaObject *attachedMeta = type->attachedPropertiesType(qmlEngine)) {
- // Right now the attached property IDs are not stable and cannot be embedded in the
- // code that is cached on disk.
- QQmlPropertyCache *cache = qmlEngine->cache(attachedMeta);
- auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
- newResolver->owner = resolver->owner;
- initMetaObjectResolver(newResolver, cache);
- member->setAttachedPropertiesId(type->attachedPropertiesId(qmlEngine));
- return newResolver->resolveMember(qmlEngine, newResolver, member);
- }
-#endif
-
- return result;
-}
-
-static void initQmlTypeResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType)
-{
- Q_ASSERT(resolver);
-
- resolver->resolveMember = &resolveQmlType;
- resolver->qmlType = qmlType;
- resolver->typenameCache = 0;
- resolver->flags = 0;
-}
-
-static QV4::IR::DiscoveredType resolveImportNamespace(
- QQmlEnginePrivate *, const QV4::IR::MemberExpressionResolver *resolver,
- QV4::IR::Member *member)
-{
- QV4::IR::Type result = QV4::IR::VarType;
- QQmlTypeNameCache *typeNamespace = resolver->typenameCache;
- const QQmlImportRef *importNamespace = resolver->import;
-
- QQmlTypeNameCache::Result r = typeNamespace->query(*member->name, importNamespace);
- if (r.isValid()) {
- member->freeOfSideEffects = true;
- if (r.scriptIndex != -1) {
- // TODO: remember the index and replace with subscript later.
- result = QV4::IR::VarType;
- } else if (r.type.isValid()) {
- // TODO: Propagate singleton information, so that it is loaded
- // through the singleton getter in the run-time. Until then we
- // can't accelerate access :(
- if (!r.type.isSingleton()) {
- auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
- newResolver->owner = resolver->owner;
- initQmlTypeResolver(newResolver, r.type);
- return QV4::IR::DiscoveredType(newResolver);
- }
- } else {
- Q_ASSERT(false); // How can this happen?
- }
- }
-
- return result;
-}
-
-static void initImportNamespaceResolver(QV4::IR::MemberExpressionResolver *resolver,
- QQmlTypeNameCache *imports, const QQmlImportRef *importNamespace)
-{
- resolver->resolveMember = &resolveImportNamespace;
- resolver->import = importNamespace;
- resolver->typenameCache = imports;
- resolver->flags = 0;
-}
-
-static QV4::IR::DiscoveredType resolveMetaObjectProperty(
- QQmlEnginePrivate *qmlEngine, const QV4::IR::MemberExpressionResolver *resolver,
- QV4::IR::Member *member)
-{
- QV4::IR::Type result = QV4::IR::VarType;
- QQmlPropertyCache *metaObject = resolver->propertyCache;
-
- if (member->name->constData()->isUpper() && (resolver->flags & LookupsIncludeEnums)) {
- const QMetaObject *mo = metaObject->createMetaObject();
- QByteArray enumName = member->name->toUtf8();
- for (int ii = mo->enumeratorCount() - 1; ii >= 0; --ii) {
- QMetaEnum metaEnum = mo->enumerator(ii);
- bool ok;
- int value = metaEnum.keyToValue(enumName.constData(), &ok);
- if (ok) {
- member->setEnumValue(value);
- return QV4::IR::SInt32Type;
- }
- }
- }
-
- if (member->kind != QV4::IR::Member::MemberOfIdObjectsArray && member->kind != QV4::IR::Member::MemberOfSingletonObject &&
- qmlEngine && !(resolver->flags & LookupsExcludeProperties)) {
- QQmlPropertyData *property = member->property;
- if (!property && metaObject) {
- if (QQmlPropertyData *candidate = metaObject->property(*member->name, /*object*/0, /*context*/0)) {
- const bool isFinalProperty = (candidate->isFinal() || (resolver->flags & AllPropertiesAreFinal))
- && !candidate->isFunction();
-
- if (lookupHints()
- && !(resolver->flags & AllPropertiesAreFinal)
- && !candidate->isFinal()
- && !candidate->isFunction()
- && candidate->isDirect()) {
- qWarning() << "Hint: Access to property" << *member->name << "of" << metaObject->className() << "could be accelerated if it was marked as FINAL";
- }
-
- if (isFinalProperty && metaObject->isAllowedInRevision(candidate)) {
- property = candidate;
- member->inhibitTypeConversionOnWrite = true;
- if (!(resolver->flags & ResolveTypeInformationOnly))
- member->property = candidate; // Cache for next iteration and isel needs it.
- }
- }
- }
-
- if (property) {
- // Enums cannot be mapped to IR types, they need to go through the run-time handling
- // of accepting strings that will then be converted to the right values.
- if (property->isEnum())
- return QV4::IR::VarType;
-
- switch (property->propType()) {
- case QMetaType::Bool: result = QV4::IR::BoolType; break;
- case QMetaType::Int: result = QV4::IR::SInt32Type; break;
- case QMetaType::Double: result = QV4::IR::DoubleType; break;
- case QMetaType::QString: result = QV4::IR::StringType; break;
- default:
- if (property->isQObject()) {
- if (QQmlPropertyCache *cache = qmlEngine->propertyCacheForType(property->propType())) {
- auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
- newResolver->owner = resolver->owner;
- initMetaObjectResolver(newResolver, cache);
- return QV4::IR::DiscoveredType(newResolver);
- }
- } else if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType())) {
- if (QQmlPropertyCache *cache = qmlEngine->cache(valueTypeMetaObject)) {
- auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
- newResolver->owner = resolver->owner;
- initMetaObjectResolver(newResolver, cache);
- newResolver->flags |= ResolveTypeInformationOnly;
- return QV4::IR::DiscoveredType(newResolver);
- }
- }
- break;
- }
- }
- }
-
- return result;
-}
-
-static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject)
-{
- Q_ASSERT(resolver);
-
- resolver->resolveMember = &resolveMetaObjectProperty;
- resolver->propertyCache = metaObject;
- resolver->flags = 0;
-}
-
-static QV4::IR::DiscoveredType resolveScopedEnum(QQmlEnginePrivate *qmlEngine,
- const QV4::IR::MemberExpressionResolver *resolver,
- QV4::IR::Member *member)
-{
- if (!member->name->constData()->isUpper())
- return QV4::IR::VarType;
-
- QQmlType type = resolver->qmlType;
- int index = resolver->flags;
-
- bool ok = false;
- int value = type.scopedEnumValue(qmlEngine, index, *member->name, &ok);
- if (!ok)
- return QV4::IR::VarType;
- member->setEnumValue(value);
- return QV4::IR::SInt32Type;
-}
-
-static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType, int index)
-{
- Q_ASSERT(resolver);
-
- resolver->resolveMember = &resolveScopedEnum;
- resolver->qmlType = qmlType;
- resolver->flags = index;
-}
-#endif
-
-#endif // V4_BOOTSTRAP
-
-void JSCodeGen::beginFunctionBodyHook()
-{
- _qmlContextSlot = bytecodeGenerator->newRegister();
- _importedScriptsSlot = bytecodeGenerator->newRegister();
-
-#ifndef V4_BOOTSTRAP
- Instruction::LoadQmlContext load;
- load.result = Reference::fromStackSlot(this, _qmlContextSlot).stackSlot();
- bytecodeGenerator->addInstruction(load);
-
-#if 0
- temp->type = QV4::IR::QObjectType;
- temp->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>();
- initMetaObjectResolver(temp->memberResolver, _scopeObject);
- auto name = _block->NAME(QV4::IR::Name::builtin_qml_context, 0, 0);
- name->type = temp->type;
-#endif
-
- Instruction::LoadQmlImportedScripts loadScripts;
- loadScripts.result = Reference::fromStackSlot(this, _importedScriptsSlot).stackSlot();
- bytecodeGenerator->addInstruction(loadScripts);
-#endif
-}
-
-QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &name)
-{
-#ifndef V4_BOOTSTRAP
- if (_disableAcceleratedLookups)
- return Reference();
-
- // Implement QML lookup semantics in the current file context.
- //
- // Note: We do not check if properties of the qml scope object or context object
- // are final. That's because QML tries to get as close as possible to lexical scoping,
- // which means in terms of properties that only those visible at compile time are chosen.
- // I.e. access to a "foo" property declared within the same QML component as "property int foo"
- // will always access that instance and as integer. If a sub-type implements its own property string foo,
- // then that one is not chosen for accesses from within this file, because it wasn't visible at compile
- // time. This corresponds to the logic in QQmlPropertyCache::findProperty to find the property associated
- // with the correct QML context.
-
- // Look for IDs first.
- for (const IdMapping &mapping : qAsConst(_idObjects)) {
- if (name == mapping.name) {
- if (_context->contextType == QV4::Compiler::ContextType::Binding)
- _context->idObjectDependencies.insert(mapping.idIndex);
-
- Instruction::LoadIdObject load;
- load.base = Reference::fromStackSlot(this, _qmlContextSlot).stackSlot();
- load.index = mapping.idIndex;
-
- Reference result = Reference::fromAccumulator(this);
- bytecodeGenerator->addInstruction(load);
- result.isReadonly = true;
- return result;
- }
- }
-
- if (name.at(0).isUpper()) {
- QQmlTypeNameCache::Result r = imports->query(name);
- if (r.isValid()) {
- if (r.scriptIndex != -1) {
- Reference imports = Reference::fromStackSlot(this, _importedScriptsSlot);
- return Reference::fromSubscript(imports, Reference::fromConst(this, QV4::Encode(r.scriptIndex)));
- } else if (r.type.isValid()) {
- return Reference::fromName(this, name);
- } else {
- Q_ASSERT(r.importNamespace);
- return Reference::fromName(this, name);
- }
- }
- }
-
- if (_scopeObject) {
- QQmlPropertyData *data = lookupQmlCompliantProperty(_scopeObject, name);
- if (data) {
- // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time
- if (data->isFunction())
- return Reference::fromName(this, name);
-
- Reference base = Reference::fromStackSlot(this, _qmlContextSlot);
- Reference::PropertyCapturePolicy capturePolicy;
- if (!data->isConstant() && !data->isQmlBinding())
- capturePolicy = Reference::CaptureAtRuntime;
- else
- capturePolicy = data->isConstant() ? Reference::DontCapture : Reference::CaptureAheadOfTime;
- return Reference::fromQmlScopeObject(base, data->coreIndex(), data->notifyIndex(), capturePolicy);
- }
- }
-
- if (_contextObject) {
- QQmlPropertyData *data = lookupQmlCompliantProperty(_contextObject, name);
- if (data) {
- // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time
- if (data->isFunction())
- return Reference::fromName(this, name);
-
- Reference base = Reference::fromStackSlot(this, _qmlContextSlot);
- Reference::PropertyCapturePolicy capturePolicy;
- if (!data->isConstant() && !data->isQmlBinding())
- capturePolicy = Reference::CaptureAtRuntime;
- else
- capturePolicy = data->isConstant() ? Reference::DontCapture : Reference::CaptureAheadOfTime;
- return Reference::fromQmlContextObject(base, data->coreIndex(), data->notifyIndex(), capturePolicy);
- }
- }
-#else
- Q_UNUSED(name)
-#endif // V4_BOOTSTRAP
- return Reference();
-}
-
#ifndef V4_BOOTSTRAP
QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, RevisionCheck check) const
@@ -2435,7 +2031,6 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO
b->value.compiledScriptIndex = functionIndices.count() - 1;
QmlIR::CompiledFunctionOrExpression *foe = pool->New<QmlIR::CompiledFunctionOrExpression>();
- foe->disableAcceleratedLookups = true;
foe->nameIndex = 0;
QQmlJS::AST::ExpressionNode *expr;
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index 8512b22fbd..298fe7dd92 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -278,7 +278,6 @@ struct Q_QML_PRIVATE_EXPORT CompiledFunctionOrExpression
QQmlJS::AST::Node *parentNode = nullptr; // FunctionDeclaration, Statement or Expression
QQmlJS::AST::Node *node = nullptr; // FunctionDeclaration, Statement or Expression
quint32 nameIndex = 0;
- bool disableAcceleratedLookups = false;
CompiledFunctionOrExpression *next = nullptr;
};
@@ -431,6 +430,12 @@ public:
bool visit(QQmlJS::AST::UiScriptBinding *ast) override;
bool visit(QQmlJS::AST::UiSourceElement *ast) override;
+ void throwRecursionDepthError() override
+ {
+ recordError(AST::SourceLocation(),
+ QStringLiteral("Maximum statement or expression depth exceeded"));
+ }
+
void accept(QQmlJS::AST::Node *node);
// returns index in _objects
@@ -533,47 +538,16 @@ struct Q_QML_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,
- QQmlTypeNameCache *imports, const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames);
-
- struct IdMapping
- {
- QString name;
- int idIndex;
- QQmlPropertyCache *type;
- };
- typedef QVector<IdMapping> ObjectIdMapping;
-
- void beginContextScope(const ObjectIdMapping &objectIds, QQmlPropertyCache *contextObject);
- void beginObjectScope(QQmlPropertyCache *scopeObject);
+ const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames);
// Returns mapping from input functions to index in IR::Module::functions / compiledData->runtimeFunctions
QVector<int> generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions);
- int defineFunction(const QString &name, AST::Node *ast,
- AST::FormalParameterList *formals,
- AST::StatementList *body) override;
-
-protected:
- void beginFunctionBodyHook() override;
- bool canAccelerateGlobalLookups() const override { return !_disableAcceleratedLookups; }
- Reference fallbackNameLookup(const QString &name) override;
-
private:
- // returns nullptr if lookup needs to happen by name
- QQmlPropertyData *lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name);
-
QString sourceCode;
QQmlJS::Engine *jsEngine; // needed for memory pool
QQmlJS::AST::UiProgram *qmlRoot;
- QQmlTypeNameCache *imports;
const QV4::Compiler::StringTableGenerator *stringPool;
-
- bool _disableAcceleratedLookups;
- ObjectIdMapping _idObjects;
- QQmlPropertyCache *_contextObject;
- QQmlPropertyCache *_scopeObject;
- int _qmlContextSlot;
- int _importedScriptsSlot;
};
struct Q_QML_PRIVATE_EXPORT IRLoader {
diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h
index 7d416561bb..21d653af55 100644
--- a/src/qml/compiler/qqmlpropertycachecreator_p.h
+++ b/src/qml/compiler/qqmlpropertycachecreator_p.h
@@ -578,7 +578,7 @@ public:
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);
+ QQmlCompileError propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyData::Flags *propertyFlags);
void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const;
@@ -693,11 +693,6 @@ inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::property
const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion,
QQmlPropertyData::Flags *propertyFlags)
{
- const int targetObjectIndex = objectForId(component, alias.targetObjectId);
- Q_ASSERT(targetObjectIndex >= 0);
-
- const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex);
-
*type = 0;
bool writable = false;
bool resettable = false;
@@ -705,11 +700,36 @@ inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::property
propertyFlags->isAlias = true;
if (alias.aliasToLocalAlias) {
- auto targetAlias = targetObject.aliasesBegin();
- for (uint i = 0; i < alias.localAliasIndex; ++i)
- ++targetAlias;
- return propertyDataForAlias(component, *targetAlias, type, minorVersion, propertyFlags);
- } else if (alias.encodedMetaPropertyIndex == -1) {
+ const QV4::CompiledData::Alias *lastAlias = &alias;
+ QVarLengthArray<const QV4::CompiledData::Alias *, 4> seenAliases({lastAlias});
+
+ do {
+ const CompiledObject *targetObject = objectContainer->objectAt(
+ objectForId(component, lastAlias->targetObjectId));
+ Q_ASSERT(targetObject);
+
+ auto nextAlias = targetObject->aliasesBegin();
+ for (uint i = 0; i < lastAlias->localAliasIndex; ++i)
+ ++nextAlias;
+
+ const QV4::CompiledData::Alias *targetAlias = &(*nextAlias);
+ if (seenAliases.contains(targetAlias)) {
+ return QQmlCompileError(targetAlias->location,
+ QQmlPropertyCacheCreatorBase::tr("Cyclic alias"));
+ }
+
+ seenAliases.append(targetAlias);
+ lastAlias = targetAlias;
+ } while (lastAlias->aliasToLocalAlias);
+
+ return propertyDataForAlias(component, *lastAlias, type, minorVersion, propertyFlags);
+ }
+
+ const int targetObjectIndex = objectForId(component, alias.targetObjectId);
+ Q_ASSERT(targetObjectIndex >= 0);
+ const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex);
+
+ if (alias.encodedMetaPropertyIndex == -1) {
Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject);
auto *typeRef = objectContainer->resolvedType(targetObject.inheritedTypeNameIndex);
if (!typeRef) {
diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp
index 4714f505a7..8c06760d42 100644
--- a/src/qml/compiler/qqmlpropertyvalidator.cpp
+++ b/src/qml/compiler/qqmlpropertyvalidator.cpp
@@ -693,7 +693,10 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
} else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) {
return noError;
} else if (QQmlValueTypeFactory::isValueType(property->propType())) {
- return QQmlCompileError(binding->location, tr("Unexpected object assignment for property \"%1\"").arg(propertyName));
+ 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 {
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp
index 70b048d737..239f04a58f 100644
--- a/src/qml/compiler/qqmltypecompiler.cpp
+++ b/src/qml/compiler/qqmltypecompiler.cpp
@@ -44,7 +44,6 @@
#include <private/qqmlcustomparser_p.h>
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlcomponent_p.h>
-#include <private/qqmldelegatecomponent_p.h>
#define COMPILE_EXCEPTION(token, desc) \
{ \
@@ -146,8 +145,7 @@ 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, typeNameCache.data(), &document->jsGenerator.stringTable, engine->v8engine()->illegalNames());
- v4CodeGenerator.setUseFastLookups(false);
+ document->program, &document->jsGenerator.stringTable, engine->v8engine()->illegalNames());
QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator);
if (!jsCodeGen.generateCodeForComponents())
return nullptr;
@@ -767,10 +765,6 @@ void QQmlScriptStringScanner::scan()
if (!pd || pd->propType() != scriptStringMetaType)
continue;
- QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex);
- if (foe)
- foe->disableAcceleratedLookups = true;
-
QString script = compiler->bindingAsString(obj, binding->value.compiledScriptIndex);
binding->stringIndex = compiler->registerString(script);
}
@@ -786,6 +780,23 @@ 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);
@@ -807,15 +818,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)) {
@@ -1324,24 +1329,6 @@ bool QQmlJSCodeGenerator::compileComponent(int contextObject)
contextObject = componentBinding->value.objectIndex;
}
- QmlIR::JSCodeGen::ObjectIdMapping idMapping;
- idMapping.reserve(obj->namedObjectsInComponent.size());
- for (int i = 0; i < obj->namedObjectsInComponent.size(); ++i) {
- const int objectIndex = obj->namedObjectsInComponent.at(i);
- QmlIR::JSCodeGen::IdMapping m;
- const QmlIR::Object *obj = qmlObjects.at(objectIndex);
- m.name = stringAt(obj->idNameIndex);
- m.idIndex = obj->id;
- m.type = propertyCaches->at(objectIndex);
-
- auto *tref = resolvedType(obj->inheritedTypeNameIndex);
- if (tref && tref->isFullyDynamicType)
- m.type = nullptr;
-
- idMapping << m;
- }
- v4CodeGen->beginContextScope(idMapping, propertyCaches->at(contextObject));
-
if (!compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject))
return false;
@@ -1355,16 +1342,9 @@ bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIn
return true;
if (object->functionsAndExpressions->count > 0) {
- QQmlPropertyCache *scopeObject = propertyCaches->at(scopeObjectIndex);
- v4CodeGen->beginObjectScope(scopeObject);
-
QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
- for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next) {
- const bool haveCustomParser = customParsers.contains(object->inheritedTypeNameIndex);
- if (haveCustomParser)
- foe->disableAcceleratedLookups = true;
+ 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()) {
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 448fbff27b..88d3dbe9c5 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -100,9 +100,10 @@ Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict)
, hasError(false)
{
jsUnitGenerator->codeGeneratorName = QStringLiteral("moth");
+ pushExpr();
}
-const char *globalNames[] = {
+const char *Codegen::s_globalNames[] = {
"isNaN",
"parseFloat",
"String",
@@ -182,7 +183,7 @@ void Codegen::generateFromProgram(const QString &fileName,
//
// Since this can be called from the loader thread we can't get the list
// directly from the engine, so let's hardcode the most important ones here
- for (const char **g = globalNames; *g != nullptr; ++g)
+ for (const char **g = s_globalNames; *g != nullptr; ++g)
m_globalNames << QString::fromLatin1(*g);
}
@@ -264,7 +265,7 @@ Context *Codegen::enterBlock(Node *node)
Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
{
if (hasError)
- return _expr.result();
+ return exprResult();
if (expr.isConstant()) {
auto v = Value::fromReturnedValue(expr.constant);
@@ -293,8 +294,8 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
}
case UPlus: {
expr.loadInAccumulator();
- Instruction::UPlus uplus;
- bytecodeGenerator->addInstruction(uplus);
+ Instruction::UPlus uplus = {};
+ bytecodeGenerator->addTracingInstruction(uplus);
return Reference::fromAccumulator(this);
}
case Not: {
@@ -310,11 +311,11 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
return Reference::fromAccumulator(this);
}
case PostIncrement:
- if (!_expr.accept(nx) || requiresReturnValue) {
+ if (!exprAccept(nx) || requiresReturnValue) {
Reference e = expr.asLValue();
e.loadInAccumulator();
- Instruction::UPlus uplus;
- bytecodeGenerator->addInstruction(uplus);
+ Instruction::UPlus uplus = {};
+ bytecodeGenerator->addTracingInstruction(uplus);
Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator();
Instruction::Increment inc = {};
bytecodeGenerator->addTracingInstruction(inc);
@@ -330,17 +331,17 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
e.loadInAccumulator();
Instruction::Increment inc = {};
bytecodeGenerator->addTracingInstruction(inc);
- if (_expr.accept(nx))
+ if (exprAccept(nx))
return e.storeConsumeAccumulator();
else
return e.storeRetainAccumulator();
}
case PostDecrement:
- if (!_expr.accept(nx) || requiresReturnValue) {
+ if (!exprAccept(nx) || requiresReturnValue) {
Reference e = expr.asLValue();
e.loadInAccumulator();
- Instruction::UPlus uplus;
- bytecodeGenerator->addInstruction(uplus);
+ Instruction::UPlus uplus = {};
+ bytecodeGenerator->addTracingInstruction(uplus);
Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator();
Instruction::Decrement dec = {};
bytecodeGenerator->addTracingInstruction(dec);
@@ -356,7 +357,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
e.loadInAccumulator();
Instruction::Decrement dec = {};
bytecodeGenerator->addTracingInstruction(dec);
- if (_expr.accept(nx))
+ if (exprAccept(nx))
return e.storeConsumeAccumulator();
else
return e.storeRetainAccumulator();
@@ -368,22 +369,13 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
void Codegen::addCJump()
{
- bytecodeGenerator->addCJumpInstruction(_expr.trueBlockFollowsCondition(),
- _expr.iftrue(), _expr.iffalse());
-}
-
-void Codegen::accept(Node *node)
-{
- if (hasError)
- return;
-
- if (node)
- node->accept(this);
+ const Result &expression = currentExpr();
+ bytecodeGenerator->addCJumpInstruction(expression.trueBlockFollowsCondition(),
+ expression.iftrue(), expression.iffalse());
}
void Codegen::statement(Statement *ast)
{
- RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
RegisterScope scope(this);
bytecodeGenerator->setLocation(ast->firstSourceLocation());
@@ -399,23 +391,21 @@ void Codegen::statement(ExpressionNode *ast)
if (! ast) {
return;
} else {
- RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
RegisterScope scope(this);
- Result r(nx);
- qSwap(_expr, r);
+ pushExpr(Result(nx));
VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast);
qSwap(_volatileMemoryLocations, vLocs);
accept(ast);
qSwap(_volatileMemoryLocations, vLocs);
- qSwap(_expr, r);
+ Reference result = popResult();
if (hasError)
return;
- if (r.result().loadTriggersSideEffect())
- r.result().loadInAccumulator(); // triggers side effects
+ if (result.loadTriggersSideEffect())
+ result.loadInAccumulator(); // triggers side effects
}
}
@@ -428,11 +418,9 @@ void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *ift
if (!ast)
return;
- RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
- Result r(iftrue, iffalse, trueBlockFollowsCondition);
- qSwap(_expr, r);
+ pushExpr(Result(iftrue, iffalse, trueBlockFollowsCondition));
accept(ast);
- qSwap(_expr, r);
+ Result r = popExpr();
if (hasError)
return;
@@ -450,18 +438,6 @@ void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *ift
}
}
-Codegen::Reference Codegen::expression(ExpressionNode *ast)
-{
- RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
- Result r;
- if (ast) {
- qSwap(_expr, r);
- accept(ast);
- qSwap(_expr, r);
- }
- return r.result();
-}
-
void Codegen::program(Program *ast)
{
if (ast) {
@@ -875,17 +851,13 @@ bool Codegen::visit(ExportDeclaration *ast)
Reference exportedValue;
if (auto *fdecl = AST::cast<FunctionDeclaration*>(ast->variableStatementOrDeclaration)) {
- Result r;
- qSwap(_expr, r);
+ pushExpr();
visit(static_cast<FunctionExpression*>(fdecl));
- qSwap(_expr, r);
- exportedValue = r.result();
+ exportedValue = popResult();
} else if (auto *classDecl = AST::cast<ClassDeclaration*>(ast->variableStatementOrDeclaration)) {
- Result r;
- qSwap(_expr, r);
+ pushExpr();
visit(static_cast<ClassExpression*>(classDecl));
- qSwap(_expr, r);
- exportedValue = r.result();
+ exportedValue = popResult();
} else if (ExpressionNode *expr = ast->variableStatementOrDeclaration->expressionCast()) {
exportedValue = expression(expr);
}
@@ -1068,7 +1040,7 @@ bool Codegen::visit(ClassExpression *ast)
(void) ctor.storeRetainAccumulator();
}
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -1151,7 +1123,7 @@ bool Codegen::visit(ArrayPattern *ast)
}
if (!it) {
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
Q_ASSERT(it->element && it->element->type == PatternElement::SpreadElement);
@@ -1246,7 +1218,7 @@ bool Codegen::visit(ArrayPattern *ast)
}
array.loadInAccumulator();
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -1262,7 +1234,7 @@ bool Codegen::visit(ArrayMemberExpression *ast)
return false;
if (base.isSuper()) {
Reference index = expression(ast->expression).storeOnStack();
- _expr.setResult(Reference::fromSuperProperty(index));
+ setExprResult(Reference::fromSuperProperty(index));
return false;
}
base = base.storeOnStack();
@@ -1272,17 +1244,17 @@ bool Codegen::visit(ArrayMemberExpression *ast)
QString s = str->value.toString();
uint arrayIndex = QV4::String::toArrayIndex(s);
if (arrayIndex == UINT_MAX) {
- _expr.setResult(Reference::fromMember(base, str->value.toString()));
+ setExprResult(Reference::fromMember(base, str->value.toString()));
return false;
}
Reference index = Reference::fromConst(this, QV4::Encode(arrayIndex));
- _expr.setResult(Reference::fromSubscript(base, index));
+ setExprResult(Reference::fromSubscript(base, index));
return false;
}
Reference index = expression(ast->expression);
if (hasError)
return false;
- _expr.setResult(Reference::fromSubscript(base, index));
+ setExprResult(Reference::fromSubscript(base, index));
return false;
}
@@ -1313,12 +1285,13 @@ bool Codegen::visit(BinaryExpression *ast)
TailCallBlocker blockTailCalls(this);
if (ast->op == QSOperator::And) {
- if (_expr.accept(cx)) {
+ if (exprAccept(cx)) {
auto iftrue = bytecodeGenerator->newLabel();
- condition(ast->left, &iftrue, _expr.iffalse(), true);
+ condition(ast->left, &iftrue, currentExpr().iffalse(), true);
iftrue.link();
blockTailCalls.unblock();
- condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition());
+ const Result &expr = currentExpr();
+ condition(ast->right, expr.iftrue(), expr.iffalse(), expr.trueBlockFollowsCondition());
} else {
auto iftrue = bytecodeGenerator->newLabel();
auto endif = bytecodeGenerator->newLabel();
@@ -1340,15 +1313,16 @@ bool Codegen::visit(BinaryExpression *ast)
endif.link();
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
}
return false;
} else if (ast->op == QSOperator::Or) {
- if (_expr.accept(cx)) {
+ if (exprAccept(cx)) {
auto iffalse = bytecodeGenerator->newLabel();
- condition(ast->left, _expr.iftrue(), &iffalse, false);
+ condition(ast->left, currentExpr().iftrue(), &iffalse, false);
iffalse.link();
- condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition());
+ const Result &expr = currentExpr();
+ condition(ast->right, expr.iftrue(), expr.iffalse(), expr.trueBlockFollowsCondition());
} else {
auto iffalse = bytecodeGenerator->newLabel();
auto endif = bytecodeGenerator->newLabel();
@@ -1370,7 +1344,7 @@ bool Codegen::visit(BinaryExpression *ast)
endif.link();
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
}
return false;
} else if (ast->op == QSOperator::Assign) {
@@ -1381,9 +1355,9 @@ bool Codegen::visit(BinaryExpression *ast)
return false;
right = right.storeOnStack();
destructurePattern(p, right);
- if (!_expr.accept(nx)) {
+ if (!exprAccept(nx)) {
right.loadInAccumulator();
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
}
return false;
}
@@ -1403,10 +1377,10 @@ bool Codegen::visit(BinaryExpression *ast)
if (hasError)
return false;
r.loadInAccumulator();
- if (_expr.accept(nx))
- _expr.setResult(left.storeConsumeAccumulator());
+ if (exprAccept(nx))
+ setExprResult(left.storeConsumeAccumulator());
else
- _expr.setResult(left.storeRetainAccumulator());
+ setExprResult(left.storeRetainAccumulator());
return false;
}
@@ -1449,7 +1423,7 @@ bool Codegen::visit(BinaryExpression *ast)
return false;
binopHelper(baseOp(ast->op), tempLeft, right).loadInAccumulator();
- _expr.setResult(left.storeRetainAccumulator());
+ setExprResult(left.storeRetainAccumulator());
break;
}
@@ -1461,7 +1435,7 @@ bool Codegen::visit(BinaryExpression *ast)
Reference right = expression(ast->right);
if (hasError)
return false;
- _expr.setResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left));
+ setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left));
break;
}
// intentional fall-through!
@@ -1487,7 +1461,7 @@ bool Codegen::visit(BinaryExpression *ast)
Reference right;
if (AST::NumericLiteral *rhs = AST::cast<AST::NumericLiteral *>(ast->right)) {
visit(rhs);
- right = _expr.result();
+ right = exprResult();
} else {
left = left.storeOnStack(); // force any loads of the lhs, so the rhs won't clobber it
right = expression(ast->right);
@@ -1495,7 +1469,7 @@ bool Codegen::visit(BinaryExpression *ast)
if (hasError)
return false;
- _expr.setResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right));
+ setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right));
break;
}
@@ -1672,7 +1646,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::StrictEqual: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpStrictEqual cmp;
@@ -1683,7 +1657,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::StrictNotEqual: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpStrictNotEqual cmp;
@@ -1694,7 +1668,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Equal: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpEq cmp;
@@ -1705,7 +1679,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::NotEqual: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpNe cmp;
@@ -1716,7 +1690,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Gt: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpGt cmp;
@@ -1727,7 +1701,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Ge: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpGe cmp;
@@ -1738,7 +1712,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Lt: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpLt cmp;
@@ -1749,7 +1723,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Le:
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpLe cmp;
@@ -1765,59 +1739,46 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
return Reference::fromAccumulator(this);
}
-static QSOperator::Op operatorForSwappedOperands(QSOperator::Op oper)
-{
- switch (oper) {
- case QSOperator::StrictEqual: return QSOperator::StrictEqual;
- case QSOperator::StrictNotEqual: return QSOperator::StrictNotEqual;
- case QSOperator::Equal: return QSOperator::Equal;
- case QSOperator::NotEqual: return QSOperator::NotEqual;
- case QSOperator::Gt: return QSOperator::Le;
- case QSOperator::Ge: return QSOperator::Lt;
- case QSOperator::Lt: return QSOperator::Ge;
- case QSOperator::Le: return QSOperator::Gt;
- default: Q_UNIMPLEMENTED(); return QSOperator::Invalid;
- }
-}
-
Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Reference &right)
{
- if (left.isConstant()) {
- oper = operatorForSwappedOperands(oper);
- qSwap(left, right);
- }
+ // See if we can generate specialized comparison instructions:
+ if (oper == QSOperator::Equal || oper == QSOperator::NotEqual) {
+ // Because == and != are reflexive, we can do the following:
+ if (left.isConstant() && !right.isConstant())
+ qSwap(left, right); // null==a -> a==null
- if (right.isConstant() && (oper == QSOperator::Equal || oper == QSOperator::NotEqual)) {
- Value c = Value::fromReturnedValue(right.constant);
- if (c.isNull() || c.isUndefined()) {
- left.loadInAccumulator();
- if (oper == QSOperator::Equal) {
- Instruction::CmpEqNull cmp;
- bytecodeGenerator->addInstruction(cmp);
- addCJump();
- return Reference();
- } else if (oper == QSOperator::NotEqual) {
- Instruction::CmpNeNull cmp;
- bytecodeGenerator->addInstruction(cmp);
- addCJump();
- return Reference();
- }
- } else if (c.isInt32()) {
- left.loadInAccumulator();
- if (oper == QSOperator::Equal) {
- Instruction::CmpEqInt cmp;
- cmp.lhs = c.int_32();
- bytecodeGenerator->addInstruction(cmp);
- addCJump();
- return Reference();
- } else if (oper == QSOperator::NotEqual) {
- Instruction::CmpNeInt cmp;
- cmp.lhs = c.int_32();
- bytecodeGenerator->addInstruction(cmp);
- addCJump();
- return Reference();
- }
+ if (right.isConstant()) {
+ Value c = Value::fromReturnedValue(right.constant);
+ if (c.isNull() || c.isUndefined()) {
+ left.loadInAccumulator();
+ if (oper == QSOperator::Equal) {
+ Instruction::CmpEqNull cmp;
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ return Reference();
+ } else if (oper == QSOperator::NotEqual) {
+ Instruction::CmpNeNull cmp;
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ return Reference();
+ }
+ } else if (c.isInt32()) {
+ left.loadInAccumulator();
+ if (oper == QSOperator::Equal) {
+ Instruction::CmpEqInt cmp;
+ cmp.lhs = c.int_32();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ return Reference();
+ } else if (oper == QSOperator::NotEqual) {
+ Instruction::CmpNeInt cmp;
+ cmp.lhs = c.int_32();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ return Reference();
+ }
+ }
}
}
@@ -1902,8 +1863,6 @@ bool Codegen::visit(CallExpression *ast)
switch (base.type) {
case Reference::Member:
case Reference::Subscript:
- case Reference::QmlScopeObject:
- case Reference::QmlContextObject:
base = base.asLValue();
break;
case Reference::Name:
@@ -1953,7 +1912,7 @@ bool Codegen::visit(CallExpression *ast)
bytecodeGenerator->addInstruction(call);
}
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -1965,21 +1924,7 @@ bool Codegen::visit(CallExpression *ast)
void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject)
{
//### Do we really need all these call instructions? can's we load the callee in a temp?
- if (base.type == Reference::QmlScopeObject) {
- Instruction::CallScopeObjectProperty call;
- call.base = base.qmlBase.stackSlot();
- call.name = base.qmlCoreIndex;
- call.argc = calldata.argc;
- call.argv = calldata.argv;
- bytecodeGenerator->addTracingInstruction(call);
- } else if (base.type == Reference::QmlContextObject) {
- Instruction::CallContextObjectProperty call;
- call.base = base.qmlBase.stackSlot();
- call.name = base.qmlCoreIndex;
- call.argc = calldata.argc;
- call.argv = calldata.argv;
- bytecodeGenerator->addTracingInstruction(call);
- } else if (base.type == Reference::Member) {
+ if (base.type == Reference::Member) {
if (!disable_lookups && useFastLookups) {
Instruction::CallPropertyLookup call;
call.base = base.propertyBase.stackSlot();
@@ -2009,11 +1954,19 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio
call.argv = calldata.argv;
bytecodeGenerator->addTracingInstruction(call);
} else if (!disable_lookups && useFastLookups && base.global) {
- Instruction::CallGlobalLookup call;
- call.index = registerGlobalGetterLookup(base.nameAsIndex());
- call.argc = calldata.argc;
- call.argv = calldata.argv;
- bytecodeGenerator->addTracingInstruction(call);
+ if (base.qmlGlobal) {
+ Instruction::CallQmlContextPropertyLookup call;
+ call.index = registerQmlContextPropertyGetterLookup(base.nameAsIndex());
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addTracingInstruction(call);
+ } else {
+ Instruction::CallGlobalLookup call;
+ call.index = registerGlobalGetterLookup(base.nameAsIndex());
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addTracingInstruction(call);
+ }
} else {
Instruction::CallName call;
call.name = base.nameAsIndex();
@@ -2046,7 +1999,7 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio
bytecodeGenerator->addTracingInstruction(call);
}
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
}
Codegen::Arguments Codegen::pushArgs(ArgumentList *args)
@@ -2142,7 +2095,7 @@ bool Codegen::visit(ConditionalExpression *ast)
ko.loadInAccumulator();
jump_endif.link();
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -2172,7 +2125,7 @@ bool Codegen::visit(DeleteExpression *ast)
throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode."));
return false;
}
- _expr.setResult(Reference::fromConst(this, QV4::Encode(false)));
+ setExprResult(Reference::fromConst(this, QV4::Encode(false)));
return false;
case Reference::Name: {
if (_context->isStrict) {
@@ -2182,7 +2135,7 @@ bool Codegen::visit(DeleteExpression *ast)
Instruction::DeleteName del;
del.name = expr.nameAsIndex();
bytecodeGenerator->addInstruction(del);
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
case Reference::Member: {
@@ -2197,7 +2150,7 @@ bool Codegen::visit(DeleteExpression *ast)
del.base = expr.propertyBase.stackSlot();
del.index = index.stackSlot();
bytecodeGenerator->addInstruction(del);
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
case Reference::Subscript: {
@@ -2207,14 +2160,14 @@ bool Codegen::visit(DeleteExpression *ast)
del.base = expr.elementBase;
del.index = expr.elementSubscript.stackSlot();
bytecodeGenerator->addInstruction(del);
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
default:
break;
}
// [[11.4.1]] Return true if it's not a reference
- _expr.setResult(Reference::fromConst(this, QV4::Encode(true)));
+ setExprResult(Reference::fromConst(this, QV4::Encode(true)));
return false;
}
@@ -2223,7 +2176,7 @@ bool Codegen::visit(FalseLiteral *)
if (hasError)
return false;
- _expr.setResult(Reference::fromConst(this, QV4::Encode(false)));
+ setExprResult(Reference::fromConst(this, QV4::Encode(false)));
return false;
}
@@ -2232,7 +2185,7 @@ bool Codegen::visit(SuperLiteral *)
if (hasError)
return false;
- _expr.setResult(Reference::fromSuper(this));
+ setExprResult(Reference::fromSuper(this));
return false;
}
@@ -2250,12 +2203,12 @@ bool Codegen::visit(FieldMemberExpression *ast)
if (_context->isArrowFunction || _context->contextType == ContextType::Eval) {
Reference r = referenceForName(QStringLiteral("new.target"), false);
r.isReadonly = true;
- _expr.setResult(r);
+ setExprResult(r);
return false;
}
Reference r = Reference::fromStackSlot(this, CallData::NewTarget);
- _expr.setResult(r);
+ setExprResult(r);
return false;
}
}
@@ -2268,10 +2221,10 @@ bool Codegen::visit(FieldMemberExpression *ast)
load.stringId = registerString(ast->name.toString());
bytecodeGenerator->addInstruction(load);
Reference property = Reference::fromAccumulator(this).storeOnStack();
- _expr.setResult(Reference::fromSuperProperty(property));
+ setExprResult(Reference::fromSuperProperty(property));
return false;
}
- _expr.setResult(Reference::fromMember(base, ast->name.toString()));
+ setExprResult(Reference::fromMember(base, ast->name.toString()));
return false;
}
@@ -2281,12 +2234,15 @@ bool Codegen::visit(TaggedTemplate *ast)
return false;
RegisterScope scope(this);
+ return handleTaggedTemplate(expression(ast->base), ast);
+}
- int functionObject = -1, thisObject = -1;
-
- Reference base = expression(ast->base);
+bool Codegen::handleTaggedTemplate(Reference base, TaggedTemplate *ast)
+{
if (hasError)
return false;
+
+ int functionObject = -1, thisObject = -1;
switch (base.type) {
case Reference::Member:
case Reference::Subscript:
@@ -2347,7 +2303,7 @@ bool Codegen::visit(FunctionExpression *ast)
if (hasError)
return false;
loadClosure(function);
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -2379,14 +2335,10 @@ Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs, co
return r;
}
- // This hook allows implementing QML lookup semantics
- Reference fallback = fallbackNameLookup(name);
- if (fallback.type != Reference::Invalid)
- return fallback;
-
Reference r = Reference::fromName(this, name);
- r.global = useFastLookups && (resolved.type == Context::ResolvedName::Global);
- if (!r.global && canAccelerateGlobalLookups() && m_globalNames.contains(name))
+ r.global = useFastLookups && (resolved.type == Context::ResolvedName::Global || resolved.type == Context::ResolvedName::QmlGlobal);
+ r.qmlGlobal = resolved.type == Context::ResolvedName::QmlGlobal;
+ if (!r.global && !r.qmlGlobal && m_globalNames.contains(name))
r.global = true;
return r;
}
@@ -2402,18 +2354,12 @@ void Codegen::loadClosure(int closureId)
}
}
-Codegen::Reference Codegen::fallbackNameLookup(const QString &name)
-{
- Q_UNUSED(name)
- return Reference();
-}
-
bool Codegen::visit(IdentifierExpression *ast)
{
if (hasError)
return false;
- _expr.setResult(referenceForName(ast->name.toString(), false, ast->firstSourceLocation()));
+ setExprResult(referenceForName(ast->name.toString(), false, ast->firstSourceLocation()));
return false;
}
@@ -2463,7 +2409,7 @@ void Codegen::handleConstruct(const Reference &base, ArgumentList *arguments)
// set the result up as the thisObject
Reference::fromAccumulator(this).storeOnStack(CallData::This);
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
}
bool Codegen::visit(NewExpression *ast)
@@ -2512,7 +2458,7 @@ bool Codegen::visit(NotExpression *ast)
return false;
TailCallBlocker blockTailCalls(this);
- _expr.setResult(unop(Not, expression(ast->expression)));
+ setExprResult(unop(Not, expression(ast->expression)));
return false;
}
@@ -2521,10 +2467,10 @@ bool Codegen::visit(NullExpression *)
if (hasError)
return false;
- if (_expr.accept(cx))
- bytecodeGenerator->jump().link(*_expr.iffalse());
+ if (exprAccept(cx))
+ bytecodeGenerator->jump().link(*currentExpr().iffalse());
else
- _expr.setResult(Reference::fromConst(this, Encode::null()));
+ setExprResult(Reference::fromConst(this, Encode::null()));
return false;
}
@@ -2534,7 +2480,7 @@ bool Codegen::visit(NumericLiteral *ast)
if (hasError)
return false;
- _expr.setResult(Reference::fromConst(this, QV4::Encode::smallestNumber(ast->value)));
+ setExprResult(Reference::fromConst(this, QV4::Encode::smallestNumber(ast->value)));
return false;
}
@@ -2646,8 +2592,7 @@ bool Codegen::visit(ObjectPattern *ast)
call.argc = argc;
call.args = Moth::StackSlot::createRegister(args);
bytecodeGenerator->addInstruction(call);
- Reference result = Reference::fromAccumulator(this);
- _expr.setResult(result);
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -2666,7 +2611,7 @@ bool Codegen::visit(PostDecrementExpression *ast)
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken))
return false;
- _expr.setResult(unop(PostDecrement, expr));
+ setExprResult(unop(PostDecrement, expr));
return false;
}
@@ -2686,7 +2631,7 @@ bool Codegen::visit(PostIncrementExpression *ast)
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken))
return false;
- _expr.setResult(unop(PostIncrement, expr));
+ setExprResult(unop(PostIncrement, expr));
return false;
}
@@ -2704,7 +2649,7 @@ bool Codegen::visit(PreDecrementExpression *ast)
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken))
return false;
- _expr.setResult(unop(PreDecrement, expr));
+ setExprResult(unop(PreDecrement, expr));
return false;
}
@@ -2723,7 +2668,7 @@ bool Codegen::visit(PreIncrementExpression *ast)
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken))
return false;
- _expr.setResult(unop(PreIncrement, expr));
+ setExprResult(unop(PreIncrement, expr));
return false;
}
@@ -2734,7 +2679,7 @@ bool Codegen::visit(RegExpLiteral *ast)
auto r = Reference::fromStackSlot(this);
r.isReadonly = true;
- _expr.setResult(r);
+ setExprResult(r);
Instruction::MoveRegExp instr;
instr.regExpId = jsUnitGenerator->registerRegExp(ast);
@@ -2750,7 +2695,7 @@ bool Codegen::visit(StringLiteral *ast)
auto r = Reference::fromAccumulator(this);
r.isReadonly = true;
- _expr.setResult(r);
+ setExprResult(r);
Instruction::LoadRuntimeString instr;
instr.stringId = registerString(ast->value.toString());
@@ -2800,7 +2745,7 @@ bool Codegen::visit(TemplateLiteral *ast)
auto r = Reference::fromAccumulator(this);
r.isReadonly = true;
- _expr.setResult(r);
+ setExprResult(r);
return false;
}
@@ -2813,10 +2758,10 @@ bool Codegen::visit(ThisExpression *)
if (_context->isArrowFunction) {
Reference r = referenceForName(QStringLiteral("this"), false);
r.isReadonly = true;
- _expr.setResult(r);
+ setExprResult(r);
return false;
}
- _expr.setResult(Reference::fromThis(this));
+ setExprResult(Reference::fromThis(this));
return false;
}
@@ -2826,7 +2771,7 @@ bool Codegen::visit(TildeExpression *ast)
return false;
TailCallBlocker blockTailCalls(this);
- _expr.setResult(unop(Compl, expression(ast->expression)));
+ setExprResult(unop(Compl, expression(ast->expression)));
return false;
}
@@ -2835,7 +2780,7 @@ bool Codegen::visit(TrueLiteral *)
if (hasError)
return false;
- _expr.setResult(Reference::fromConst(this, QV4::Encode(true)));
+ setExprResult(Reference::fromConst(this, QV4::Encode(true)));
return false;
}
@@ -2861,7 +2806,7 @@ bool Codegen::visit(TypeOfExpression *ast)
Instruction::TypeofValue instr;
bytecodeGenerator->addInstruction(instr);
}
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -2872,7 +2817,7 @@ bool Codegen::visit(UnaryMinusExpression *ast)
return false;
TailCallBlocker blockTailCalls(this);
- _expr.setResult(unop(UMinus, expression(ast->expression)));
+ setExprResult(unop(UMinus, expression(ast->expression)));
return false;
}
@@ -2882,7 +2827,7 @@ bool Codegen::visit(UnaryPlusExpression *ast)
return false;
TailCallBlocker blockTailCalls(this);
- _expr.setResult(unop(UPlus, expression(ast->expression)));
+ setExprResult(unop(UPlus, expression(ast->expression)));
return false;
}
@@ -2895,7 +2840,7 @@ bool Codegen::visit(VoidExpression *ast)
TailCallBlocker blockTailCalls(this);
statement(ast->expression);
- _expr.setResult(Reference::fromConst(this, Encode::undefined()));
+ setExprResult(Reference::fromConst(this, Encode::undefined()));
return false;
}
@@ -2909,7 +2854,7 @@ bool Codegen::visit(FunctionDeclaration * ast)
if (_functionContext->contextType == ContextType::Binding)
referenceForName(ast->name.toString(), true).loadInAccumulator();
- _expr.accept(nx);
+ exprAccept(nx);
return false;
}
@@ -2965,7 +2910,7 @@ bool Codegen::visit(YieldExpression *ast)
done.link();
lhsValue.loadInAccumulator();
- _expr.setResult(acc);
+ setExprResult(acc);
return false;
}
@@ -2976,7 +2921,7 @@ bool Codegen::visit(YieldExpression *ast)
BytecodeGenerator::Jump jump = bytecodeGenerator->addJumpInstruction(resume);
emitReturn(acc);
jump.link();
- _expr.setResult(acc);
+ setExprResult(acc);
return false;
}
@@ -3115,8 +3060,6 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
bytecodeGenerator->addInstruction(yield);
}
- beginFunctionBodyHook();
-
statementList(body);
if (!hasError) {
@@ -3855,8 +3798,14 @@ QQmlRefPointer<CompiledData::CompilationUnit> Codegen::createUnitForLoading()
class Codegen::VolatileMemoryLocationScanner: protected QQmlJS::AST::Visitor
{
VolatileMemoryLocations locs;
+ Codegen *parent;
public:
+ VolatileMemoryLocationScanner(Codegen *parent) :
+ QQmlJS::AST::Visitor(parent->recursionDepth()),
+ parent(parent)
+ {}
+
Codegen::VolatileMemoryLocations scan(AST::Node *s)
{
s->accept(this);
@@ -3921,25 +3870,41 @@ public:
}
}
+ void throwRecursionDepthError() override
+ {
+ parent->throwRecursionDepthError();
+ }
+
private:
- void collectIdentifiers(QVector<QStringView> &ids, AST::Node *node) const {
+ void collectIdentifiers(QVector<QStringView> &ids, AST::Node *node) {
class Collector: public QQmlJS::AST::Visitor {
+ private:
QVector<QStringView> &ids;
+ VolatileMemoryLocationScanner *parent;
+
public:
- Collector(QVector<QStringView> &ids): ids(ids) {}
- virtual bool visit(IdentifierExpression *ie) {
+ Collector(QVector<QStringView> &ids, VolatileMemoryLocationScanner *parent) :
+ QQmlJS::AST::Visitor(parent->recursionDepth()), ids(ids), parent(parent)
+ {}
+
+ bool visit(IdentifierExpression *ie) final {
ids.append(ie->name);
return false;
}
+
+ void throwRecursionDepthError() final
+ {
+ parent->throwRecursionDepthError();
+ }
};
- Collector collector(ids);
+ Collector collector(ids, this);
node->accept(&collector);
}
};
-Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node *ast) const
+Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node *ast)
{
- VolatileMemoryLocationScanner scanner;
+ VolatileMemoryLocationScanner scanner(this);
return scanner.scan(ast);
}
@@ -4041,10 +4006,6 @@ bool Codegen::Reference::operator==(const Codegen::Reference &other) const
return index == other.index;
case Const:
return constant == other.constant;
- case QmlScopeObject:
- case QmlContextObject:
- return qmlCoreIndex == other.qmlCoreIndex && qmlNotifyIndex == other.qmlNotifyIndex
- && capturePolicy == other.capturePolicy;
}
return true;
}
@@ -4102,9 +4063,7 @@ Codegen::Reference Codegen::Reference::storeConsumeAccumulator() const
Codegen::Reference Codegen::Reference::baseObject() const
{
- if (type == Reference::QmlScopeObject || type == Reference::QmlContextObject) {
- return Reference::fromStackSlot(codegen, qmlBase.stackSlot());
- } else if (type == Reference::Member) {
+ if (type == Reference::Member) {
RValue rval = propertyBase;
if (!rval.isValid())
return Reference::fromConst(codegen, Encode::undefined());
@@ -4189,8 +4148,6 @@ bool Codegen::Reference::storeWipesAccumulator() const
case Name:
case Member:
case Subscript:
- case QmlScopeObject:
- case QmlContextObject:
return true;
}
}
@@ -4270,18 +4227,6 @@ void Codegen::Reference::storeAccumulator() const
store.index = elementSubscript.stackSlot();
codegen->bytecodeGenerator->addTracingInstruction(store);
} return;
- case QmlScopeObject: {
- Instruction::StoreScopeObjectProperty store;
- store.base = qmlBase;
- store.propertyIndex = qmlCoreIndex;
- codegen->bytecodeGenerator->addInstruction(store);
- } return;
- case QmlContextObject: {
- Instruction::StoreContextObjectProperty store;
- store.base = qmlBase;
- store.propertyIndex = qmlCoreIndex;
- codegen->bytecodeGenerator->addInstruction(store);
- } return;
case Invalid:
case Accumulator:
case Const:
@@ -4396,9 +4341,15 @@ QT_WARNING_POP
}
}
if (!disable_lookups && global) {
- Instruction::LoadGlobalLookup load;
- load.index = codegen->registerGlobalGetterLookup(nameAsIndex());
- codegen->bytecodeGenerator->addTracingInstruction(load);
+ if (qmlGlobal) {
+ Instruction::LoadQmlContextPropertyLookup load;
+ load.index = codegen->registerQmlContextPropertyGetterLookup(nameAsIndex());
+ codegen->bytecodeGenerator->addTracingInstruction(load);
+ } else {
+ Instruction::LoadGlobalLookup load;
+ load.index = codegen->registerGlobalGetterLookup(nameAsIndex());
+ codegen->bytecodeGenerator->addTracingInstruction(load);
+ }
} else {
Instruction::LoadName load;
load.name = nameAsIndex();
@@ -4432,24 +4383,6 @@ QT_WARNING_POP
load.base = elementBase;
codegen->bytecodeGenerator->addTracingInstruction(load);
} return;
- case QmlScopeObject: {
- Instruction::LoadScopeObjectProperty load;
- load.base = qmlBase;
- load.propertyIndex = qmlCoreIndex;
- load.captureRequired = capturePolicy == CaptureAtRuntime;
- codegen->bytecodeGenerator->addInstruction(load);
- if (capturePolicy == CaptureAheadOfTime)
- codegen->_context->scopeObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex);
- } return;
- case QmlContextObject: {
- Instruction::LoadContextObjectProperty load;
- load.base = qmlBase;
- load.propertyIndex = qmlCoreIndex;
- load.captureRequired = capturePolicy == CaptureAtRuntime;
- codegen->bytecodeGenerator->addInstruction(load);
- if (capturePolicy == CaptureAheadOfTime)
- codegen->_context->contextObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex);
- } return;
case Invalid:
break;
}
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index 4d7001fe64..ad86483132 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -184,16 +184,31 @@ public:
Member,
Subscript,
Import,
- QmlScopeObject,
- QmlContextObject,
- LastLValue = QmlContextObject,
+ LastLValue = Import,
Const
} type = Invalid;
bool isLValue() const { return !isReadonly && type > Accumulator; }
- Reference(Codegen *cg, Type type = Invalid) : type(type), constant(0), codegen(cg) {}
- Reference(): constant(0) {}
+ Reference(Codegen *cg, Type t = Invalid) : Reference()
+ {
+ type = t;
+ codegen = cg;
+ }
+
+ Reference() :
+ constant(0),
+ isArgOrEval(false),
+ isReadonly(false),
+ isReferenceToConst(false),
+ requiresTDZCheck(false),
+ subscriptRequiresTDZCheck(false),
+ stackSlotIsLocalOrArgument(false),
+ isVolatile(false),
+ global(false),
+ qmlGlobal(false)
+ {}
+
Reference(const Reference &) = default;
Reference(Reference &&) = default;
Reference &operator =(const Reference &) = default;
@@ -206,10 +221,6 @@ public:
bool isValid() const { return type != Invalid; }
bool loadTriggersSideEffect() const {
switch (type) {
- case QmlScopeObject:
- return capturePolicy != DontCapture;
- case QmlContextObject:
- return capturePolicy != DontCapture;
case Name:
case Member:
case Subscript:
@@ -228,28 +239,6 @@ public:
return isStackSlot();
}
- enum PropertyCapturePolicy {
- /*
- We're reading a property from the scope or context object, but it's a CONSTANT property,
- so we don't need to register a dependency at all.
- */
- DontCapture,
- /*
- We're reading the property of a QObject, and we know that it's the
- scope object or context object, which we know very well. Instead of registering a
- property capture every time, we can do that ahead of time and then register all those
- captures in one shot in registerQmlDependencies().
- */
- CaptureAheadOfTime,
- /*
- We're reading the property of a QObject, and we're not quite sure where
- the QObject comes from or what it is. So, when reading that property at run-time,
- make sure that we capture where we read that property so that if it changes we can
- re-evaluate the entire expression.
- */
- CaptureAtRuntime
- };
-
static Reference fromAccumulator(Codegen *cg) {
return Reference(cg, Accumulator);
}
@@ -316,22 +305,6 @@ public:
r.isReadonly = true;
return r;
}
- static Reference fromQmlScopeObject(const Reference &base, qint16 coreIndex, qint16 notifyIndex, PropertyCapturePolicy capturePolicy) {
- Reference r(base.codegen, QmlScopeObject);
- r.qmlBase = base.storeOnStack().stackSlot();
- r.qmlCoreIndex = coreIndex;
- r.qmlNotifyIndex = notifyIndex;
- r.capturePolicy = capturePolicy;
- return r;
- }
- static Reference fromQmlContextObject(const Reference &base, qint16 coreIndex, qint16 notifyIndex, PropertyCapturePolicy capturePolicy) {
- Reference r(base.codegen, QmlContextObject);
- r.qmlBase = base.storeOnStack().stackSlot();
- r.qmlCoreIndex = coreIndex;
- r.qmlNotifyIndex = notifyIndex;
- r.capturePolicy = capturePolicy;
- return r;
- }
static Reference fromThis(Codegen *cg) {
Reference r = fromStackSlot(cg, CallData::This);
r.isReadonly = true;
@@ -386,25 +359,21 @@ public:
Moth::StackSlot elementBase;
RValue elementSubscript;
};
- struct { // QML scope/context object case
- Moth::StackSlot qmlBase;
- qint16 qmlCoreIndex;
- qint16 qmlNotifyIndex;
- PropertyCapturePolicy capturePolicy;
- };
Moth::StackSlot property; // super property
};
QString name;
- mutable bool isArgOrEval = false;
- bool isReadonly = false;
- bool isReferenceToConst = false;
- bool requiresTDZCheck = false;
- bool subscriptRequiresTDZCheck = false;
- bool stackSlotIsLocalOrArgument = false;
- bool isVolatile = false;
- bool global = false;
Codegen *codegen = nullptr;
+ quint32 isArgOrEval:1;
+ quint32 isReadonly:1;
+ quint32 isReferenceToConst:1;
+ quint32 requiresTDZCheck:1;
+ quint32 subscriptRequiresTDZCheck:1;
+ quint32 stackSlotIsLocalOrArgument:1;
+ quint32 isVolatile:1;
+ quint32 global:1;
+ quint32 qmlGlobal:1;
+
private:
void storeAccumulator() const;
Reference doStoreOnStack(int tempIndex) const;
@@ -499,6 +468,10 @@ protected:
void setResult(const Reference &result) {
_result = result;
}
+
+ void setResult(Reference &&result) {
+ _result = std::move(result);
+ }
};
void enterContext(AST::Node *node);
@@ -532,6 +505,7 @@ public:
int registerGetterLookup(int nameIndex) { return jsUnitGenerator->registerGetterLookup(nameIndex); }
int registerSetterLookup(int nameIndex) { return jsUnitGenerator->registerSetterLookup(nameIndex); }
int registerGlobalGetterLookup(int nameIndex) { return jsUnitGenerator->registerGlobalGetterLookup(nameIndex); }
+ int registerQmlContextPropertyGetterLookup(int nameIndex) { return jsUnitGenerator->registerQmlContextPropertyGetterLookup(nameIndex); }
// Returns index in _module->functions
virtual int defineFunction(const QString &name, AST::Node *ast,
@@ -544,9 +518,22 @@ protected:
void condition(AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue,
const BytecodeGenerator::Label *iffalse,
bool trueBlockFollowsCondition);
- Reference expression(AST::ExpressionNode *ast);
- void accept(AST::Node *node);
+ inline Reference expression(AST::ExpressionNode *ast)
+ {
+ if (!ast || hasError)
+ return Reference();
+
+ pushExpr();
+ ast->accept(this);
+ return popResult();
+ }
+
+ inline void accept(AST::Node *node)
+ {
+ if (!hasError && node)
+ node->accept(this);
+ }
void program(AST::Program *ast);
void statementList(AST::StatementList *ast);
@@ -561,12 +548,6 @@ protected:
Reference referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name);
- // Hooks provided to implement QML lookup semantics
- virtual bool canAccelerateGlobalLookups() const { return true; }
- virtual Reference fallbackNameLookup(const QString &name);
-
- virtual void beginFunctionBodyHook() {}
-
void emitReturn(const Reference &expr);
// nodes
@@ -670,6 +651,11 @@ protected:
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);
+ void throwRecursionDepthError() override
+ {
+ throwSyntaxError(AST::SourceLocation(),
+ QStringLiteral("Maximum statement or expression depth exceeded"));
+ }
public:
QList<DiagnosticMessage> errors() const;
@@ -684,6 +670,7 @@ public:
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);
void setUseFastLookups(bool b) { useFastLookups = b; }
@@ -714,13 +701,40 @@ public:
m_globalNames = globalNames;
}
+ static const char *s_globalNames[];
protected:
friend class ScanFunctions;
friend struct ControlFlow;
friend struct ControlFlowCatch;
friend struct ControlFlowFinally;
- Result _expr;
+
+ inline void setExprResult(const Reference &result) { m_expressions.back().setResult(result); }
+ inline void setExprResult(Reference &&result) { m_expressions.back().setResult(std::move(result)); }
+ inline Reference exprResult() const { return m_expressions.back().result(); }
+
+ inline bool exprAccept(Format f) { return m_expressions.back().accept(f); }
+
+ inline const Result &currentExpr() const { return m_expressions.back(); }
+
+ inline void pushExpr(Result &&expr) { m_expressions.push_back(std::move(expr)); }
+ inline void pushExpr(const Result &expr) { m_expressions.push_back(expr); }
+ inline void pushExpr() { m_expressions.emplace_back(); }
+
+ inline Result popExpr()
+ {
+ const Result result = m_expressions.back();
+ m_expressions.pop_back();
+ return result;
+ }
+
+ inline Reference popResult() {
+ const Reference result = m_expressions.back().result();
+ m_expressions.pop_back();
+ return result;
+ }
+
+ std::vector<Result> m_expressions;
VolatileMemoryLocations _volatileMemoryLocations;
Module *_module;
int _returnAddress;
@@ -769,33 +783,8 @@ protected:
bool _onoff;
};
- class RecursionDepthCheck {
- public:
- RecursionDepthCheck(Codegen *cg, const AST::SourceLocation &loc)
- : _cg(cg)
- {
-#ifdef QT_NO_DEBUG
- const int depthLimit = 4000; // limit to ~1000 deep
-#else
- const int depthLimit = 1000; // limit to ~250 deep
-#endif // QT_NO_DEBUG
-
- ++_cg->_recursionDepth;
- if (_cg->_recursionDepth > depthLimit)
- _cg->throwSyntaxError(loc, QStringLiteral("Maximum statement or expression depth exceeded"));
- }
-
- ~RecursionDepthCheck()
- { --_cg->_recursionDepth; }
-
- private:
- Codegen *_cg;
- };
- int _recursionDepth = 0;
- friend class RecursionDepthCheck;
-
private:
- VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast) const;
+ VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast);
void handleConstruct(const Reference &base, AST::ArgumentList *args);
};
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index 5dd6fca023..a78094f17c 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -50,6 +50,8 @@
#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>
@@ -95,18 +97,25 @@ CompilationUnit::CompilationUnit(const Unit *unitData, const QString &fileName,
setUnitData(unitData, nullptr, fileName, finalUrlString);
}
-#ifndef V4_BOOTSTRAP
CompilationUnit::~CompilationUnit()
{
+#ifndef V4_BOOTSTRAP
unlink();
+#endif
if (data) {
if (data->qmlUnit() != qmlData)
free(const_cast<QmlUnit *>(qmlData));
qmlData = nullptr;
+#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
@@ -117,6 +126,7 @@ CompilationUnit::~CompilationUnit()
delete [] imports;
imports = nullptr;
}
+#ifndef V4_BOOTSTRAP
QString CompilationUnit::localCacheFilePath(const QUrl &url)
{
@@ -167,6 +177,8 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
l->setter = QV4::Lookup::setterGeneric;
else if (type == CompiledData::Lookup::Type_GlobalGetter)
l->globalGetter = QV4::Lookup::globalGetterGeneric;
+ else if (type == CompiledData::Lookup::Type_QmlContextPropertyGetter)
+ l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
l->nameIndex = compiledLookups[i].nameIndex;
}
}
@@ -269,6 +281,24 @@ void CompilationUnit::unlink()
propertyCaches.clear();
+ if (runtimeLookups) {
+ for (uint i = 0; i < data->lookupTableSize; ++i) {
+ QV4::Lookup &l = runtimeLookups[i];
+ if (l.getter == QV4::QObjectWrapper::lookupGetter) {
+ if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache)
+ pc->release();
+ } else if (l.getter == QQmlValueTypeWrapper::lookupGetter) {
+ if (QQmlPropertyCache *pc = l.qgadgetLookup.propertyCache)
+ pc->release();
+ }
+
+ if (l.qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty) {
+ if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache)
+ pc->release();
+ }
+ }
+ }
+
dependentScripts.clear();
typeNameCache = nullptr;
@@ -440,8 +470,10 @@ Heap::Module *CompilationUnit::instantiate(ExecutionEngine *engine)
ScopedString importName(scope);
const uint importCount = data->importEntryTableSize;
- imports = new const Value *[importCount];
- memset(imports, 0, importCount * sizeof(Value *));
+ if (importCount > 0) {
+ imports = new const Value *[importCount];
+ memset(imports, 0, importCount * sizeof(Value *));
+ }
for (uint i = 0; i < importCount; ++i) {
const CompiledData::ImportEntry &entry = data->importEntryTable()[i];
auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this);
@@ -867,6 +899,8 @@ bool ResolvedTypeReference::addToHash(QCryptographicHash *hash, QQmlEngine *engi
hash->addData(createPropertyCache(engine)->checksum(&ok));
return ok;
}
+ if (!compilationUnit)
+ return false;
hash->addData(compilationUnit->unitData()->md5Checksum, sizeof(compilationUnit->unitData()->md5Checksum));
return true;
}
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index 1aaba13241..4cfd2d86e8 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/compiler/qv4compileddata_p.h
@@ -73,7 +73,13 @@
QT_BEGIN_NAMESPACE
// Bump this whenever the compiler data structures change in an incompatible way.
-#define QV4_DATA_STRUCTURE_VERSION 0x1b
+//
+// 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 0x22 // Add trace slot to UPlus
class QIODevice;
class QQmlPropertyData;
@@ -159,9 +165,10 @@ static_assert(sizeof(RegExp) == 4, "RegExp structure needs to have the expected
struct Lookup
{
enum Type : unsigned int {
- Type_Getter = 0x0,
- Type_Setter = 0x1,
- Type_GlobalGetter = 2
+ Type_Getter = 0,
+ Type_Setter = 1,
+ Type_GlobalGetter = 2,
+ Type_QmlContextPropertyGetter = 3
};
union {
@@ -286,29 +293,16 @@ struct Function
quint16_le nRegisters;
Location location;
- // Qml Extensions Begin
- // Array of resolved ID objects
- size_t dependingIdObjectsOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); }
- quint16_le nDependingIdObjects;
- quint16_le nDependingContextProperties;
- // Array of int pairs (property index and notify index)
- size_t dependingContextPropertiesOffset() const { return dependingIdObjectsOffset() + nDependingIdObjects * sizeof(quint32); }
- quint16_le nDependingScopeProperties;
- // Array of int pairs (property index and notify index)
- size_t dependingScopePropertiesOffset() const { return dependingContextPropertiesOffset() + nDependingContextProperties * sizeof(quint32); }
- // Qml Extensions End
+ quint32_le nLabelInfos;
+ size_t labelInfosOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); }
typedef quint16_le TraceInfoCount;
TraceInfoCount nTraceInfos;
static constexpr TraceInfoCount NoTracing() { return TraceInfoCount::max(); }
- quint32_le nLabelInfos;
- size_t labelInfosOffset() const { return dependingScopePropertiesOffset() + nDependingScopeProperties; }
-
// Keep all unaligned data at the end
quint8 flags;
quint8 padding1;
- quint16 padding2;
// quint32 formalsIndex[nFormals]
// quint32 localsIndex[nLocals]
@@ -316,9 +310,6 @@ struct Function
const quint32_le *formalsTable() const { return reinterpret_cast<const quint32_le *>(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()); }
- const quint32_le *qmlIdObjectDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingIdObjectsOffset()); }
- const quint32_le *qmlContextPropertiesDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingContextPropertiesOffset()); }
- const quint32_le *qmlScopePropertiesDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingScopePropertiesOffset()); }
// --- QQmlPropertyCacheCreator interface
const quint32_le *formalsBegin() const { return formalsTable(); }
@@ -329,11 +320,9 @@ struct Function
const char *code() const { return reinterpret_cast<const char *>(this) + codeOffset; }
- inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; }
-
- static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int nIdObjectDependencies, int nPropertyDependencies, int labelInfoSize, int codeSize) {
- int trailingData = (nFormals + nLocals + nInnerfunctions + nIdObjectDependencies + labelInfoSize +
- 2 * nPropertyDependencies)*sizeof (quint32) + nLines*sizeof(CodeOffsetToLine);
+ static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int labelInfoSize, int codeSize) {
+ int trailingData = (nFormals + 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);
return int(size);
@@ -343,7 +332,7 @@ struct Function
return (a + 7) & ~size_t(7);
}
};
-static_assert(sizeof(Function) == 60, "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) == 52, "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 {
@@ -1106,11 +1095,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit final : public CompilationUnitBase
const QmlUnit *qmlData = nullptr;
public:
CompilationUnit(const Unit *unitData = nullptr, const QString &fileName = QString(), const QString &finalUrlString = QString());
-#ifdef V4_BOOTSTRAP
- ~CompilationUnit() {}
-#else
~CompilationUnit();
-#endif
void addref()
{
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index 0833f552e6..01c033cb2a 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -154,15 +154,19 @@ int QV4::Compiler::JSUnitGenerator::registerSetterLookup(int nameIndex)
return lookups.size() - 1;
}
-int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(const QString &name)
+int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex)
{
- return registerGlobalGetterLookup(registerString(name));
+ CompiledData::Lookup l;
+ l.type_and_flags = CompiledData::Lookup::Type_GlobalGetter;
+ l.nameIndex = nameIndex;
+ lookups << l;
+ return lookups.size() - 1;
}
-int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex)
+int QV4::Compiler::JSUnitGenerator::registerQmlContextPropertyGetterLookup(int nameIndex)
{
CompiledData::Lookup l;
- l.type_and_flags = CompiledData::Lookup::Type_GlobalGetter;
+ l.type_and_flags = CompiledData::Lookup::Type_QmlContextPropertyGetter;
l.nameIndex = nameIndex;
lookups << l;
return lookups.size() - 1;
@@ -423,28 +427,6 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
function->nTraceInfos = irFunction->nTraceInfos;
function->nRegisters = irFunction->registerCountInFunction;
- function->nDependingIdObjects = 0;
- function->nDependingContextProperties = 0;
- function->nDependingScopeProperties = 0;
-
- if (!irFunction->idObjectDependencies.isEmpty()) {
- function->nDependingIdObjects = irFunction->idObjectDependencies.count();
- Q_ASSERT(function->dependingIdObjectsOffset() == currentOffset);
- currentOffset += function->nDependingIdObjects * sizeof(quint32);
- }
-
- if (!irFunction->contextObjectPropertyDependencies.isEmpty()) {
- function->nDependingContextProperties = irFunction->contextObjectPropertyDependencies.count();
- Q_ASSERT(function->dependingContextPropertiesOffset() == currentOffset);
- currentOffset += function->nDependingContextProperties * sizeof(quint32) * 2;
- }
-
- if (!irFunction->scopeObjectPropertyDependencies.isEmpty()) {
- function->nDependingScopeProperties = irFunction->scopeObjectPropertyDependencies.count();
- Q_ASSERT(function->dependingScopePropertiesOffset() == currentOffset);
- currentOffset += function->nDependingScopeProperties * sizeof(quint32) * 2;
- }
-
if (!irFunction->labelInfo.empty()) {
function->nLabelInfos = quint32(irFunction->labelInfo.size());
Q_ASSERT(function->labelInfosOffset() == currentOffset);
@@ -470,25 +452,6 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
// write line numbers
memcpy(f + function->lineNumberOffset(), irFunction->lineNumberMapping.constData(), irFunction->lineNumberMapping.size()*sizeof(CompiledData::CodeOffsetToLine));
- // write QML dependencies
- quint32_le *writtenDeps = (quint32_le *)(f + function->dependingIdObjectsOffset());
- for (int id : irFunction->idObjectDependencies) {
- Q_ASSERT(id >= 0);
- *writtenDeps++ = static_cast<quint32>(id);
- }
-
- writtenDeps = (quint32_le *)(f + function->dependingContextPropertiesOffset());
- for (auto property : irFunction->contextObjectPropertyDependencies) {
- *writtenDeps++ = property.key(); // property index
- *writtenDeps++ = property.value(); // notify index
- }
-
- writtenDeps = (quint32_le *)(f + function->dependingScopePropertiesOffset());
- for (auto property : irFunction->scopeObjectPropertyDependencies) {
- *writtenDeps++ = property.key(); // property index
- *writtenDeps++ = property.value(); // notify index
- }
-
quint32_le *labels = (quint32_le *)(f + function->labelInfosOffset());
for (unsigned u : irFunction->labelInfo) {
*labels++ = u;
@@ -690,10 +653,8 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
Context *f = module->functions.at(i);
blockAndFunctionOffsets[i] = nextOffset;
- const int qmlIdDepsCount = f->idObjectDependencies.count();
- const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count();
quint32 size = QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->lineNumberMapping.size(), f->nestedContexts.size(),
- qmlIdDepsCount, qmlPropertyDepsCount, int(f->labelInfo.size()), f->code.size());
+ int(f->labelInfo.size()), f->code.size());
functionSize += size - f->code.size();
nextOffset += size;
}
diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h
index 2f5889ab53..49e334bb81 100644
--- a/src/qml/compiler/qv4compiler_p.h
+++ b/src/qml/compiler/qv4compiler_p.h
@@ -118,8 +118,8 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
int registerGetterLookup(int nameIndex);
int registerSetterLookup(const QString &name);
int registerSetterLookup(int nameIndex);
- int registerGlobalGetterLookup(const QString &name);
int registerGlobalGetterLookup(int nameIndex);
+ int registerQmlContextPropertyGetterLookup(int nameIndex);
int registerRegExp(QQmlJS::AST::RegExpLiteral *regexp);
diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp
index 931759fa72..52215c2ce6 100644
--- a/src/qml/compiler/qv4compilercontext.cpp
+++ b/src/qml/compiler/qv4compilercontext.cpp
@@ -187,10 +187,13 @@ Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::AS
}
// ### can we relax the restrictions here?
- if (c->contextType == ContextType::Eval || c->contextType == ContextType::Binding)
+ if (c->contextType == ContextType::Eval)
return result;
- result.type = ResolvedName::Global;
+ if (c->contextType == ContextType::Binding || c->contextType == ContextType::ScriptImportedByQML)
+ result.type = ResolvedName::QmlGlobal;
+ else
+ result.type = ResolvedName::Global;
return result;
}
diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h
index 5b91b93346..57ef4be36e 100644
--- a/src/qml/compiler/qv4compilercontext_p.h
+++ b/src/qml/compiler/qv4compilercontext_p.h
@@ -217,7 +217,6 @@ struct Context {
bool usesThis = false;
bool innerFunctionAccessesThis = false;
bool innerFunctionAccessesNewTarget = false;
- bool hasTry = false;
bool returnsClosure = false;
mutable bool argumentsCanEscape = false;
bool requiresExecutionContext = false;
@@ -278,11 +277,6 @@ struct Context {
}
};
- // Qml extension:
- SmallSet<int> idObjectDependencies;
- PropertyDependencyMap contextObjectPropertyDependencies;
- PropertyDependencyMap scopeObjectPropertyDependencies;
-
Context(Context *parent, ContextType type)
: parent(parent)
, contextType(type)
@@ -338,6 +332,7 @@ struct Context {
struct ResolvedName {
enum Type {
Unresolved,
+ QmlGlobal,
Global,
Local,
Stack,
diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp
index e0eaa8867b..8984b6931e 100644
--- a/src/qml/compiler/qv4compilerscanfunctions.cpp
+++ b/src/qml/compiler/qv4compilerscanfunctions.cpp
@@ -57,7 +57,8 @@ using namespace QV4::Compiler;
using namespace QQmlJS::AST;
ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType)
- : _cg(cg)
+ : QQmlJS::AST::Visitor(cg->recursionDepth())
+ , _cg(cg)
, _sourceCode(sourceCode)
, _context(nullptr)
, _allowFuncDecls(true)
@@ -96,25 +97,6 @@ void ScanFunctions::leaveEnvironment()
_context = _contextStack.isEmpty() ? nullptr : _contextStack.top();
}
-bool ScanFunctions::preVisit(Node *ast)
-{
- if (_cg->hasError)
- return false;
- ++_recursionDepth;
-
- if (_recursionDepth > 1000) {
- _cg->throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Maximum statement or expression depth exceeded"));
- return false;
- }
-
- return true;
-}
-
-void ScanFunctions::postVisit(Node *)
-{
- --_recursionDepth;
-}
-
void ScanFunctions::checkDirectivePrologue(StatementList *ast)
{
for (StatementList *it = ast; it; it = it->next) {
@@ -759,9 +741,9 @@ void ScanFunctions::calcEscapingVariables()
if (c->contextType != ContextType::ESModule)
continue;
for (const auto &entry: c->exportEntries) {
- auto m = c->members.find(entry.localName);
- if (m != c->members.end())
- m->canEscape = true;
+ auto mIt = c->members.constFind(entry.localName);
+ if (mIt != c->members.constEnd())
+ mIt->canEscape = true;
}
break;
}
@@ -777,8 +759,8 @@ void ScanFunctions::calcEscapingVariables()
}
Q_ASSERT(c != inner);
while (c) {
- Context::MemberMap::const_iterator it = c->members.find(var);
- if (it != c->members.end()) {
+ Context::MemberMap::const_iterator it = c->members.constFind(var);
+ if (it != c->members.constEnd()) {
if (c->parent || it->isLexicallyScoped()) {
it->canEscape = true;
c->requiresExecutionContext = true;
@@ -834,15 +816,17 @@ void ScanFunctions::calcEscapingVariables()
// add an escaping 'this' variable
c->addLocalVar(QStringLiteral("this"), Context::VariableDefinition, VariableScope::Let);
c->requiresExecutionContext = true;
- auto m = c->members.find(QStringLiteral("this"));
- m->canEscape = true;
+ auto mIt = c->members.constFind(QStringLiteral("this"));
+ Q_ASSERT(mIt != c->members.constEnd());
+ mIt->canEscape = true;
}
if (c->innerFunctionAccessesNewTarget) {
// add an escaping 'new.target' variable
c->addLocalVar(QStringLiteral("new.target"), Context::VariableDefinition, VariableScope::Let);
c->requiresExecutionContext = true;
- auto m = c->members.find(QStringLiteral("new.target"));
- m->canEscape = true;
+ auto mIt = c->members.constFind(QStringLiteral("new.target"));
+ Q_ASSERT(mIt != c->members.constEnd());
+ mIt->canEscape = true;
}
if (c->allVarsEscape && c->contextType == ContextType::Block && c->members.isEmpty())
c->allVarsEscape = false;
@@ -863,8 +847,9 @@ void ScanFunctions::calcEscapingVariables()
}
if (c->contextType == ContextType::Block && c->isCatchBlock) {
c->requiresExecutionContext = true;
- auto m = c->members.find(c->caughtVariable);
- m->canEscape = true;
+ auto mIt = c->members.constFind(c->caughtVariable);
+ Q_ASSERT(mIt != c->members.constEnd());
+ mIt->canEscape = true;
}
const QLatin1String exprForOn("expression for on");
if (c->contextType == ContextType::Binding && c->name.length() > exprForOn.size() &&
@@ -873,7 +858,7 @@ void ScanFunctions::calcEscapingVariables()
// we don't know if the code is a signal handler or not.
c->requiresExecutionContext = true;
if (c->allVarsEscape) {
- for (auto &m : c->members)
+ for (const auto &m : qAsConst(c->members))
m.canEscape = true;
}
}
@@ -893,3 +878,8 @@ void ScanFunctions::calcEscapingVariables()
}
}
}
+
+void ScanFunctions::throwRecursionDepthError()
+{
+ _cg->throwRecursionDepthError();
+}
diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h
index 28ad846bcd..0f7bf1818a 100644
--- a/src/qml/compiler/qv4compilerscanfunctions_p.h
+++ b/src/qml/compiler/qv4compilerscanfunctions_p.h
@@ -96,9 +96,6 @@ protected:
using Visitor::visit;
using Visitor::endVisit;
- bool preVisit(AST::Node *ast) override;
- void postVisit(AST::Node *) override;
-
void checkDirectivePrologue(AST::StatementList *ast);
void checkName(const QStringRef &name, const AST::SourceLocation &loc);
@@ -160,6 +157,8 @@ protected:
bool visit(AST::WithStatement *ast) override;
void endVisit(AST::WithStatement *ast) override;
+ void throwRecursionDepthError() override;
+
protected:
bool enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::StatementList *body, bool enterName);
@@ -173,8 +172,6 @@ protected:
bool _allowFuncDecls;
ContextType defaultProgramType;
- unsigned _recursionDepth = 0;
-
private:
static constexpr AST::Node *astNodeForGlobalEnvironment = nullptr;
};
diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp
index c0b1be1492..b019f191fa 100644
--- a/src/qml/compiler/qv4instr_moth.cpp
+++ b/src/qml/compiler/qv4instr_moth.cpp
@@ -287,6 +287,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << index << TRACE_SLOT;
MOTH_END_INSTR(LoadGlobalLookup)
+ MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup)
+ d << index << TRACE_SLOT;
+ MOTH_END_INSTR(LoadQmlContextPropertyLookup)
+
MOTH_BEGIN_INSTR(StoreNameSloppy)
d << name;
MOTH_END_INSTR(StoreNameSloppy)
@@ -328,26 +332,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(property, nFormals);
MOTH_END_INSTR(StoreSuperProperty)
- MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
- d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]";
- MOTH_END_INSTR(StoreScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadScopeObjectProperty)
- d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]" << (captureRequired ? " (capture)" : " (no capture)");
- MOTH_END_INSTR(LoadScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(StoreContextObjectProperty)
- d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]";
- MOTH_END_INSTR(StoreContextObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadContextObjectProperty)
- d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]" << (captureRequired ? " (capture)" : " (no capture)");
- MOTH_END_INSTR(LoadContextObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadIdObject)
- d << dumpRegister(base, nFormals) << "[" << index << "]";
- MOTH_END_INSTR(LoadIdObject)
-
MOTH_BEGIN_INSTR(Yield)
MOTH_END_INSTR(Yield)
@@ -394,15 +378,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
MOTH_END_INSTR(CallGlobalLookup)
- MOTH_BEGIN_INSTR(CallScopeObjectProperty)
- d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals)
- << TRACE_SLOT;
- MOTH_END_INSTR(CallScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(CallContextObjectProperty)
- d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals)
- << TRACE_SLOT;
- MOTH_END_INSTR(CallContextObjectProperty)
+ MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup)
+ d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
+ MOTH_END_INSTR(CallQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(CallWithSpread)
d << "new " << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals)
@@ -615,6 +593,7 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(UNot)
MOTH_BEGIN_INSTR(UPlus)
+ d << TRACE_SLOT;
MOTH_END_INSTR(UPlus)
MOTH_BEGIN_INSTR(UMinus)
@@ -729,14 +708,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << index;
MOTH_END_INSTR(GetTemplateObject)
- MOTH_BEGIN_INSTR(LoadQmlContext)
- d << dumpRegister(result, nFormals);
- MOTH_END_INSTR(LoadQmlContext)
-
- MOTH_BEGIN_INSTR(LoadQmlImportedScripts)
- d << dumpRegister(result, nFormals);
- MOTH_END_INSTR(LoadQmlImportedScripts)
-
MOTH_BEGIN_INSTR(TailCall)
d << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(TailCall)
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index 3996143843..6a8c9a9549 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -86,12 +86,11 @@ QT_BEGIN_NAMESPACE
#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_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_LoadScopeObjectProperty(op) INSTRUCTION(op, LoadScopeObjectProperty, 3, propertyIndex, base, captureRequired)
-#define INSTR_LoadContextObjectProperty(op) INSTRUCTION(op, LoadContextObjectProperty, 3, propertyIndex, base, captureRequired)
#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)
@@ -101,8 +100,6 @@ 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_StoreScopeObjectProperty(op) INSTRUCTION(op, StoreScopeObjectProperty, 2, base, propertyIndex)
-#define INSTR_StoreContextObjectProperty(op) INSTRUCTION(op, StoreContextObjectProperty, 2, base, propertyIndex)
#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)
@@ -113,8 +110,7 @@ QT_BEGIN_NAMESPACE
#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_CallScopeObjectProperty(op) INSTRUCTION(op, CallScopeObjectProperty, 5, name, base, argc, argv, traceSlot)
-#define INSTR_CallContextObjectProperty(op) INSTRUCTION(op, CallContextObjectProperty, 5, name, base, 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_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv)
#define INSTR_ConstructWithSpread(op) INSTRUCTION(op, ConstructWithSpread, 3, func, argc, argv)
@@ -171,7 +167,7 @@ QT_BEGIN_NAMESPACE
#define INSTR_CmpIn(op) INSTRUCTION(op, CmpIn, 1, lhs)
#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_UPlus(op) INSTRUCTION(op, UPlus, 1, traceSlot)
#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 1, traceSlot)
#define INSTR_UCompl(op) INSTRUCTION(op, UCompl, 0)
#define INSTR_Increment(op) INSTRUCTION(op, Increment, 1, traceSlot)
@@ -194,7 +190,6 @@ QT_BEGIN_NAMESPACE
#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_LoadQmlContext(op) INSTRUCTION(op, LoadQmlContext, 1, result)
#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)
@@ -228,6 +223,7 @@ QT_BEGIN_NAMESPACE
F(LoadClosure) \
F(LoadName) \
F(LoadGlobalLookup) \
+ F(LoadQmlContextPropertyLookup) \
F(StoreNameSloppy) \
F(StoreNameStrict) \
F(LoadElement) \
@@ -238,11 +234,6 @@ QT_BEGIN_NAMESPACE
F(SetLookup) \
F(LoadSuperProperty) \
F(StoreSuperProperty) \
- F(StoreScopeObjectProperty) \
- F(StoreContextObjectProperty) \
- F(LoadScopeObjectProperty) \
- F(LoadContextObjectProperty) \
- F(LoadIdObject) \
F(ConvertThisToObject) \
F(ToObject) \
F(Jump) \
@@ -296,8 +287,7 @@ QT_BEGIN_NAMESPACE
F(CallName) \
F(CallPossiblyDirectEval) \
F(CallGlobalLookup) \
- F(CallScopeObjectProperty) \
- F(CallContextObjectProperty) \
+ F(CallQmlContextPropertyLookup) \
F(CallWithSpread) \
F(Construct) \
F(ConstructWithSpread) \
@@ -328,8 +318,6 @@ QT_BEGIN_NAMESPACE
F(CreateMappedArgumentsObject) \
F(CreateUnmappedArgumentsObject) \
F(CreateRestParameter) \
- F(LoadQmlContext) \
- F(LoadQmlImportedScripts) \
F(Yield) \
F(YieldStar) \
F(Resume) \
diff --git a/src/qml/doc/snippets/qml/tablemodel/fruit-example-complex.qml b/src/qml/doc/snippets/qml/tablemodel/fruit-example-complex.qml
new file mode 100644
index 0000000000..104a2209d7
--- /dev/null
+++ b/src/qml/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/qml/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.qml b/src/qml/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.qml
index 3d44f61668..d3f6176c70 100644
--- a/src/qml/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.qml
+++ b/src/qml/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.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 documentation of the Qt Toolkit.
@@ -65,32 +65,37 @@ ApplicationWindow {
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 object (line) is one cell/column,
- // and each property in that object is a role.
- { 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: true },
- { amount: 1 },
- { fruitType: "Banana" },
- { fruitName: "Cavendish" },
- { fruitPrice: 3.50 }
- ]
+ {
+ // 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]
}
@@ -99,15 +104,15 @@ ApplicationWindow {
DelegateChoice {
column: 0
delegate: CheckBox {
- checked: model.checked
- onToggled: model.checked = checked
+ checked: model.display
+ onToggled: model.display = checked
}
}
DelegateChoice {
column: 1
delegate: SpinBox {
- value: model.amount
- onValueModified: model.amount = value
+ value: model.display
+ onValueModified: model.display = value
}
}
DelegateChoice {
diff --git a/src/qml/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.qml b/src/qml/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.qml
index 5f00eb484b..f51c1818c3 100644
--- a/src/qml/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.qml
+++ b/src/qml/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.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 documentation of the Qt Toolkit.
@@ -65,32 +65,37 @@ Window {
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 object (line) is one cell/column,
- // and each property in that object is a role.
- { 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: true },
- { amount: 1 },
- { fruitType: "Banana" },
- { fruitName: "Cavendish" },
- { fruitPrice: 3.50 }
- ]
+ {
+ // 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]
}
@@ -100,7 +105,6 @@ Window {
padding: 12
selectByMouse: true
- // TODO: the property used here is undefined
onAccepted: model.display = text
Rectangle {
diff --git a/src/qml/doc/snippets/qml/tablemodel/roleDataProvider.qml b/src/qml/doc/snippets/qml/tablemodel/roleDataProvider.qml
deleted file mode 100644
index 63978a370d..0000000000
--- a/src/qml/doc/snippets/qml/tablemodel/roleDataProvider.qml
+++ /dev/null
@@ -1,83 +0,0 @@
-/****************************************************************************
-**
-** 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.12
-import Qt.labs.qmlmodels 1.0
-
-TableView {
- columnSpacing: 1; rowSpacing: 1
- model: TableModel {
- property real taxPercent: 10
- rows: [
- [{ fruitType: "Apple" }, { fruitPrice: 1.50 }],
- [{ fruitType: "Orange" }, { fruitPrice: 2.50 }]
- ]
- roleDataProvider: function(index, role, cellData) {
- if (role === "display") {
- if (cellData.hasOwnProperty("fruitPrice")) {
- console.log("row", index.row, "taxing your fruit", JSON.stringify(cellData))
- return (cellData.fruitPrice * (1 + taxPercent / 100)).toFixed(2);
- }
- else if (cellData.hasOwnProperty("fruitType"))
- return cellData.fruitType;
- }
- return cellData;
- }
- }
- delegate: Rectangle {
- implicitWidth: 150; implicitHeight: 30
- color: "lightsteelblue"
- Text {
- x: 6; anchors.verticalCenter: parent.verticalCenter
- text: display
- }
- }
-}
-//![0]
diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc
index f6f630c749..41bc9fd140 100644
--- a/src/qml/doc/src/cppintegration/definetypes.qdoc
+++ b/src/qml/doc/src/cppintegration/definetypes.qdoc
@@ -142,9 +142,10 @@ types:
\li qmlRegisterType() (with no parameters) registers a C++ type that is not
instantiable and cannot be referred to from QML. This enables the engine to
coerce any inherited types that are instantiable from QML.
-\li qmlRegisterInterface() registers a Qt interface type with a specific QML
-type name. The type is not instantiable from QML but can be referred to by its
-type name.
+\li qmlRegisterInterface() registers an existing Qt interface type. The type is
+not instantiable from QML, and you cannot declare QML properties with it. Using
+C++ properties of this type from QML will do the expected interface casts,
+though.
\li qmlRegisterUncreatableType() registers a named C++ type that is not
instantiable but should be identifiable as a type to the QML type system. This
is useful if a type's enums or attached properties should be accessible from QML
diff --git a/src/qml/doc/src/javascript/functionlist.qdoc b/src/qml/doc/src/javascript/functionlist.qdoc
index 24ff640284..7a6a922480 100644
--- a/src/qml/doc/src/javascript/functionlist.qdoc
+++ b/src/qml/doc/src/javascript/functionlist.qdoc
@@ -55,6 +55,8 @@
\li decodeURIComponent(encodedURIComponent)
\li encodeURI(uri)
\li encodeURIComponent(uriComponent)
+ \li escape(string)
+ \li unescape(string)
\endlist
\section2 Constructor Properties
@@ -63,11 +65,20 @@
\li Object
\li Function
\li Array
+ \li ArrayBuffer
\li String
\li Boolean
\li Number
+ \li DataView
\li Date
+ \li Promise
\li RegExp
+ \li Map
+ \li WeakMap
+ \li Set
+ \li WeakSet
+ \li SharedArrayBuffer
+ \li Symbol
\li Error
\li EvalError
\li RangeError
@@ -80,8 +91,11 @@
\section2 Other Properties
\list
+ \li Atomics
\li Math
\li JSON
+ \li Reflect
+ \li Proxy
\endlist
\section1 The Object Object
@@ -92,12 +106,19 @@
\list
\li getPrototypeOf(O)
+ \li setPrototypeOf(O, P)
\li getOwnPropertyDescriptor(O, P)
+ \li getOwnPropertyDescriptors(O)
\li getOwnPropertyNames(O)
+ \li getOwnPropertySymbols(O)
+ \li assign(O [, Properties])
\li create(O [, Properties])
\li defineProperty(O, P, Attributes)
\li defineProperties(O, Properties)
+ \li entries(O)
+ \li is(V1, V2)
\li keys(O)
+ \li values(O)
\li seal(O)
\li isSealed(O)
\li freeze(O)
@@ -117,6 +138,8 @@
\li hasOwnProperty(V)
\li isPrototypeOf(V)
\li propertyIsEnumerable(V)
+ \li __defineGetter__(P, F)
+ \li __defineSetter__(P, F)
\endlist
\section1 Function Objects
@@ -130,6 +153,7 @@
\li apply(thisArg, argArray)
\li call(thisArg [, arg1 [, arg2, ...]])
\li bind((thisArg [, arg1 [, arg2, …]])
+ \li [Symbol.hasInstance](O)
\endlist
\section1 Array Objects
@@ -142,9 +166,14 @@
\li toString()
\li toLocaleString()
\li concat([item1 [, item2 [, ...]]])
+ \li copyWithin([item1 [, item2 [, ...]]])
+ \li entries()
+ \li fill(item [, index1 [, index2]])
\li join(separator)
\li find(callbackfn [, thisArg]) // ECMAScript 6: Added in Qt 5.9
\li findIndex(callbackfn [, thisArg]) // ECMAScript 6: Added in Qt 5.9
+ \li includes(item)
+ \li keys()
\li pop()
\li push([item1 [, item2 [, ...]]])
\li reverse()
@@ -162,6 +191,8 @@
\li filter(callbackfn [, thisArg])
\li reduce(callbackfn [, initialValue])
\li reduceRight(callbackfn [, initialValue])
+ \li values()
+ \li [Symbol.iterator]()
\endlist
\section1 String Objects
@@ -175,6 +206,7 @@
\li valueOf()
\li charAt(pos)
\li charCodeAt(pos)
+ \li codePointAt(pos)
\li concat([string1 [, string2 [, ...]]])
\li endsWith(searchString [, endPosition ]) // ECMAScript 6: Added in Qt 5.8
\li includes(searchString [, position ]) // ECMAScript 6: Added in 5.8
@@ -182,18 +214,23 @@
\li lastIndexOf(searchString, position)
\li localeCompare(that)
\li match(regexp)
+ \li normalize()
+ \li padEnd(length [, string])
+ \li padStart(length [, string])
\li repeat(count) // ECMAScript 6: Added in Qt 5.9
\li replace(searchValue, replaceValue)
\li search(regexp)
\li slice(start, end)
\li split(separator, limit)
\li startsWith(searchString [, position ]) // ECMAScript 6: Added in Qt 5.8
+ \li substr(start, length)
\li substring(start, end)
\li toLowerCase()
\li toLocaleLowerCase()
\li toUpperCase()
\li toLocaleUpperCase()
\li trim()
+ \li [Symbol.iterator]()
\endlist
Additionally, the QML engine adds the following functions to the \l String prototype:
@@ -222,6 +259,7 @@
\list
\li toString(radix)
\li toLocaleString()
+ \li valueOf()
\li toFixed(fractionDigits)
\li toExponential(fractionDigits)
\li toPrecision(precision)
@@ -245,12 +283,16 @@
\li MAX_VALUE
\li MIN_VALUE
\li EPSILON // ECMAScript 6: Added in Qt 5.8
+ \li MAX_SAFE_INTEGER
+ \li MIN_SAFE_INTEGER
\endlist
\section3 Function Properties
\list
\li isFinite(x) // ECMAScript 6: Added in Qt 5.8
+ \li isInteger(x)
+ \li isSafeInteger(x)
\li isNaN(x) // ECMAScript 6: Added in Qt 5.8
\endlist
@@ -274,14 +316,27 @@
\list
\li abs(x)
\li acos(x)
+ \li acosh(x)
\li asin(x)
+ \li asinh(x)
\li atan(x)
+ \li atanh(x)
\li atan2(y, x)
+ \li cbrt(x)
\li ceil(x)
+ \li clz32(x)
\li cos(x)
+ \li cosh(x)
\li exp(x)
+ \li expm1(x)
\li floor(x)
+ \li fround(x)
+ \li hypot(x, y)
+ \li imul(x, y)
\li log(x)
+ \li log10(x)
+ \li log1p(x)
+ \li log2(x)
\li max([value1 [, value2 [, ...]]])
\li min([value1 [, value2 [, ...]]])
\li pow(x, y)
@@ -289,8 +344,11 @@
\li round(x)
\li sign(x) // ECMAScript 6: Added in Qt 5.8
\li sin(x)
+ \li sinh(x)
\li sqrt(x)
\li tan(x)
+ \li tanh(x)
+ \li trunc(x)
\endlist
\section1 Date Objects
@@ -338,11 +396,14 @@
\li setUTCDate(date)
\li setMonth(month [, date])
\li setUTCMonth(month [, date])
+ \li setYear(year)
\li setFullYear(year [, month [, date]])
\li setUTCFullYear(year [, month [, date]])
\li toUTCString()
+ \li toGMTString()
\li toISOString()
\li toJSON()
+ \li [Symbol.toPrimitive](hint)
\endlist
Additionally, the QML engine adds the following functions to the \l Date prototype:
diff --git a/src/qml/doc/src/javascript/hostenvironment.qdoc b/src/qml/doc/src/javascript/hostenvironment.qdoc
index eb40f10065..c22c392b80 100644
--- a/src/qml/doc/src/javascript/hostenvironment.qdoc
+++ b/src/qml/doc/src/javascript/hostenvironment.qdoc
@@ -40,11 +40,10 @@ not provide a \c window object or \c{DOM API} as commonly found in a browser env
Like a browser or server-side JavaScript environment, the QML runtime implements the
\l{ECMA-262}{ECMAScript Language Specification} standard. This provides access to
all of the built-in types and functions defined by the standard, such as Object, Array, and Math.
-The QML runtime implements the 5th edition of the standard, which is the same edition commonly
-implemented by browsers.
+The QML runtime implements the 7th edition of the standard.
The standard ECMAScript built-ins are not explicitly documented in the QML documentation. For more
-information on their use, please refer to the ECMA-262 5th edition standard or one of the many online
+information on their use, please refer to the ECMA-262 7th edition standard or one of the many online
JavaScript reference and tutorial sites, such as the \l{W3Schools JavaScript Reference} (JavaScript Objects
Reference section). Many sites focus on JavaScript in the browser, so in some cases you may need to double
check the specification to determine whether a given function or object is part of standard ECMAScript or
diff --git a/src/qml/doc/src/javascript/number.qdoc b/src/qml/doc/src/javascript/number.qdoc
index b6f80f474a..288232255c 100644
--- a/src/qml/doc/src/javascript/number.qdoc
+++ b/src/qml/doc/src/javascript/number.qdoc
@@ -96,7 +96,7 @@
\code
var german = Qt.locale("de_DE");
var d;
- d = Number.fromLocaleString(german, "1234,56) // d == 1234.56
+ d = Number.fromLocaleString(german, "1234,56") // d == 1234.56
d = Number.fromLocaleString(german, "1.234,56") // d == 1234.56
d = Number.fromLocaleString(german, "1234.56") // throws exception
d = Number.fromLocaleString(german, "1.234") // d == 1234.0
diff --git a/src/qml/doc/src/statemachine.qdoc b/src/qml/doc/src/statemachine.qdoc
index 6986f1baa0..231b85af76 100644
--- a/src/qml/doc/src/statemachine.qdoc
+++ b/src/qml/doc/src/statemachine.qdoc
@@ -27,7 +27,7 @@
/*!
\qmlmodule QtQml.StateMachine 1.\QtMinorVersion
- \title Declarative State Machine QML Types
+ \title Qt QML State Machine QML Types
\brief Provides QML types to create and execute state graphs.
The following is a list of QML types provided by the module:
@@ -322,7 +322,7 @@
\section1 Related Information
\list
- \li \l{Declarative State Machine QML Types}
+ \li \l{Qt QML State Machine QML Types}
\li \l{The State Machine Framework}
\endlist
*/
diff --git a/src/qml/jit/jit.pri b/src/qml/jit/jit.pri
index 49eb2e8a37..bc0d20dba7 100644
--- a/src/qml/jit/jit.pri
+++ b/src/qml/jit/jit.pri
@@ -18,7 +18,13 @@ SOURCES += \
$$PWD/qv4node.cpp \
$$PWD/qv4graph.cpp \
$$PWD/qv4graphbuilder.cpp \
+ $$PWD/qv4lowering.cpp \
$$PWD/qv4tracingjit.cpp \
+ $$PWD/qv4mi.cpp \
+ $$PWD/qv4domtree.cpp \
+ $$PWD/qv4schedulers.cpp \
+ $$PWD/qv4blockscheduler.cpp \
+ $$PWD/qv4loopinfo.cpp
HEADERS += \
$$PWD/qv4ir_p.h \
@@ -27,4 +33,11 @@ HEADERS += \
$$PWD/qv4node_p.h \
$$PWD/qv4graph_p.h \
$$PWD/qv4graphbuilder_p.h \
+ $$PWD/qv4lowering_p.h \
+ $$PWD/qv4mi_p.h \
+ $$PWD/qv4miblockset_p.h \
+ $$PWD/qv4domtree_p.h \
+ $$PWD/qv4schedulers_p.h \
+ $$PWD/qv4blockscheduler_p.h \
+ $$PWD/qv4loopinfo_p.h
}
diff --git a/src/qml/jit/qv4baselineassembler.cpp b/src/qml/jit/qv4baselineassembler.cpp
index 1b60e96f2c..238c11f478 100644
--- a/src/qml/jit/qv4baselineassembler.cpp
+++ b/src/qml/jit/qv4baselineassembler.cpp
@@ -208,17 +208,20 @@ public:
isNumber.link(this);
}
+ // this converts both the lhs and the accumulator to int32
void toInt32LhsAcc(Address lhs, RegisterID lhsTarget)
{
load64(lhs, lhsTarget);
urshift64(lhsTarget, TrustedImm32(Value::QuickType_Shift), ScratchRegister2);
auto lhsIsInt = branch32(Equal, TrustedImm32(Value::QT_Int), ScratchRegister2);
- pushAligned(AccumulatorRegister);
+ const Address accumulatorStackAddress(JSStackFrameRegister,
+ offsetof(CallData, accumulator));
+ storeAccumulator(accumulatorStackAddress);
move(lhsTarget, registerForArg(0));
callHelper(toInt32Helper);
move(ReturnValueRegister, lhsTarget);
- popAligned(AccumulatorRegister);
+ loadAccumulator(accumulatorStackAddress);
lhsIsInt.link(this);
urshift64(AccumulatorRegister, TrustedImm32(Value::QuickType_Shift), ScratchRegister2);
@@ -498,6 +501,7 @@ public:
isNumber.link(this);
}
+ // this converts both the lhs and the accumulator to int32
void toInt32LhsAcc(Address lhs, RegisterID lhsTarget)
{
bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue
@@ -510,32 +514,28 @@ public:
auto lhsIsInt = jump();
lhsIsNotInt.link(this);
- if (accumulatorNeedsSaving) {
- push(AccumulatorRegisterTag);
- push(AccumulatorRegisterValue);
- }
+
+ // Save accumulator from being garbage collected, no matter if we will reuse the register.
+ const Address accumulatorStackAddress(JSStackFrameRegister,
+ offsetof(CallData, accumulator));
+ storeAccumulator(accumulatorStackAddress);
if (ArgInRegCount < 2) {
- if (!accumulatorNeedsSaving)
- subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
+ subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
push(lhsTarget);
load32(lhs, lhsTarget);
push(lhsTarget);
} else {
- if (accumulatorNeedsSaving)
- subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
move(lhsTarget, registerForArg(1));
load32(lhs, registerForArg(0));
}
callHelper(toInt32Helper);
move(ReturnValueRegisterValue, lhsTarget);
- if (accumulatorNeedsSaving) {
- addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
- pop(AccumulatorRegisterValue);
- pop(AccumulatorRegisterTag);
- } else if (ArgInRegCount < 2) {
+ if (ArgInRegCount < 2)
addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
- }
+
+ if (accumulatorNeedsSaving) // otherwise it's still the same
+ loadAccumulator(accumulatorStackAddress);
lhsIsInt.link(this);
diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp
index f2f7a12598..80155d7b20 100644
--- a/src/qml/jit/qv4baselinejit.cpp
+++ b/src/qml/jit/qv4baselinejit.cpp
@@ -213,6 +213,14 @@ void BaselineJIT::generate_LoadGlobalLookup(int index, int /*traceSlot*/)
BASELINEJIT_GENERATE_RUNTIME_CALL(LoadGlobalLookup, CallResultDestination::InAccumulator);
}
+void BaselineJIT::generate_LoadQmlContextPropertyLookup(int index, int /*traceSlot*/)
+{
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(index, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlContextPropertyLookup, CallResultDestination::InAccumulator);
+}
+
void BaselineJIT::generate_StoreNameSloppy(int name)
{
STORE_IP();
@@ -329,61 +337,6 @@ void BaselineJIT::generate_StoreSuperProperty(int property)
BASELINEJIT_GENERATE_RUNTIME_CALL(StoreSuperProperty, CallResultDestination::Ignore);
}
-
-void BaselineJIT::generate_StoreScopeObjectProperty(int base, int propertyIndex)
-{
- STORE_ACC();
- as->prepareCallWithArgCount(4);
- as->passAccumulatorAsArg(3);
- as->passInt32AsArg(propertyIndex, 2);
- as->passJSSlotAsArg(base, 1);
- as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(StoreQmlScopeObjectProperty, CallResultDestination::Ignore);
-}
-
-void BaselineJIT::generate_StoreContextObjectProperty(int base, int propertyIndex)
-{
- STORE_ACC();
- as->prepareCallWithArgCount(4);
- as->passAccumulatorAsArg(3);
- as->passInt32AsArg(propertyIndex, 2);
- as->passJSSlotAsArg(base, 1);
- as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(StoreQmlContextObjectProperty, CallResultDestination::Ignore);
-}
-
-void BaselineJIT::generate_LoadScopeObjectProperty(int propertyIndex, int base, int captureRequired)
-{
- STORE_IP();
- as->prepareCallWithArgCount(4);
- as->passInt32AsArg(captureRequired, 3);
- as->passInt32AsArg(propertyIndex, 2);
- as->passJSSlotAsArg(base, 1);
- as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlScopeObjectProperty, CallResultDestination::InAccumulator);
-}
-
-void BaselineJIT::generate_LoadContextObjectProperty(int propertyIndex, int base, int captureRequired)
-{
- STORE_IP();
- as->prepareCallWithArgCount(4);
- as->passInt32AsArg(captureRequired, 3);
- as->passInt32AsArg(propertyIndex, 2);
- as->passJSSlotAsArg(base, 1);
- as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlContextObjectProperty, CallResultDestination::InAccumulator);
-}
-
-void BaselineJIT::generate_LoadIdObject(int index, int base)
-{
- STORE_IP();
- as->prepareCallWithArgCount(3);
- as->passInt32AsArg(index, 2);
- as->passJSSlotAsArg(base, 1);
- as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlIdObject, CallResultDestination::InAccumulator);
-}
-
void BaselineJIT::generate_Yield()
{
// #####
@@ -493,31 +446,18 @@ void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv, int /
BASELINEJIT_GENERATE_RUNTIME_CALL(CallGlobalLookup, CallResultDestination::InAccumulator);
}
-void BaselineJIT::generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv, int /*traceSlot*/)
-{
- STORE_IP();
- as->prepareCallWithArgCount(5);
- as->passInt32AsArg(argc, 4);
- as->passJSSlotAsArg(argv, 3);
- as->passInt32AsArg(propIdx, 2);
- as->passJSSlotAsArg(base, 1);
- as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(CallQmlScopeObjectProperty, CallResultDestination::InAccumulator);
-}
-
-void BaselineJIT::generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv, int /*traceSlot*/)
+void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int argv,
+ int /*traceSlot*/)
{
STORE_IP();
- as->prepareCallWithArgCount(5);
- as->passInt32AsArg(argc, 4);
- as->passJSSlotAsArg(argv, 3);
- as->passInt32AsArg(propIdx, 2);
- as->passJSSlotAsArg(base, 1);
+ as->prepareCallWithArgCount(4);
+ as->passInt32AsArg(argc, 3);
+ as->passJSSlotAsArg(argv, 2);
+ as->passInt32AsArg(index, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(CallQmlContextObjectProperty, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallQmlContextPropertyLookup, CallResultDestination::InAccumulator);
}
-
void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv, int /*traceSlot*/)
{
STORE_IP();
@@ -891,7 +831,7 @@ void BaselineJIT::generate_CmpInstanceOf(int lhs)
}
void BaselineJIT::generate_UNot() { as->unot(); }
-void BaselineJIT::generate_UPlus() { as->toNumber(); }
+void BaselineJIT::generate_UPlus(int /*traceSlot*/) { as->toNumber(); }
void BaselineJIT::generate_UMinus(int /*traceSlot*/) { as->uminus(); }
void BaselineJIT::generate_UCompl() { as->ucompl(); }
void BaselineJIT::generate_Increment(int /*traceSlot*/) { as->inc(); }
@@ -937,22 +877,6 @@ void BaselineJIT::generate_Sub(int lhs, int /*traceSlot*/) { as->sub(lhs); }
// as->checkException();
//}
-void BaselineJIT::generate_LoadQmlContext(int result)
-{
- as->prepareCallWithArgCount(1);
- as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlContext, CallResultDestination::InAccumulator);
- as->storeReg(result);
-}
-
-void BaselineJIT::generate_LoadQmlImportedScripts(int result)
-{
- as->prepareCallWithArgCount(1);
- as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlImportedScripts, CallResultDestination::InAccumulator);
- as->storeReg(result);
-}
-
void BaselineJIT::generate_InitializeBlockDeadTemporalZone(int firstReg, int count)
{
as->loadValue(Value::emptyValue().rawValue());
diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h
index 5f3cb7eb2e..37ab37eac2 100644
--- a/src/qml/jit/qv4baselinejit_p.h
+++ b/src/qml/jit/qv4baselinejit_p.h
@@ -97,6 +97,7 @@ public:
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_StoreNameSloppy(int name) override;
void generate_StoreNameStrict(int name) override;
void generate_LoadElement(int base, int traceSlot) override;
@@ -107,15 +108,6 @@ public:
void generate_SetLookup(int index, int base) override;
void generate_LoadSuperProperty(int property) override;
void generate_StoreSuperProperty(int property) override;
- void generate_StoreScopeObjectProperty(int base,
- int propertyIndex) override;
- void generate_StoreContextObjectProperty(int base,
- int propertyIndex) override;
- void generate_LoadScopeObjectProperty(int propertyIndex, int base,
- int captureRequired) override;
- void generate_LoadContextObjectProperty(int propertyIndex, int base,
- int captureRequired) override;
- void generate_LoadIdObject(int index, int base) override;
void generate_Yield() override;
void generate_YieldStar() override;
void generate_Resume(int) override;
@@ -128,8 +120,7 @@ public:
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_CallScopeObjectProperty(int propIdx, int base, int argc, int argv, int traceSlot) override;
- void generate_CallContextObjectProperty(int propIdx, int base, 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_TailCall(int func, int thisObject, int argc, int argv) override;
void generate_Construct(int func, int argc, int argv) override;
@@ -188,7 +179,7 @@ public:
void generate_CmpIn(int lhs) override;
void generate_CmpInstanceOf(int lhs) override;
void generate_UNot() override;
- void generate_UPlus() override;
+ void generate_UPlus(int) override;
void generate_UMinus(int traceSlot) override;
void generate_UCompl() override;
void generate_Increment(int traceSlot) override;
@@ -211,8 +202,6 @@ public:
void generate_Div(int lhs) override;
void generate_Mod(int lhs, int traceSlot) override;
void generate_Sub(int lhs, int traceSlot) override;
- void generate_LoadQmlContext(int result) override;
- void generate_LoadQmlImportedScripts(int result) override;
void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override;
void generate_ThrowOnNullOrUndefined() override;
void generate_GetTemplateObject(int index) override;
diff --git a/src/qml/jit/qv4blockscheduler.cpp b/src/qml/jit/qv4blockscheduler.cpp
new file mode 100644
index 0000000000..3e2bfe15c5
--- /dev/null
+++ b/src/qml/jit/qv4blockscheduler.cpp
@@ -0,0 +1,208 @@
+/****************************************************************************
+**
+** 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 <QtCore/qloggingcategory.h>
+
+#include "qv4blockscheduler_p.h"
+#include "qv4domtree_p.h"
+#include "qv4loopinfo_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcBlockScheduler, "qt.v4.ir.blockscheduler")
+
+bool BlockScheduler::checkCandidate(MIBlock *candidate)
+{
+ Q_ASSERT(loopInfo.loopHeaderFor(candidate) == currentGroup.group);
+
+ for (MIBlock *pred : candidate->inEdges()) {
+ if (pred->isDeoptBlock())
+ continue;
+
+ if (emitted.alreadyProcessed(pred))
+ continue;
+
+ if (dominatorTree.dominates(candidate->index(), pred->index())) {
+ // this is a loop, where there in
+ // -> candidate edge is the jump back to the top of the loop.
+ continue;
+ }
+
+ if (pred == candidate)
+ // this is a very tight loop, e.g.:
+ // L1: ...
+ // goto L1
+ // This can happen when, for example, the basic-block merging gets rid of the empty
+ // body block. In this case, we can safely schedule this block (if all other
+ // incoming edges are either loop-back edges, or have been scheduled already).
+ continue;
+
+ return false; // an incoming edge that is not yet emitted, and is not a back-edge
+ }
+
+ if (loopInfo.isLoopHeader(candidate)) {
+ // postpone everything, and schedule the loop first.
+ postponedGroups.push(currentGroup);
+ currentGroup = WorkForGroup(candidate);
+ }
+
+ return true;
+}
+
+MIBlock *BlockScheduler::pickNext()
+{
+ while (true) {
+ while (currentGroup.postponed.isEmpty()) {
+ if (postponedGroups.isEmpty())
+ return nullptr;
+ if (currentGroup.group) // record the first and the last node of a group
+ loopsStartEnd[currentGroup.group] = sequence.back();
+ currentGroup = postponedGroups.pop();
+ }
+
+ MIBlock *next = currentGroup.postponed.pop();
+ if (checkCandidate(next))
+ return next;
+ }
+
+ Q_UNREACHABLE();
+ return nullptr;
+}
+
+void BlockScheduler::emitBlock(MIBlock *bb)
+{
+ if (emitted.alreadyProcessed(bb))
+ return;
+
+ sequence.push_back(bb);
+ emitted.markAsProcessed(bb);
+}
+
+void BlockScheduler::schedule(MIBlock *functionEntryPoint)
+{
+ MIBlock *next = functionEntryPoint;
+
+ while (next) {
+ emitBlock(next);
+ // postpone all outgoing edges, if they were not already processed
+ QVarLengthArray<MIBlock *, 32> nonExceptionEdges;
+ // first postpone all exception edges, so they will be processed last
+ for (int i = next->outEdges().size(); i != 0; ) {
+ --i;
+ MIBlock *out = next->outEdges().at(i);
+ if (emitted.alreadyProcessed(out))
+ continue;
+ if (out == nullptr)
+ continue;
+ if (out->instructions().front().opcode() == Meta::OnException)
+ postpone(out);
+ else
+ nonExceptionEdges.append(out);
+ }
+ for (MIBlock *edge : nonExceptionEdges)
+ postpone(edge);
+ next = pickNext();
+ }
+
+ // finally schedule all de-optimization blocks at the end
+ for (auto bb : dominatorTree.function()->blocks()) {
+ if (bb->isDeoptBlock())
+ emitBlock(bb);
+ }
+}
+
+void BlockScheduler::postpone(MIBlock *bb)
+{
+ if (currentGroup.group == loopInfo.loopHeaderFor(bb)) {
+ currentGroup.postponed.append(bb);
+ return;
+ }
+
+ for (int i = postponedGroups.size(); i != 0; ) {
+ --i;
+ WorkForGroup &g = postponedGroups[i];
+ if (g.group == loopInfo.loopHeaderFor(bb)) {
+ g.postponed.append(bb);
+ return;
+ }
+ }
+
+ Q_UNREACHABLE();
+}
+
+void BlockScheduler::dump() const
+{
+ if (!lcBlockScheduler().isDebugEnabled())
+ return;
+
+ QString s = QStringLiteral("Scheduled blocks:\n");
+ for (auto *bb : sequence) {
+ s += QLatin1String(" L") + QString::number(bb->index());
+ MIBlock *loopEnd = loopsStartEnd[bb];
+ if (loopEnd)
+ s += QLatin1String(", loop start, ends at L") + QString::number(loopEnd->index());
+ s += QLatin1Char('\n');
+ }
+ qCDebug(lcBlockScheduler).noquote().nospace() << s;
+}
+
+BlockScheduler::BlockScheduler(const DominatorTree &dominatorTree, const LoopInfo &loopInfo)
+ : dominatorTree(dominatorTree)
+ , loopInfo(loopInfo)
+ , sequence(0)
+ , emitted(dominatorTree.function())
+{
+ schedule(dominatorTree.function()->blocks().front());
+
+ dump();
+
+ if (dominatorTree.function()->blockCount() != sequence.size()) {
+ qFatal("The block scheduler did not schedule all blocks. This is most likely due to"
+ "a non-natural loop.");
+ // Usually caused by having an execution path that manages to skip over unwind handler
+ // reset: any exception happening after will jump back to the unwind handler, and thereby
+ // creating a loop that can be entered in 2 different ways.
+ }
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4blockscheduler_p.h b/src/qml/jit/qv4blockscheduler_p.h
new file mode 100644
index 0000000000..1289fda9f0
--- /dev/null
+++ b/src/qml/jit/qv4blockscheduler_p.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** 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 QV4BLOCKSCHEDULER_P_H
+#define QV4BLOCKSCHEDULER_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/qstack.h>
+
+#include "qv4mi_p.h"
+#include "qv4util_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+class DominatorTree;
+class LoopInfo;
+
+// High-level algorithm:
+// 0. start with the first node (the start node) of a function
+// 1. emit the node
+// 2. add all outgoing edges that are not yet emitted to the postponed stack
+// 3. When the postponed stack is empty, pop a stack from the loop stack. If that is empty too,
+// we're done.
+// 4. pop a node from the postponed stack, and check if it can be scheduled:
+// a. if all incoming edges are scheduled, go to 4.
+// b. if an incoming edge is unscheduled, but it's a back-edge (an edge in a loop that jumps
+// back to the start of the loop), ignore it
+// c. if there is any unscheduled edge that is not a back-edge, ignore this node, and go to 4.
+// 5. if this node is the start of a loop, push the postponed stack on the loop stack.
+// 6. go back to 1.
+//
+// The postponing action in step 2 will put the node into its containing group. The case where this
+// is important is when a (labeled) continue or a (labeled) break statement occur in a loop: the
+// outgoing edge points to a node that is not part of the current loop (and possibly not of the
+// parent loop).
+//
+// Linear scan register allocation benefits greatly from short life-time intervals with few holes
+// (see for example section 4 (Lifetime Analysis) of [Wimmer1]). This algorithm makes sure that the
+// blocks of a group are scheduled together, with no non-loop blocks in between. This applies
+// recursively for nested loops. It also schedules groups of if-then-else-endif blocks together for
+// the same reason.
+class BlockScheduler
+{
+ const DominatorTree &dominatorTree;
+ const LoopInfo &loopInfo;
+
+ struct WorkForGroup
+ {
+ MIBlock *group;
+ QStack<MIBlock *> postponed;
+
+ WorkForGroup(MIBlock *group = nullptr) : group(group) {}
+ };
+ WorkForGroup currentGroup;
+ QStack<WorkForGroup> postponedGroups;
+ std::vector<MIBlock *> sequence;
+
+ class ProcessedBlocks
+ {
+ BitVector processed;
+
+ public:
+ ProcessedBlocks(MIFunction *function)
+ : processed(int(function->blockCount()), false)
+ {}
+
+ bool alreadyProcessed(MIBlock *bb) const
+ {
+ Q_ASSERT(bb);
+
+ return processed.at(bb->index());
+ }
+
+ void markAsProcessed(MIBlock *bb)
+ {
+ processed.setBit(bb->index());
+ }
+ } emitted;
+ QHash<MIBlock *, MIBlock *> loopsStartEnd;
+
+ bool checkCandidate(MIBlock *candidate);
+ MIBlock *pickNext();
+ void emitBlock(MIBlock *bb);
+ void schedule(MIBlock *functionEntryPoint);
+ void postpone(MIBlock *bb);
+ void dump() const;
+
+public:
+ BlockScheduler(const DominatorTree &dominatorTree, const LoopInfo &loopInfo);
+
+ const std::vector<MIBlock *> &scheduledBlockSequence() const
+ { return sequence; }
+
+ QHash<MIBlock *, MIBlock *> loopEndsByStartBlock() const
+ { return loopsStartEnd; }
+};
+
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4BLOCKSCHEDULER_P_H
diff --git a/src/qml/jit/qv4domtree.cpp b/src/qml/jit/qv4domtree.cpp
new file mode 100644
index 0000000000..9484f4e2dc
--- /dev/null
+++ b/src/qml/jit/qv4domtree.cpp
@@ -0,0 +1,436 @@
+/****************************************************************************
+**
+** 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 <QtCore/qloggingcategory.h>
+#include <QtCore/qbuffer.h>
+
+#include "qv4domtree_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcDomTree, "qt.v4.ir.domTree")
+Q_LOGGING_CATEGORY(lcDomFrontier, "qt.v4.ir.domFrontier")
+
+DominatorTree::DominatorTree(MIFunction *f)
+ : m_function(f)
+ , m_data(new Data)
+{
+ calculateIDoms();
+ m_data.reset();
+}
+
+void DominatorTree::dumpImmediateDominators() const
+{
+ QBuffer buf;
+ buf.open(QIODevice::WriteOnly);
+ QTextStream qout(&buf);
+ qout << "Immediate dominators for " << m_function->irFunction()->name() << ":" << endl;
+ for (MIBlock *to : m_function->blocks()) {
+ MIBlock::Index from = m_idom.at(to->index());
+ if (from != MIBlock::InvalidIndex)
+ qout << " " << from;
+ else
+ qout << " (none)";
+ qout << " dominates " << to->index() << endl;
+ }
+ qCDebug(lcDomTree, "%s", buf.data().constData());
+}
+
+void DominatorTree::setImmediateDominator(MIBlock::Index dominated, MIBlock::Index dominator)
+{
+ if (m_idom.size() <= size_t(dominated))
+ m_idom.resize(dominated + 1);
+ m_idom[dominated] = dominator;
+}
+
+bool DominatorTree::dominates(MIBlock::Index dominator, MIBlock::Index dominated) const
+{
+ // dominator can be Invalid when the dominated block has no dominator (i.e. the start node)
+ Q_ASSERT(dominated != MIBlock::InvalidIndex);
+
+ if (dominator == dominated)
+ return false;
+
+ for (MIBlock::Index it = m_idom[dominated]; it != MIBlock::InvalidIndex; it = m_idom[it]) {
+ if (it == dominator)
+ return true;
+ }
+
+ return false;
+}
+
+// Calculate a depth-first iteration order on the nodes of the dominator tree.
+//
+// The order of the nodes in the vector is not the same as one where a recursive depth-first
+// iteration is done on a tree. Rather, the nodes are (reverse) sorted on tree depth.
+// So for the:
+// 1 dominates 2
+// 2 dominates 3
+// 3 dominates 4
+// 2 dominates 5
+// the order will be:
+// 4, 3, 5, 2, 1
+// or:
+// 4, 5, 3, 2, 1
+// So the order of nodes on the same depth is undefined, but it will be after the nodes
+// they dominate, and before the nodes that dominate them.
+//
+// The reason for this order is that a proper DFS pre-/post-order would require inverting
+// the idom vector by either building a real tree datastructure or by searching the idoms
+// for siblings and children. Both have a higher time complexity than sorting by depth.
+std::vector<MIBlock *> DominatorTree::calculateDFNodeIterOrder() const
+{
+ std::vector<int> depths = calculateNodeDepths();
+ std::vector<MIBlock *> order = m_function->blocks();
+ std::sort(order.begin(), order.end(), [&depths](MIBlock *one, MIBlock *two) -> bool {
+ return depths.at(one->index()) > depths.at(two->index());
+ });
+ return order;
+}
+
+// Algorithm:
+// - for each node:
+// - get the depth of a node. If it's unknown (-1):
+// - get the depth of the immediate dominator.
+// - if that's unknown too, calculate it by calling calculateNodeDepth
+// - set the current node's depth to that of immediate dominator + 1
+std::vector<int> DominatorTree::calculateNodeDepths() const
+{
+ std::vector<int> nodeDepths(size_t(m_function->blockCount()), -1);
+ for (MIBlock *bb : m_function->blocks()) {
+ int &bbDepth = nodeDepths[bb->index()];
+ if (bbDepth == -1) {
+ const int immDom = m_idom[bb->index()];
+ if (immDom == -1) {
+ // no immediate dominator, so it's either the start block, or an unreachable block
+ bbDepth = 0;
+ } else {
+ int immDomDepth = nodeDepths[immDom];
+ if (immDomDepth == -1)
+ immDomDepth = calculateNodeDepth(immDom, nodeDepths);
+ bbDepth = immDomDepth + 1;
+ }
+ }
+ }
+ return nodeDepths;
+}
+
+// Algorithm:
+// - search for the first dominator of a node that has a known depth. As all nodes are
+// reachable from the start node, and that node's depth is 0, this is finite.
+// - while doing that search, put all unknown nodes in the worklist
+// - pop all nodes from the worklist, and set their depth to the previous' (== dominating)
+// node's depth + 1
+// This way every node's depth is calculated once, and the complexity is O(n).
+int DominatorTree::calculateNodeDepth(MIBlock::Index nodeIdx, std::vector<int> &nodeDepths) const
+{
+ std::vector<int> worklist;
+ worklist.reserve(8);
+ int depth = -1;
+
+ do {
+ worklist.push_back(nodeIdx);
+ nodeIdx = m_idom[nodeIdx];
+ depth = nodeDepths[nodeIdx];
+ } while (depth == -1);
+
+ for (auto it = worklist.rbegin(), eit = worklist.rend(); it != eit; ++it)
+ nodeDepths[*it] = ++depth;
+
+ return depth;
+}
+
+namespace {
+struct DFSTodo {
+ MIBlock::Index node = MIBlock::InvalidIndex;
+ MIBlock::Index parent = MIBlock::InvalidIndex;
+
+ DFSTodo() = default;
+ DFSTodo(MIBlock::Index node, MIBlock::Index parent)
+ : node(node)
+ , parent(parent)
+ {}
+};
+} // anonymous namespace
+
+void DominatorTree::dfs(MIBlock::Index node)
+{
+ std::vector<DFSTodo> worklist;
+ worklist.reserve(m_data->vertex.capacity() / 2);
+ DFSTodo todo(node, MIBlock::InvalidIndex);
+
+ while (true) {
+ MIBlock::Index n = todo.node;
+
+ if (m_data->dfnum[n] == 0) {
+ m_data->dfnum[n] = m_data->size;
+ m_data->vertex[m_data->size] = n;
+ m_data->parent[n] = todo.parent;
+ ++m_data->size;
+
+ MIBlock::OutEdges out = m_function->block(n)->outEdges();
+ for (int i = out.size() - 1; i > 0; --i)
+ worklist.emplace_back(out[i]->index(), n);
+
+ if (!out.isEmpty()) {
+ todo.node = out.first()->index();
+ todo.parent = n;
+ continue;
+ }
+ }
+
+ if (worklist.empty())
+ break;
+
+ todo = worklist.back();
+ worklist.pop_back();
+ }
+}
+
+void DominatorTree::link(MIBlock::Index p, MIBlock::Index n)
+{
+ m_data->ancestor[n] = p;
+ m_data->best[n] = n;
+}
+
+void DominatorTree::calculateIDoms()
+{
+ Q_ASSERT(m_function->block(0)->inEdges().count() == 0);
+
+ const size_t bbCount = m_function->blockCount();
+ m_data->vertex = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+ m_data->parent = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+ m_data->dfnum = std::vector<unsigned>(bbCount, 0);
+ m_data->semi = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+ m_data->ancestor = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+ m_idom = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+ m_data->samedom = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+ m_data->best = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+
+ QHash<MIBlock::Index, std::vector<MIBlock::Index>> bucket;
+ bucket.reserve(int(bbCount));
+
+ dfs(m_function->block(0)->index());
+
+ std::vector<MIBlock::Index> worklist;
+ worklist.reserve(m_data->vertex.capacity() / 2);
+
+ for (int i = m_data->size - 1; i > 0; --i) {
+ MIBlock::Index n = m_data->vertex[i];
+ MIBlock::Index p = m_data->parent[n];
+ MIBlock::Index s = p;
+
+ for (auto inEdge : m_function->block(n)->inEdges()) {
+ if (inEdge->isDeoptBlock())
+ continue;
+ MIBlock::Index v = inEdge->index();
+ MIBlock::Index ss = MIBlock::InvalidIndex;
+ if (m_data->dfnum[v] <= m_data->dfnum[n])
+ ss = v;
+ else
+ ss = m_data->semi[ancestorWithLowestSemi(v, worklist)];
+ if (m_data->dfnum[ss] < m_data->dfnum[s])
+ s = ss;
+ }
+ m_data->semi[n] = s;
+ bucket[s].push_back(n);
+ link(p, n);
+ if (bucket.contains(p)) {
+ for (MIBlock::Index v : bucket[p]) {
+ MIBlock::Index y = ancestorWithLowestSemi(v, worklist);
+ MIBlock::Index semi_v = m_data->semi[v];
+ if (m_data->semi[y] == semi_v)
+ m_idom[v] = semi_v;
+ else
+ m_data->samedom[v] = y;
+ }
+ bucket.remove(p);
+ }
+ }
+
+ for (unsigned i = 1; i < m_data->size; ++i) {
+ MIBlock::Index n = m_data->vertex[i];
+ Q_ASSERT(n != MIBlock::InvalidIndex);
+ Q_ASSERT(!bucket.contains(n));
+ Q_ASSERT(m_data->ancestor[n] != MIBlock::InvalidIndex);
+ Q_ASSERT((m_data->semi[n] != MIBlock::InvalidIndex
+ && m_data->dfnum[m_data->ancestor[n]] <= m_data->dfnum[m_data->semi[n]])
+ || m_data->semi[n] == n);
+ MIBlock::Index sdn = m_data->samedom[n];
+ if (sdn != MIBlock::InvalidIndex)
+ m_idom[n] = m_idom[sdn];
+ }
+
+ if (lcDomTree().isDebugEnabled())
+ dumpImmediateDominators();
+
+ m_data.reset(nullptr);
+}
+
+MIBlock::Index DominatorTree::ancestorWithLowestSemi(MIBlock::Index v,
+ std::vector<MIBlock::Index> &worklist)
+{
+ worklist.clear();
+ for (MIBlock::Index it = v; it != MIBlock::InvalidIndex; it = m_data->ancestor[it])
+ worklist.push_back(it);
+
+ if (worklist.size() < 2)
+ return m_data->best[v];
+
+ MIBlock::Index b = MIBlock::InvalidIndex;
+ MIBlock::Index last = worklist.back();
+ Q_ASSERT(worklist.size() <= INT_MAX);
+ for (int it = static_cast<int>(worklist.size()) - 2; it >= 0; --it) {
+ MIBlock::Index bbIt = worklist[it];
+ m_data->ancestor[bbIt] = last;
+ MIBlock::Index &best_it = m_data->best[bbIt];
+ if (b != MIBlock::InvalidIndex
+ && m_data->dfnum[m_data->semi[b]] < m_data->dfnum[m_data->semi[best_it]]) {
+ best_it = b;
+ } else {
+ b = best_it;
+ }
+ }
+ return b;
+}
+
+void DominatorFrontier::compute(const DominatorTree &domTree)
+{
+ struct NodeProgress {
+ std::vector<MIBlock::Index> children;
+ std::vector<MIBlock::Index> todo;
+ };
+
+ MIFunction *function = domTree.function();
+ m_df.resize(function->blockCount());
+
+ // compute children of each node in the dominator tree
+ std::vector<std::vector<MIBlock::Index> > children; // BasicBlock index -> children
+ children.resize(function->blockCount());
+ for (MIBlock *n : function->blocks()) {
+ const MIBlock::Index nodeIndex = n->index();
+ Q_ASSERT(function->block(nodeIndex) == n);
+ const MIBlock::Index nodeDominator = domTree.immediateDominator(nodeIndex);
+ if (nodeDominator == MIBlock::InvalidIndex)
+ continue; // there is no dominator to add this node to as a child (e.g. the start node)
+ children[nodeDominator].push_back(nodeIndex);
+ }
+
+ // Fill the worklist and initialize the node status for each basic-block
+ std::vector<NodeProgress> nodeStatus;
+ nodeStatus.resize(function->blockCount());
+ std::vector<MIBlock::Index> worklist;
+ worklist.reserve(function->blockCount());
+ for (MIBlock *bb : function->blocks()) {
+ MIBlock::Index nodeIndex = bb->index();
+ worklist.push_back(nodeIndex);
+ NodeProgress &np = nodeStatus[nodeIndex];
+ np.children = children[nodeIndex];
+ np.todo = children[nodeIndex];
+ }
+
+ BitVector DF_done(int(function->blockCount()), false);
+
+ while (!worklist.empty()) {
+ MIBlock::Index node = worklist.back();
+
+ if (DF_done.at(node)) {
+ worklist.pop_back();
+ continue;
+ }
+
+ NodeProgress &np = nodeStatus[node];
+ auto it = np.todo.begin();
+ while (it != np.todo.end()) {
+ if (DF_done.at(*it)) {
+ it = np.todo.erase(it);
+ } else {
+ worklist.push_back(*it);
+ break;
+ }
+ }
+
+ if (np.todo.empty()) {
+ MIBlockSet &miBlockSet = m_df[node];
+ miBlockSet.init(function);
+ for (MIBlock *y : function->block(node)->outEdges()) {
+ if (domTree.immediateDominator(y->index()) != node)
+ miBlockSet.insert(y);
+ }
+ for (MIBlock::Index child : np.children) {
+ const MIBlockSet &ws = m_df[child];
+ for (auto w : ws) {
+ const MIBlock::Index wIndex = w->index();
+ if (node == wIndex || !domTree.dominates(node, w->index()))
+ miBlockSet.insert(w);
+ }
+ }
+ DF_done.setBit(node);
+ worklist.pop_back();
+ }
+ }
+
+ if (lcDomFrontier().isDebugEnabled())
+ dump(domTree.function());
+}
+
+void DominatorFrontier::dump(MIFunction *function)
+{
+ QBuffer buf;
+ buf.open(QIODevice::WriteOnly);
+ QTextStream qout(&buf);
+ qout << "Dominator Frontiers:" << endl;
+ for (MIBlock *n : function->blocks()) {
+ qout << "\tDF[" << n->index() << "]: {";
+ const MIBlockSet &SList = m_df[n->index()];
+ for (MIBlockSet::const_iterator i = SList.begin(), ei = SList.end(); i != ei; ++i) {
+ if (i != SList.begin())
+ qout << ", ";
+ qout << (*i)->index();
+ }
+ qout << "}" << endl;
+ }
+ qCDebug(lcDomFrontier, "%s", buf.data().constData());
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4domtree_p.h b/src/qml/jit/qv4domtree_p.h
new file mode 100644
index 0000000000..703e17ab61
--- /dev/null
+++ b/src/qml/jit/qv4domtree_p.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** 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 QV4DOMTREE_P_H
+#define QV4DOMTREE_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 "qv4mi_p.h"
+#include "qv4miblockset_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+class DominatorTree
+{
+ Q_DISABLE_COPY_MOVE(DominatorTree)
+
+public:
+ DominatorTree(MIFunction *f);
+ ~DominatorTree() = default;
+
+ void dumpImmediateDominators() const;
+ MIFunction *function() const
+ { return m_function; }
+
+ void setImmediateDominator(MIBlock::Index dominated, MIBlock::Index dominator);
+
+ MIBlock::Index immediateDominator(MIBlock::Index blockIndex) const
+ { return m_idom[blockIndex]; }
+
+ bool dominates(MIBlock::Index dominator, MIBlock::Index dominated) const;
+
+ bool insideSameDominatorChain(MIBlock::Index one, MIBlock::Index other) const
+ { return one == other || dominates(one, other) || dominates(other, one); }
+
+ std::vector<MIBlock *> calculateDFNodeIterOrder() const;
+
+ std::vector<int> calculateNodeDepths() const;
+
+private: // functions
+ int calculateNodeDepth(MIBlock::Index nodeIdx, std::vector<int> &nodeDepths) const;
+ void link(MIBlock::Index p, MIBlock::Index n);
+ void calculateIDoms();
+ void dfs(MIBlock::Index node);
+ MIBlock::Index ancestorWithLowestSemi(MIBlock::Index v, std::vector<MIBlock::Index> &worklist);
+
+private: // data
+ struct Data {
+ std::vector<unsigned> dfnum; // MIBlock index -> dfnum
+ std::vector<MIBlock::Index> vertex;
+ std::vector<MIBlock::Index> parent; // MIBlock index -> parent MIBlock index
+ std::vector<MIBlock::Index> ancestor; // MIBlock index -> ancestor MIBlock index
+ std::vector<MIBlock::Index> best; // MIBlock index -> best MIBlock index
+ std::vector<MIBlock::Index> semi; // MIBlock index -> semi dominator MIBlock index
+ std::vector<MIBlock::Index> samedom; // MIBlock index -> same dominator MIBlock index
+ unsigned size = 0;
+ };
+
+ MIFunction *m_function;
+ QScopedPointer<Data> m_data;
+ std::vector<MIBlock::Index> m_idom; // MIBlock index -> immediate dominator MIBlock index
+};
+
+class DominatorFrontier
+{
+public:
+ DominatorFrontier(const DominatorTree &domTree)
+ { compute(domTree); }
+
+ const MIBlockSet &operator[](MIBlock *n) const
+ { return m_df[n->index()]; }
+
+private: // functions
+ void compute(const DominatorTree &domTree);
+ void dump(MIFunction *function);
+
+private: // data
+ std::vector<MIBlockSet> m_df; // MIBlock index -> dominator frontier
+};
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4DOMTREE_P_H
diff --git a/src/qml/jit/qv4graphbuilder.cpp b/src/qml/jit/qv4graphbuilder.cpp
index 94b8e86e08..2c073701ee 100644
--- a/src/qml/jit/qv4graphbuilder.cpp
+++ b/src/qml/jit/qv4graphbuilder.cpp
@@ -800,45 +800,10 @@ void GraphBuilder::generate_StoreSuperProperty(int property)
env()->accumulator());
}
-void GraphBuilder::generate_StoreScopeObjectProperty(int base, int propertyIndex)
+void GraphBuilder::generate_LoadQmlContextPropertyLookup(int propertyIndex, int /*traceSlot*/)
{
- createNode(opBuilder()->get<Meta::QMLStoreScopeObjectProperty>(),
- env()->slot(base),
- createConstant(propertyIndex),
- env()->accumulator());
-}
-
-void GraphBuilder::generate_StoreContextObjectProperty(int base, int propertyIndex)
-{
- createNode(opBuilder()->get<Meta::QMLStoreContextObjectProperty>(),
- env()->slot(base),
- createConstant(propertyIndex),
- env()->accumulator());
-}
-
-void GraphBuilder::generate_LoadScopeObjectProperty(int propertyIndex, int base,
- int captureRequired)
-{
- bindAcc(createNode(opBuilder()->get<Meta::QMLLoadScopeObjectProperty>(),
- env()->slot(base),
- createConstant(propertyIndex),
- createConstant(captureRequired)));
-}
-
-void GraphBuilder::generate_LoadContextObjectProperty(int propertyIndex, int base,
- int captureRequired)
-{
- bindAcc(createNode(opBuilder()->get<Meta::QMLLoadContextObjectProperty>(),
- env()->slot(base),
- createConstant(propertyIndex),
- createConstant(captureRequired)));
-}
-
-void GraphBuilder::generate_LoadIdObject(int index, int base)
-{
- bindAcc(createNode(opBuilder()->get<Meta::QMLLoadIdObject>(),
- env()->slot(base),
- createConstant(index)));
+ bindAcc(createNode(opBuilder()->get<Meta::QMLLoadQmlContextPropertyLookup>(),
+ createConstant(propertyIndex)));
}
void GraphBuilder::generate_Yield() { Q_UNREACHABLE(); }
@@ -913,22 +878,12 @@ void GraphBuilder::generate_CallGlobalLookup(int index, int argc, int argv, int
finalizeCall(Meta::JSCallGlobalLookup, args, argc, argv);
}
-void GraphBuilder::generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv,
- int /*traceSlot*/)
-{
- VarArgNodes args;
- args.append(env()->slot(base));
- args.append(createConstant(propIdx));
- finalizeCall(Meta::QMLCallScopeObjectProperty, args, argc, argv);
-}
-
-void GraphBuilder::generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv,
- int /*traceSlot*/)
+void GraphBuilder::generate_CallQmlContextPropertyLookup(int index, int argc, int argv,
+ int /*traceSlot*/)
{
VarArgNodes args;
- args.append(env()->slot(base));
- args.append(createConstant(propIdx));
- finalizeCall(Meta::QMLCallContextObjectProperty, args, argc, argv);
+ args.append(createConstant(index));
+ finalizeCall(Meta::QMLCallQmlContextPropertyLookup, args, argc, argv);
}
void GraphBuilder::generate_SetUnwindHandler(int offset)
@@ -1472,7 +1427,7 @@ void GraphBuilder::generate_UNot()
createToBoolean(env()->accumulator())));
}
-void GraphBuilder::generate_UPlus()
+void GraphBuilder::generate_UPlus(int /*traceSlot*/)
{
Node* control = createNode(opBuilder()->get<Meta::JSToNumber>(),
env()->accumulator());
@@ -1636,17 +1591,6 @@ void GraphBuilder::generate_Sub(int lhs, int /*traceSlot*/)
env()->accumulator()));
}
-void GraphBuilder::generate_LoadQmlContext(int result)
-{
- env()->bindNodeToSlot(createNode(opBuilder()->get<Meta::QMLLoadContext>()), result);
-}
-
-void GraphBuilder::generate_LoadQmlImportedScripts(int result)
-{
- env()->bindNodeToSlot(createNode(opBuilder()->get<Meta::QMLLoadImportedScripts>()),
- result);
-}
-
void GraphBuilder::generate_InitializeBlockDeadTemporalZone(int firstReg, int count)
{
for (int reg = firstReg; reg < firstReg + count; ++reg)
diff --git a/src/qml/jit/qv4graphbuilder_p.h b/src/qml/jit/qv4graphbuilder_p.h
index 6393cab9ef..450d8640b7 100644
--- a/src/qml/jit/qv4graphbuilder_p.h
+++ b/src/qml/jit/qv4graphbuilder_p.h
@@ -173,15 +173,7 @@ protected: // ByteCodeHandler
void generate_SetLookup(int index, int base) override;
void generate_LoadSuperProperty(int property) override;
void generate_StoreSuperProperty(int property) override;
- void generate_StoreScopeObjectProperty(int base,
- int propertyIndex) override;
- void generate_StoreContextObjectProperty(int base,
- int propertyIndex) override;
- void generate_LoadScopeObjectProperty(int propertyIndex, int base,
- int captureRequired) override;
- void generate_LoadContextObjectProperty(int propertyIndex, int base,
- int captureRequired) override;
- void generate_LoadIdObject(int index, int base) override;
+ void generate_LoadQmlContextPropertyLookup(int property, int traceSlot) override;
void generate_Yield() override;
void generate_YieldStar() override;
void generate_Resume(int offset) override;
@@ -196,10 +188,7 @@ protected: // ByteCodeHandler
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_CallScopeObjectProperty(int propIdx, int base, int argc, int argv,
- int traceSlot) override;
- void generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv,
- int traceSlot) override;
+ void generate_CallQmlContextPropertyLookup(int index, int argc, int argv, int traceSlot) override;
void generate_SetUnwindHandler(int offset) override;
void generate_UnwindDispatch() override;
void generate_UnwindToLabel(int level, int offset) override;
@@ -261,7 +250,7 @@ protected: // ByteCodeHandler
void generate_CmpIn(int lhs) override;
void generate_CmpInstanceOf(int lhs) override;
void generate_UNot() override;
- void generate_UPlus() override;
+ void generate_UPlus(int traceSlot) override;
void generate_UMinus(int traceSlot) override;
void generate_UCompl() override;
void generate_Increment(int traceSlot) override;
@@ -284,8 +273,6 @@ protected: // ByteCodeHandler
void generate_Div(int lhs) override;
void generate_Mod(int lhs, int traceSlot) override;
void generate_Sub(int lhs, int traceSlot) override;
- void generate_LoadQmlContext(int result) override;
- void generate_LoadQmlImportedScripts(int result) override;
void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override;
void generate_ThrowOnNullOrUndefined() override;
void generate_GetTemplateObject(int index) override;
diff --git a/src/qml/jit/qv4ir.cpp b/src/qml/jit/qv4ir.cpp
index 0b82330394..cb3eeeec60 100644
--- a/src/qml/jit/qv4ir.cpp
+++ b/src/qml/jit/qv4ir.cpp
@@ -78,7 +78,7 @@ QString Function::name() const
if (auto n = v4Function()->name())
name = n->toQString();
if (name.isEmpty())
- name.sprintf("%p", static_cast<void *>(v4Function()));
+ name = QString::asprintf("%p", v4Function());
auto loc = v4Function()->sourceLocation();
return name + QStringLiteral(" (%1:%2:%3)").arg(loc.sourceFile, QString::number(loc.line),
QString::number(loc.column));
@@ -159,7 +159,7 @@ QByteArray Dumper::dump(const Function *f)
name = n->toQString();
fo[QLatin1String("_searchKey")] = QStringLiteral("function %1").arg(name);
if (name.isEmpty())
- name.sprintf("%p", static_cast<void *>(f->v4Function()));
+ name = QString::asprintf("%p", f->v4Function());
fo[QLatin1String("name")] = name;
}
diff --git a/src/qml/jit/qv4loopinfo.cpp b/src/qml/jit/qv4loopinfo.cpp
new file mode 100644
index 0000000000..0366c49e30
--- /dev/null
+++ b/src/qml/jit/qv4loopinfo.cpp
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** 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 <QtCore/qloggingcategory.h>
+
+#include "qv4loopinfo_p.h"
+#include "qv4domtree_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcLoopinfo, "qt.v4.ir.loopinfo")
+
+void LoopInfo::detectLoops()
+{
+ blockInfos.resize(dt.function()->blockCount());
+
+ std::vector<MIBlock *> backedges;
+ backedges.reserve(4);
+
+ const auto order = dt.calculateDFNodeIterOrder();
+ for (MIBlock *bb : order) {
+ if (bb->isDeoptBlock())
+ continue;
+
+ backedges.clear();
+
+ for (MIBlock *pred : bb->inEdges()) {
+ if (bb == pred || dt.dominates(bb->index(), pred->index()))
+ backedges.push_back(pred);
+ }
+
+ if (!backedges.empty())
+ subLoop(bb, backedges);
+ }
+
+ collectLoopExits();
+
+ dump();
+}
+
+void LoopInfo::collectLoopExits()
+{
+ for (MIBlock::Index i = 0, ei = MIBlock::Index(blockInfos.size()); i != ei; ++i) {
+ BlockInfo &bi = blockInfos[i];
+ MIBlock *currentBlock = dt.function()->block(i);
+ if (bi.isLoopHeader) {
+ for (MIBlock *outEdge : currentBlock->outEdges()) {
+ if (outEdge != currentBlock && !inLoopOrSubLoop(outEdge, currentBlock))
+ bi.loopExits.push_back(outEdge);
+ }
+ }
+ if (MIBlock *containingLoop = bi.loopHeader) {
+ BlockInfo &loopInfo = blockInfos[containingLoop->index()];
+ for (MIBlock *outEdge : currentBlock->outEdges()) {
+ if (outEdge != containingLoop && !inLoopOrSubLoop(outEdge, containingLoop))
+ loopInfo.loopExits.push_back(outEdge);
+ }
+ }
+ }
+}
+
+bool LoopInfo::inLoopOrSubLoop(MIBlock *block, MIBlock *loopHeader) const
+{
+ const BlockInfo &bi = blockInfos[block->index()];
+ MIBlock *loopHeaderForBlock = bi.loopHeader;
+ if (loopHeaderForBlock == nullptr)
+ return false; // block is not in any loop
+
+ while (loopHeader) {
+ if (loopHeader == loopHeaderForBlock)
+ return true;
+ // look into the parent loop of loopHeader to see if block is contained there
+ loopHeader = blockInfos[loopHeader->index()].loopHeader;
+ }
+
+ return false;
+}
+
+void LoopInfo::subLoop(MIBlock *loopHead, const std::vector<MIBlock *> &backedges)
+{
+ blockInfos[loopHead->index()].isLoopHeader = true;
+
+ std::vector<MIBlock *> worklist;
+ worklist.reserve(backedges.size() + 8);
+ worklist.insert(worklist.end(), backedges.begin(), backedges.end());
+ while (!worklist.empty()) {
+ MIBlock *predIt = worklist.back();
+ worklist.pop_back();
+
+ MIBlock *subloop = blockInfos[predIt->index()].loopHeader;
+ if (subloop) {
+ // This is a discovered block. Find its outermost discovered loop.
+ while (MIBlock *parentLoop = blockInfos[subloop->index()].loopHeader)
+ subloop = parentLoop;
+
+ // If it is already discovered to be a subloop of this loop, continue.
+ if (subloop == loopHead)
+ continue;
+
+ // Yay, it's a subloop of this loop.
+ blockInfos[subloop->index()].loopHeader = loopHead;
+ predIt = subloop;
+
+ // Add all predecessors of the subloop header to the worklist, as long as
+ // those predecessors are not in the current subloop. It might be the case
+ // that they are in other loops, which we will then add as a subloop to the
+ // current loop.
+ for (MIBlock *predIn : predIt->inEdges())
+ if (blockInfos[predIn->index()].loopHeader != subloop)
+ worklist.push_back(predIn);
+ } else {
+ if (predIt == loopHead)
+ continue;
+
+ // This is an undiscovered block. Map it to the current loop.
+ blockInfos[predIt->index()].loopHeader = loopHead;
+
+ // Add all incoming edges to the worklist.
+ for (MIBlock *bb : predIt->inEdges())
+ worklist.push_back(bb);
+ }
+ }
+}
+
+void LoopInfo::dump() const
+{
+ if (!lcLoopinfo().isDebugEnabled())
+ return;
+
+ QString s = QStringLiteral("Loop information:\n");
+ for (size_t i = 0, ei = blockInfos.size(); i != ei; ++i) {
+ const BlockInfo &bi = blockInfos[i];
+ s += QStringLiteral(" %1 : is loop header: %2, contained in loop header's loop: ")
+ .arg(i).arg(bi.isLoopHeader ? QLatin1String("yes") : QLatin1String("no"));
+ if (bi.loopHeader)
+ s += QString::number(bi.loopHeader->index());
+ else
+ s += QLatin1String("<none>");
+ if (bi.isLoopHeader) {
+ s += QStringLiteral(", loop exits: ");
+ if (bi.loopExits.empty()) {
+ s += QLatin1String("<none>");
+ } else {
+ bool first = true;
+ for (MIBlock *exit : bi.loopExits) {
+ if (first)
+ first = false;
+ else
+ s += QStringLiteral(", ");
+ s += QString::number(exit->index());
+ }
+ }
+ }
+ s += QLatin1Char('\n');
+ }
+ qCDebug(lcLoopinfo).noquote().nospace() << s;
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4loopinfo_p.h b/src/qml/jit/qv4loopinfo_p.h
new file mode 100644
index 0000000000..6a865e6dc6
--- /dev/null
+++ b/src/qml/jit/qv4loopinfo_p.h
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** 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 QV4LOOPINFO_P_H
+#define QV4LOOPINFO_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 "qv4mi_p.h"
+
+#include <QHash>
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+class DominatorTree;
+
+// Detect all (sub-)loops in a function.
+//
+// Doing loop detection on the CFG is better than relying on the statement information in
+// order to mark loops. Although JavaScript only has natural loops, it can still be the case
+// that something is not a loop even though a loop-like-statement is in the source. For
+// example:
+// while (true) {
+// if (i > 0)
+// break;
+// else
+// break;
+// }
+//
+// Algorithm:
+// - do a DFS on the dominator tree, where for each node:
+// - collect all back-edges
+// - if there are back-edges, the node is a loop-header for a new loop, so:
+// - walk the CFG is reverse-direction, and for every node:
+// - if the node already belongs to a loop, we've found a nested loop:
+// - get the loop-header for the (outermost) nested loop
+// - add that loop-header to the current loop
+// - continue by walking all incoming edges that do not yet belong to the current loop
+// - if the node does not belong to a loop yet, add it to the current loop, and
+// go on with all incoming edges
+//
+// Loop-header detection by checking for back-edges is very straight forward: a back-edge is
+// an incoming edge where the other node is dominated by the current node. Meaning: all
+// execution paths that reach that other node have to go through the current node, that other
+// node ends with a (conditional) jump back to the loop header.
+//
+// The exact order of the DFS on the dominator tree is not important. The only property has to
+// be that a node is only visited when all the nodes it dominates have been visited before.
+// The reason for the DFS is that for nested loops, the inner loop's loop-header is dominated
+// by the outer loop's header. So, by visiting depth-first, sub-loops are identified before
+// their containing loops, which makes nested-loop identification free. An added benefit is
+// that the nodes for those sub-loops are only processed once.
+//
+// Note: independent loops that share the same header are merged together. For example, in
+// the code snippet below, there are 2 back-edges into the loop-header, but only one single
+// loop will be detected.
+// while (a) {
+// if (b)
+// continue;
+// else
+// continue;
+// }
+class LoopInfo
+{
+ Q_DISABLE_COPY_MOVE(LoopInfo)
+
+ struct BlockInfo
+ {
+ MIBlock *loopHeader = nullptr;
+ bool isLoopHeader = false;
+ std::vector<MIBlock *> loopExits;
+ };
+
+public:
+ LoopInfo(const DominatorTree &dt)
+ : dt(dt)
+ {}
+
+ ~LoopInfo() = default;
+
+ void detectLoops();
+
+ MIBlock *loopHeaderFor(MIBlock *bodyBlock) const
+ { return blockInfos[bodyBlock->index()].loopHeader; }
+
+ bool isLoopHeader(MIBlock *block) const
+ { return blockInfos[block->index()].isLoopHeader; }
+
+ const std::vector<MIBlock *> loopExitsForLoop(MIBlock *loopHeader) const
+ { return blockInfos[loopHeader->index()].loopExits; }
+
+private:
+ void subLoop(MIBlock *loopHead, const std::vector<MIBlock *> &backedges);
+ void collectLoopExits();
+ bool inLoopOrSubLoop(MIBlock *block, MIBlock *loopHeader) const;
+
+ void dump() const;
+
+private:
+ const DominatorTree &dt;
+ std::vector<BlockInfo> blockInfos;
+};
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4LOOPINFO_P_H
diff --git a/src/qml/jit/qv4lowering.cpp b/src/qml/jit/qv4lowering.cpp
new file mode 100644
index 0000000000..3b3711e7fa
--- /dev/null
+++ b/src/qml/jit/qv4lowering.cpp
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** 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 <QLoggingCategory>
+
+#include "qv4lowering_p.h"
+#include "qv4graph_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcLower, "qt.v4.ir.lowering")
+
+GenericLowering::GenericLowering(Function &f)
+ : m_function(f)
+{}
+
+void GenericLowering::lower()
+{
+ NodeWorkList worklist(graph());
+ // The order doesn't really matter for generic lowering, as long as it's done in 1 pass, and
+ // have any clean-up done afterwards.
+ worklist.enqueueAllInputs(graph()->endNode());
+
+ while (Node *n = worklist.dequeueNextNodeForVisiting()) {
+ worklist.enqueueAllInputs(n);
+
+ if (!CallPayload::isRuntimeCall(n->opcode()))
+ continue;
+
+ if (CallPayload::isVarArgsCall(n->opcode()))
+ replaceWithVarArgsCall(n);
+ else
+ replaceWithCall(n);
+ }
+}
+
+void GenericLowering::replaceWithCall(Node *n)
+{
+ auto newOp = opBuilder()->getCall(n->opcode());
+
+ QVarLengthArray<Node *, 32> args;
+ if (CallPayload::takesEngineAsArg(n->opcode(), 0))
+ args.append(graph()->engineNode());
+ if (CallPayload::takesFunctionAsArg(n->opcode(), args.size()))
+ args.append(graph()->functionNode());
+ if (CallPayload::takesFrameAsArg(n->opcode(), args.size()))
+ args.append(graph()->cppFrameNode());
+ const int extraLeadingArguments = args.size();
+
+ for (unsigned arg = 0, earg = n->inputCount(); arg != earg; ++arg) {
+ Node *input = n->input(arg);
+ if (input->opcode() == Meta::FrameState)
+ continue;
+
+ if (arg >= n->operation()->valueInputCount()) {
+ // effect or control input
+ args.append(input);
+ continue;
+ }
+
+ if (CallPayload::needsStorageOnJSStack(n->opcode(), args.size(), input->operation(),
+ function().nodeInfo(input)->type()))
+ input = graph()->createNode(opBuilder()->get<Meta::Alloca>(), input);
+
+ args.append(input);
+ }
+
+ Node *newCall = graph()->createNode(newOp, args.data(), args.size());
+
+ qCDebug(lcLower) << "replacing node" << n->id() << n->operation()->debugString()
+ << "with node" << newCall->id() << newOp->debugString();
+ qCDebug(lcLower) << "... old node #inputs:" << n->inputCount();
+ qCDebug(lcLower) << "... old node #uses:" << n->useCount();
+
+ function().nodeInfo(newCall)->setType(CallPayload::returnType(n->opcode()));
+ n->replaceAllUsesWith(newCall);
+ n->kill();
+
+ qCDebug(lcLower) << "... new node #inputs:" << newCall->inputCount();
+ qCDebug(lcLower) << "... new node #uses:" << newCall->useCount();
+
+ for (Node *use : newCall->uses()) {
+ // fix-up indices for SelectOutput:
+ if (use->opcode() == Meta::SelectOutput) {
+ const int oldIndex = ConstantPayload::get(*use->input(1)->operation())->value().int_32();
+ const int newIndex = oldIndex + extraLeadingArguments;
+ use->replaceInput(1, graph()->createConstantIntNode(newIndex));
+ use->replaceInput(2, newCall->input(newIndex));
+ break;
+ }
+ }
+}
+
+void GenericLowering::replaceWithVarArgsCall(Node *n)
+{
+ const bool isTailCall = n->opcode() == Meta::JSTailCall;
+ Operation *newOp = isTailCall ? opBuilder()->getTailCall()
+ : opBuilder()->getCall(n->opcode());
+
+ //### optimize this for 0 and 1 argument: we don't need to create a VarArgs array for these cases
+
+ const unsigned varArgsStart = CallPayload::varArgsStart(n->opcode()) - 1; // subtract 1 because the runtime calls all take the engine argument as arg0, which isn't in the graph before lowering.
+ Node *vaAlloc = graph()->createNode(
+ opBuilder()->get<Meta::VAAlloc>(),
+ graph()->createConstantIntNode(n->operation()->valueInputCount() - varArgsStart),
+ n->effectInput());
+ QVarLengthArray<Node *, 32> vaSealIn;
+ vaSealIn.append(vaAlloc);
+ for (unsigned i = varArgsStart, ei = n->operation()->valueInputCount(); i != ei; ++i) {
+ vaSealIn.append(graph()->createNode(opBuilder()->get<Meta::VAStore>(), vaAlloc,
+ graph()->createConstantIntNode(vaSealIn.size() - 1),
+ n->input(i)));
+ }
+ vaSealIn.append(vaAlloc);
+ Node *vaSeal = graph()->createNode(opBuilder()->getVASeal(vaSealIn.size() - 2),
+ vaSealIn.data(),
+ vaSealIn.size());
+ QVarLengthArray<Node *, 8> callArgs;
+ if (isTailCall)
+ callArgs.append(graph()->cppFrameNode());
+ callArgs.append(graph()->engineNode());
+ for (unsigned i = 0; i != varArgsStart; ++i) {
+ Node *input = n->input(i);
+ if (CallPayload::needsStorageOnJSStack(n->opcode(), callArgs.size(), input->operation(),
+ function().nodeInfo(input)->type()))
+ input = graph()->createNode(opBuilder()->get<Meta::Alloca>(), input);
+ callArgs.append(input);
+ }
+ callArgs.append(vaSeal); // args
+ if (n->opcode() != Meta::JSCreateClass) // JSCreateClass is the odd duck
+ callArgs.append(graph()->createConstantIntNode(vaSealIn.size() - 2)); // argc
+ callArgs.append(vaSeal); // effect
+ callArgs.append(n->controlInput(0)); // control flow
+ Node *newCall = graph()->createNode(newOp, callArgs.data(), unsigned(callArgs.size()));
+
+ qCDebug(lcLower) << "replacing node" << n->id() << n->operation()->debugString()
+ << "with node" << newCall->id() << newOp->debugString();
+ qCDebug(lcLower) << "... old node #inputs:" << n->inputCount();
+ qCDebug(lcLower) << "... old node #uses:" << n->useCount();
+
+ n->replaceAllUsesWith(newCall);
+ n->kill();
+
+ qCDebug(lcLower) << "... new node #inputs:" << newCall->inputCount();
+ qCDebug(lcLower) << "... new node #uses:" << newCall->useCount();
+}
+
+bool GenericLowering::allUsesAsUnboxedBool(Node *n)
+{
+ for (Node *use : n->uses()) {
+ if (use->operation()->kind() != Meta::Branch)
+ return false;
+ }
+
+ return true;
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4lowering_p.h b/src/qml/jit/qv4lowering_p.h
new file mode 100644
index 0000000000..0b482bc9f0
--- /dev/null
+++ b/src/qml/jit/qv4lowering_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** 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 QV4LOWERING_P_H
+#define QV4LOWERING_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/qqmljsmemorypool_p.h>
+#include <private/qv4global_p.h>
+#include <private/qv4ir_p.h>
+#include <private/qv4util_p.h>
+#include <private/qv4node_p.h>
+#include <private/qv4graph_p.h>
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+// Lowering replaces JS level operations with lower level ones. E.g. a JSAdd is lowered to an AddI32
+// if both inputs and the output are 32bit integers, or to a runtime call in all other cases. This
+// transforms the graph into something that is closer to actual executable code.
+
+
+// Last lowering phase: replace all JSOperations that are left with runtime calls. There is nothing
+// smart here, all that should have been done before this phase.
+class GenericLowering final
+{
+ Q_DISABLE_COPY(GenericLowering)
+
+public:
+ GenericLowering(Function &f);
+
+ void lower();
+
+private:
+ void replaceWithCall(Node *n);
+ void replaceWithVarArgsCall(Node *n);
+ static bool allUsesAsUnboxedBool(Node *n);
+
+ Function &function()
+ { return m_function; }
+
+ Graph *graph()
+ { return function().graph(); }
+
+ OperationBuilder *opBuilder()
+ { return graph()->opBuilder(); }
+
+private:
+ Function &m_function;
+};
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4LOWERING_P_H
diff --git a/src/qml/jit/qv4mi.cpp b/src/qml/jit/qv4mi.cpp
new file mode 100644
index 0000000000..f0b172243d
--- /dev/null
+++ b/src/qml/jit/qv4mi.cpp
@@ -0,0 +1,251 @@
+/****************************************************************************
+**
+** 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 <QtCore/qloggingcategory.h>
+#include <private/qqmlglobal_p.h>
+
+#include "qv4mi_p.h"
+#include "qv4node_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcMI, "qt.v4.ir.mi")
+
+QString MIOperand::debugString() const
+{
+ switch (kind()) {
+ case Invalid: return QStringLiteral("<<INVALID>>");
+ case Constant: return ConstantPayload::debugString(constantValue());
+ case VirtualRegister: return QStringLiteral("vreg%1").arg(virtualRegister());
+ case EngineRegister: return QStringLiteral("engine");
+ case CppFrameRegister: return QStringLiteral("cppFrame");
+ case Function: return QStringLiteral("function");
+ case JSStackSlot: return QStringLiteral("jsstack[%1]").arg(stackSlot());
+ case BoolStackSlot: return QStringLiteral("bstack[%1]").arg(stackSlot());
+ case JumpTarget: return targetBlock() ? QStringLiteral("L%1").arg(targetBlock()->index())
+ : QStringLiteral("<<INVALID BLOCK>>");
+ default: Q_UNREACHABLE();
+ }
+}
+
+MIInstr *MIInstr::create(QQmlJS::MemoryPool *pool, Node *irNode, unsigned nOperands)
+{
+ return pool->New<MIInstr>(irNode, pool, nOperands);
+}
+
+static QString commentIndent(const QString &line)
+{
+ int spacing = std::max(70 - line.length(), 1);
+ return line + QString(spacing, QLatin1Char(' '));
+}
+
+static QString indent(int nr)
+{
+ QString s = nr == -1 ? QString() : QString::number(nr);
+ int padding = 6 - s.size();
+ if (padding > 0)
+ s = QString(padding, QLatin1Char(' ')) + s;
+ return s;
+}
+
+MIFunction::MIFunction(Function *irFunction)
+ : m_irFunction(irFunction)
+{}
+
+void MIFunction::renumberBlocks()
+{
+ for (size_t i = 0, ei = m_blocks.size(); i != ei; ++i) {
+ MIBlock *b = m_blocks[i];
+ b->setIndex(unsigned(i));
+ }
+}
+
+void MIFunction::renumberInstructions()
+{
+ int pos = 0;
+ for (MIBlock *b : m_blocks) {
+ for (MIInstr &instr : b->instructions()) {
+ pos += 2;
+ instr.setPosition(pos);
+ }
+ }
+}
+
+void MIFunction::dump(const QString &description) const
+{
+ if (!lcMI().isDebugEnabled())
+ return;
+
+ QString s = description + QLatin1String(":\n");
+ QString name;
+ if (auto n = irFunction()->v4Function()->name())
+ name = n->toQString();
+ if (name.isEmpty())
+ QString::asprintf("%p", static_cast<void *>(irFunction()->v4Function()));
+ QString line = QStringLiteral("function %1 {").arg(name);
+ auto loc = irFunction()->v4Function()->sourceLocation();
+ s += commentIndent(line) + QStringLiteral("; %1:%2:%3\n").arg(loc.sourceFile,
+ QString::number(loc.line),
+ QString::number(loc.column));
+ for (const MIBlock *b : blocks()) {
+ line = QStringLiteral("L%1").arg(b->index());
+ bool first = true;
+ if (!b->arguments().empty()) {
+ line += QLatin1Char('(');
+ for (const MIOperand &arg : b->arguments()) {
+ if (first)
+ first = false;
+ else
+ line += QStringLiteral(", ");
+ line += arg.debugString();
+ }
+ line += QLatin1Char(')');
+ }
+ line += QLatin1Char(':');
+ line = commentIndent(line) + QStringLiteral("; preds: ");
+ if (b->inEdges().isEmpty()) {
+ line += QStringLiteral("<none>");
+ } else {
+ bool first = true;
+ for (MIBlock *in : b->inEdges()) {
+ if (first)
+ first = false;
+ else
+ line += QStringLiteral(", ");
+ line += QStringLiteral("L%1").arg(in->index());
+ }
+ }
+ s += line + QLatin1Char('\n');
+ for (const MIInstr &i : b->instructions()) {
+ line = indent(i.position()) + QLatin1String(": ");
+ if (i.hasDestination())
+ line += i.destination().debugString() + QStringLiteral(" = ");
+ line += i.irNode()->operation()->debugString();
+ bool first = true;
+ for (const MIOperand &op : i.operands()) {
+ if (first)
+ first = false;
+ else
+ line += QLatin1Char(',');
+ line += QLatin1Char(' ') + op.debugString();
+ }
+ line = commentIndent(line) + QStringLiteral("; node-id: %1").arg(i.irNode()->id());
+ if (i.irNode()->operation()->needsBytecodeOffsets())
+ line += QStringLiteral(", bytecode-offset: %1").arg(irFunction()->nodeInfo(i.irNode())->currentInstructionOffset());
+ s += line + QLatin1Char('\n');
+ }
+ s += commentIndent(QString()) + QStringLiteral("; succs: ");
+ if (b->outEdges().isEmpty()) {
+ s += QStringLiteral("<none>");
+ } else {
+ bool first = true;
+ for (MIBlock *succ : b->outEdges()) {
+ if (first)
+ first = false;
+ else
+ s += QStringLiteral(", ");
+ s += QStringLiteral("L%1").arg(succ->index());
+ }
+ }
+ s += QLatin1Char('\n');
+ }
+ s += QLatin1Char('}');
+
+ for (const QStringRef &line : s.splitRef('\n'))
+ qCDebug(lcMI).noquote().nospace() << line;
+}
+
+unsigned MIFunction::extraJSSlots() const
+{
+ uint interpreterFrameSize = CppStackFrame::requiredJSStackFrameSize(irFunction()->v4Function());
+ if (m_jsSlotCount <= interpreterFrameSize)
+ return 0;
+ return m_jsSlotCount - interpreterFrameSize;
+}
+
+void MIFunction::setStartBlock(MIBlock *newStartBlock)
+{
+ auto it = std::find(m_blocks.begin(), m_blocks.end(), newStartBlock);
+ Q_ASSERT(it != m_blocks.end());
+ std::swap(*m_blocks.begin(), *it);
+}
+
+void MIFunction::setStackSlotCounts(unsigned dword, unsigned qword, unsigned js)
+{
+ m_vregCount = 0;
+ m_dwordSlotCount = dword;
+ m_qwordSlotCount = qword;
+ m_jsSlotCount = js;
+}
+
+void MIFunction::verifyCFG() const
+{
+ if (block(MIFunction::StartBlockIndex)->instructions().front().opcode() != Meta::Start)
+ qFatal("MIFunction block 0 is not the start block");
+
+ for (MIBlock *b : m_blocks) {
+ for (MIBlock *in : b->inEdges()) {
+ if (!in->outEdges().contains(b))
+ qFatal("block %u has incoming edge from block %u, "
+ "but does not appear in that block's outgoing edges",
+ b->index(), in->index());
+ }
+ for (MIBlock *out : b->outEdges()) {
+ if (!out->inEdges().contains(b))
+ qFatal("block %u has outgoing edge from block %u, "
+ "but does not appear in that block's incoming edges",
+ b->index(), out->index());
+ }
+ }
+}
+
+MIBlock *MIBlock::findEdgeTo(Operation::Kind target) const
+{
+ for (MIBlock *outEdge : outEdges()) {
+ if (outEdge->instructions().front().opcode() == target)
+ return outEdge;
+ }
+ return nullptr;
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4mi_p.h b/src/qml/jit/qv4mi_p.h
new file mode 100644
index 0000000000..f976d1dc94
--- /dev/null
+++ b/src/qml/jit/qv4mi_p.h
@@ -0,0 +1,627 @@
+/****************************************************************************
+**
+** 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 QV4MI_P_H
+#define QV4MI_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/qv4global_p.h>
+#include <private/qv4ir_p.h>
+#include <private/qv4node_p.h>
+#include <private/qv4operation_p.h>
+
+#include <llvm/ADT/iterator.h>
+#include <llvm/ADT/iterator_range.h>
+#include <llvm/ADT/ilist.h>
+#include <llvm/ADT/ilist_node.h>
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+// This file contains the Machine Interface (MI) data structures, on which ultimately the assembler
+// will operate:
+
+class MIFunction; // containing all basic blocks, and a reference to the IR function
+
+class MIBlock; // containing an ordered sequence of instructions
+
+class MIInstr; // containing operands, and a reference to the IR node, that indicates which
+ // operation is represented by an instruction
+
+class MIOperand; // contains a description of where to get/put the input/result of an operation
+
+// A detail about the stack slots: there two stacks, the JS stack and the native stack. A frame on
+// the native stack is divided in two parts: the quad-word part and the double-word part. The
+// qword part holds 64bit values, like doubles, and pointers on 64bit architectures. The dword part
+// holds 32bit values, like int32s, booleans, and pointers on 32bit architectures. We need to know
+// the type of value a slot holds, because if we have to move it to the JS stack, we have to box it
+// correctly.
+class MIOperand final
+{
+public:
+ enum Kind {
+ Invalid = 0,
+ Constant,
+ VirtualRegister,
+
+ EngineRegister,
+ CppFrameRegister,
+ Function,
+
+ JSStackSlot,
+ BoolStackSlot,
+
+ JumpTarget,
+ };
+
+ using List = QQmlJS::FixedPoolArray<MIOperand>;
+
+public:
+ MIOperand() = default;
+
+ static MIOperand createConstant(Node *irNode)
+ {
+ MIOperand op;
+ op.m_kind = Constant;
+ op.m_irNode = irNode;
+ return op;
+ }
+
+ static MIOperand createVirtualRegister(Node *irNode, unsigned vreg)
+ {
+ MIOperand op;
+ op.m_kind = VirtualRegister;
+ op.m_irNode = irNode;
+ op.m_vreg = vreg;
+ return op;
+ }
+
+ static MIOperand createEngineRegister(Node *irNode)
+ {
+ MIOperand op;
+ op.m_kind = EngineRegister;
+ op.m_irNode = irNode;
+ return op;
+ }
+
+ static MIOperand createCppFrameRegister(Node *irNode)
+ {
+ MIOperand op;
+ op.m_kind = CppFrameRegister;
+ op.m_irNode = irNode;
+ return op;
+ }
+
+ static MIOperand createFunction(Node *irNode)
+ {
+ MIOperand op;
+ op.m_kind = Function;
+ op.m_irNode = irNode;
+ return op;
+ }
+
+ static MIOperand createJSStackSlot(Node *irNode, unsigned slot)
+ {
+ MIOperand op;
+ op.m_kind = JSStackSlot;
+ op.m_irNode = irNode;
+ op.m_slot = slot;
+ return op;
+ }
+
+ static MIOperand createBoolStackSlot(Node *irNode, unsigned slot)
+ {
+ MIOperand op;
+ op.m_kind = BoolStackSlot;
+ op.m_irNode = irNode;
+ op.m_slot = slot;
+ return op;
+ }
+
+ //### or name this createDeoptBlock?
+ static MIOperand createJumpTarget(Node *irNode, MIBlock *targetBlock)
+ {
+ MIOperand op;
+ op.m_kind = JumpTarget;
+ op.m_irNode = irNode;
+ op.m_targetBlock = targetBlock;
+ return op;
+ }
+
+ Kind kind() const
+ { return m_kind; }
+
+ bool isValid() const
+ { return m_kind != Invalid; }
+
+ bool isConstant() const
+ { return m_kind == Constant; }
+
+ bool isVirtualRegister() const
+ { return kind() == VirtualRegister; }
+
+ bool isEngineRegister() const
+ { return kind() == EngineRegister; }
+
+ bool isCppFrameRegister() const
+ { return kind() == CppFrameRegister; }
+
+ bool isFunction() const
+ { return kind() == Function; }
+
+ bool isJSStackSlot() const
+ { return kind() == JSStackSlot; }
+
+ bool isBoolStackSlot() const
+ { return kind() == BoolStackSlot; }
+
+ bool isStackSlot() const
+ { return isJSStackSlot() || isDWordSlot() || isQWordSlot(); }
+
+ bool isJumpTarget() const
+ { return kind() == JumpTarget; }
+
+ Node *irNode() const
+ { return m_irNode; }
+
+ inline Type nodeType(MIFunction *f) const;
+
+ QString debugString() const;
+
+ QV4::Value constantValue() const
+ {
+ Q_ASSERT(isConstant());
+ if (irNode()->opcode() == Meta::Undefined)
+ return QV4::Value::undefinedValue();
+ if (irNode()->opcode() == Meta::Empty)
+ return QV4::Value::emptyValue();
+ return ConstantPayload::get(*irNode()->operation())->value();
+ }
+
+ unsigned virtualRegister() const
+ { Q_ASSERT(isVirtualRegister()); return m_vreg; }
+
+ unsigned stackSlot() const
+ { Q_ASSERT(isStackSlot()); return m_slot; }
+
+ MIBlock *targetBlock() const
+ { Q_ASSERT(isJumpTarget()); return m_targetBlock; }
+
+ inline bool operator==(const MIOperand &other) const
+ {
+ if (kind() != other.kind())
+ return false;
+
+ if (isStackSlot())
+ return stackSlot() == other.stackSlot();
+
+ switch (kind()) {
+ case MIOperand::Invalid:
+ return !other.isValid();
+ case MIOperand::Constant:
+ return constantValue().asReturnedValue() == other.constantValue().asReturnedValue();
+ case MIOperand::VirtualRegister:
+ return virtualRegister() == other.virtualRegister();
+ case MIOperand::JumpTarget:
+ return targetBlock() == other.targetBlock();
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+ }
+
+ bool isDWordSlot() const
+ {
+ switch (kind()) {
+ case BoolStackSlot:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ bool isQWordSlot() const
+ {
+ switch (kind()) {
+ //### TODO: double slots
+ default:
+ return false;
+ }
+ }
+
+ bool overlaps(const MIOperand &other) const
+ {
+ if ((isDWordSlot() && other.isDWordSlot()) || (isQWordSlot() && other.isQWordSlot()))
+ ; // fine, these are the same
+ else if (kind() != other.kind())
+ return false;
+
+ if (isStackSlot())
+ return stackSlot() == other.stackSlot();
+
+ return false;
+ }
+
+private:
+ Node *m_irNode = nullptr;
+ union {
+ unsigned m_vreg;
+ unsigned m_slot;
+ MIBlock *m_targetBlock = nullptr;
+ };
+ Kind m_kind = Invalid;
+};
+
+template <typename NodeTy> struct MIInstrListParentType {};
+template <> struct MIInstrListParentType<MIInstr> { using type = MIBlock; };
+
+template <typename NodeTy> class MIInstrList;
+
+template <typename MISubClass>
+class MIInstrListTraits : public llvm::ilist_noalloc_traits<MISubClass>
+{
+protected:
+ using ListTy = MIInstrList<MISubClass>;
+ using iterator = typename llvm::simple_ilist<MISubClass>::iterator;
+ using ItemParentClass = typename MIInstrListParentType<MISubClass>::type;
+
+public:
+ MIInstrListTraits() = default;
+
+protected:
+ void setListOwner(ItemParentClass *listOwner)
+ { m_owner = listOwner; }
+
+private:
+ ItemParentClass *m_owner = nullptr;
+
+ /// getListOwner - Return the object that owns this list. If this is a list
+ /// of instructions, it returns the BasicBlock that owns them.
+ ItemParentClass *getListOwner() const {
+ return m_owner;
+ }
+
+ static ListTy &getList(ItemParentClass *Par) {
+ return Par->*(Par->getSublistAccess());
+ }
+
+ static MIInstrListTraits<MISubClass> *getSymTab(ItemParentClass *Par) {
+ return Par ? toPtr(Par->getValueSymbolTable()) : nullptr;
+ }
+
+public:
+ void addNodeToList(MISubClass *V) { V->setParent(getListOwner()); }
+ void removeNodeFromList(MISubClass *V) { V->setParent(nullptr); }
+ void transferNodesFromList(MIInstrListTraits &L2, iterator first,
+ iterator last);
+};
+
+template <class T>
+class MIInstrList: public llvm::iplist_impl<llvm::simple_ilist<T>, MIInstrListTraits<T>>
+{
+public:
+ MIInstrList(typename MIInstrListTraits<T>::ItemParentClass *owner)
+ { this->setListOwner(owner); }
+};
+
+class MIInstr final : public llvm::ilist_node_with_parent<MIInstr, MIBlock>
+{
+ Q_DISABLE_COPY_MOVE(MIInstr) // heap use only!
+
+protected:
+ friend class QQmlJS::MemoryPool;
+ MIInstr() : m_operands(nullptr, 0) {}
+
+ explicit MIInstr(Node *irNode, QQmlJS::MemoryPool *pool, unsigned nOperands)
+ : m_irNode(irNode)
+ , m_operands(pool, nOperands)
+ {}
+
+ ~MIInstr() = default;
+
+public:
+ static MIInstr *create(QQmlJS::MemoryPool *pool, Node *irNode, unsigned nOperands);
+
+ MIBlock *parent() const
+ { return m_parent; }
+
+ MIBlock *getParent() const // for ilist_node_with_parent
+ { return parent(); }
+
+ void setParent(MIBlock *parent)
+ { m_parent = parent; }
+
+ Node *irNode() const
+ { return m_irNode; }
+
+ Operation::Kind opcode() const
+ { return m_irNode->opcode(); }
+
+ int position() const
+ { return m_position; }
+
+ inline void insertBefore(MIInstr *insertPos);
+ inline void insertAfter(MIInstr *insertPos);
+ inline MIInstrList<MIInstr>::iterator eraseFromParent();
+
+ bool hasDestination() const
+ { return m_destination.isValid(); }
+
+ MIOperand destination() const
+ { return m_destination; }
+
+ void setDestination(const MIOperand &dest)
+ { m_destination = dest; }
+
+ const MIOperand &operand(unsigned index) const
+ { return m_operands.at(index); }
+
+ void setOperand(unsigned index, const MIOperand &op)
+ { m_operands.at(index) = op; }
+
+ MIOperand &operand(unsigned index)
+ { return m_operands.at(index); }
+
+ const MIOperand::List &operands() const
+ { return m_operands; }
+
+ MIOperand::List &operands()
+ { return m_operands; }
+
+private:
+ friend MIFunction;
+ void setPosition(int newPosition)
+ { m_position = newPosition; }
+
+private:
+ MIBlock *m_parent = nullptr;
+ Node *m_irNode = nullptr;
+ int m_position = -1;
+ MIOperand m_destination;
+ MIOperand::List m_operands;
+};
+
+class MIBlock final
+{
+ Q_DISABLE_COPY_MOVE(MIBlock)
+
+public:
+ using Index = unsigned;
+ enum : Index { InvalidIndex = std::numeric_limits<Index>::max() };
+
+ using MIInstructionList = MIInstrList<MIInstr>;
+
+ using InEdges = QVarLengthArray<MIBlock *, 4>;
+ using OutEdges = QVarLengthArray<MIBlock *, 2>;
+
+protected:
+ friend MIFunction;
+ explicit MIBlock(Index index)
+ : m_instructions(this),
+ m_index(index)
+ {}
+
+ void setIndex(Index newIndex)
+ { m_index = newIndex; }
+
+public:
+ ~MIBlock() = default;
+
+ const MIInstructionList &instructions() const
+ { return m_instructions; }
+
+ MIInstructionList &instructions()
+ { return m_instructions; }
+
+ static MIInstructionList MIBlock::*getSublistAccess(MIInstr * = nullptr)
+ { return &MIBlock::m_instructions; }
+
+ void addArgument(MIOperand &&arg)
+ { m_arguments.push_back(arg); }
+
+ const std::vector<MIOperand> &arguments() const
+ { return m_arguments; }
+
+ std::vector<MIOperand> &arguments()
+ { return m_arguments; }
+
+ void clearArguments()
+ { m_arguments.resize(0); }
+
+ const InEdges &inEdges() const
+ { return m_inEdges; }
+
+ void addInEdge(MIBlock *edge)
+ { m_inEdges.append(edge); }
+
+ const OutEdges &outEdges() const
+ { return m_outEdges; }
+
+ void addOutEdge(MIBlock *edge)
+ { m_outEdges.append(edge); }
+
+ Index index() const
+ { return m_index; }
+
+ MIBlock *findEdgeTo(Operation::Kind target) const;
+
+ bool isDeoptBlock() const
+ { return m_isDeoptBlock; }
+
+ void markAsDeoptBlock()
+ { m_isDeoptBlock = true; }
+
+private:
+ std::vector<MIOperand> m_arguments;
+ MIInstructionList m_instructions;
+ InEdges m_inEdges;
+ OutEdges m_outEdges;
+ Index m_index;
+ bool m_isDeoptBlock = false;
+};
+
+class MIFunction final
+{
+ Q_DISABLE_COPY_MOVE(MIFunction)
+
+public:
+ static constexpr MIBlock::Index StartBlockIndex = 0;
+
+public:
+ MIFunction(Function *irFunction);
+ ~MIFunction()
+ { qDeleteAll(m_blocks); }
+
+ Function *irFunction() const
+ { return m_irFunction; }
+
+ void setStartBlock(MIBlock *newStartBlock);
+ void renumberBlocks();
+ void renumberInstructions();
+
+ void dump(const QString &description) const;
+
+ size_t blockCount() const
+ { return blocks().size(); }
+
+ MIBlock *block(MIBlock::Index index) const
+ { return m_blocks[index]; }
+
+ const std::vector<MIBlock *> &blocks() const
+ { return m_blocks; }
+
+ MIBlock *addBlock()
+ {
+ auto *b = new MIBlock(unsigned(m_blocks.size()));
+ m_blocks.push_back(b);
+ return b;
+ }
+
+ void setBlockOrder(const std::vector<MIBlock *> &newSequence)
+ { m_blocks = newSequence; }
+
+ unsigned vregCount() const
+ { return m_vregCount; }
+
+ void setVregCount(unsigned vregCount)
+ { m_vregCount = vregCount; }
+
+ unsigned dwordSlotCount() const
+ { return m_dwordSlotCount; }
+
+ unsigned qwordSlotCount() const
+ { return m_qwordSlotCount; }
+
+ unsigned jsSlotCount() const
+ { return m_jsSlotCount; }
+
+ unsigned extraJSSlots() const;
+
+ void setStackSlotCounts(unsigned dword, unsigned qword, unsigned js);
+
+ void verifyCFG() const;
+
+private:
+ Function *m_irFunction = nullptr;
+ std::vector<MIBlock *> m_blocks;
+ unsigned m_vregCount = 0;
+ unsigned m_dwordSlotCount = 0;
+ unsigned m_qwordSlotCount = 0;
+ unsigned m_jsSlotCount = 0;
+};
+
+Type MIOperand::nodeType(MIFunction *f) const
+{
+ return f->irFunction()->nodeInfo(irNode())->type();
+}
+
+inline uint qHash(const MIOperand &key, uint seed)
+{
+ uint h = ::qHash(key.kind(), seed);
+ switch (key.kind()) {
+ case MIOperand::VirtualRegister:
+ h ^= key.virtualRegister();
+ break;
+ case MIOperand::BoolStackSlot: Q_FALLTHROUGH();
+ case MIOperand::JSStackSlot:
+ h ^= key.stackSlot();
+ break;
+ default:
+ qFatal("%s: cannot hash %s", Q_FUNC_INFO, key.debugString().toUtf8().constData());
+ }
+ return h;
+}
+
+void MIInstr::insertBefore(MIInstr *insertPos)
+{
+ insertPos->parent()->instructions().insert(insertPos->getIterator(), this);
+}
+
+void MIInstr::insertAfter(MIInstr *insertPos)
+{
+ insertPos->parent()->instructions().insert(++insertPos->getIterator(), this);
+}
+
+MIInstrList<MIInstr>::iterator MIInstr::eraseFromParent()
+{
+ auto p = parent();
+ setParent(nullptr);
+ return p->instructions().erase(getIterator());
+}
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4MI_P_H
diff --git a/src/qml/jit/qv4miblockset_p.h b/src/qml/jit/qv4miblockset_p.h
new file mode 100644
index 0000000000..5f814b99e0
--- /dev/null
+++ b/src/qml/jit/qv4miblockset_p.h
@@ -0,0 +1,291 @@
+/****************************************************************************
+**
+** 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 QV4MIBLOCKSET_P_H
+#define QV4MIBLOCKSET_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 "qv4mi_p.h"
+#include "qv4util_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+class MIBlockSet
+{
+ using Flags = BitVector;
+
+ QVarLengthArray<MIBlock::Index, 8> blockNumbers;
+ Flags *blockFlags = nullptr;
+ MIFunction *function = nullptr;
+ enum { MaxVectorCapacity = 8 };
+
+public:
+ class const_iterator;
+ friend class const_iterator;
+
+public:
+ MIBlockSet(MIFunction *f = nullptr)
+ {
+ if (f)
+ init(f);
+ }
+
+ MIBlockSet(MIBlockSet &&other) noexcept
+ {
+ std::swap(blockNumbers, other.blockNumbers);
+ std::swap(blockFlags, other.blockFlags);
+ std::swap(function, other.function);
+ }
+
+ MIBlockSet(const MIBlockSet &other)
+ : function(other.function)
+ {
+ if (other.blockFlags)
+ blockFlags = new Flags(*other.blockFlags);
+ blockNumbers = other.blockNumbers;
+ }
+
+ MIBlockSet &operator=(const MIBlockSet &other)
+ {
+ if (blockFlags) {
+ delete blockFlags;
+ blockFlags = nullptr;
+ }
+ function = other.function;
+ if (other.blockFlags)
+ blockFlags = new Flags(*other.blockFlags);
+ blockNumbers = other.blockNumbers;
+ return *this;
+ }
+
+ MIBlockSet &operator=(MIBlockSet &&other) noexcept
+ {
+ if (&other != this) {
+ std::swap(blockNumbers, other.blockNumbers);
+
+ delete blockFlags;
+ blockFlags = other.blockFlags;
+ other.blockFlags = nullptr;
+
+ function = other.function;
+ }
+ return *this;
+ }
+
+ ~MIBlockSet()
+ {
+ delete blockFlags;
+ }
+
+ void init(MIFunction *f)
+ {
+ Q_ASSERT(!function);
+ Q_ASSERT(f);
+ function = f;
+ }
+
+ bool empty() const;
+
+ void insert(MIBlock *bb)
+ {
+ Q_ASSERT(function);
+
+ if (blockFlags) {
+ blockFlags->setBit(bb->index());
+ return;
+ }
+
+ for (unsigned int blockNumber : qAsConst(blockNumbers)) {
+ if (blockNumber == bb->index())
+ return;
+ }
+
+ if (blockNumbers.size() == MaxVectorCapacity) {
+ blockFlags = new Flags(int(function->blockCount()), false);
+ for (unsigned int blockNumber : qAsConst(blockNumbers)) {
+ blockFlags->setBit(int(blockNumber));
+ }
+ blockNumbers.clear();
+ blockFlags->setBit(int(bb->index()));
+ } else {
+ blockNumbers.append(bb->index());
+ }
+ }
+
+ void remove(MIBlock *bb)
+ {
+ Q_ASSERT(function);
+
+ if (blockFlags) {
+ blockFlags->clearBit(bb->index());
+ return;
+ }
+
+ for (int i = 0; i < blockNumbers.size(); ++i) {
+ if (blockNumbers[i] == bb->index()) {
+ blockNumbers.remove(i);
+ return;
+ }
+ }
+ }
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ void collectValues(std::vector<MIBlock *> &bbs) const;
+
+ bool contains(MIBlock *bb) const
+ {
+ Q_ASSERT(function);
+
+ if (blockFlags)
+ return blockFlags->at(bb->index());
+
+ for (unsigned int blockNumber : blockNumbers) {
+ if (blockNumber == bb->index())
+ return true;
+ }
+
+ return false;
+ }
+};
+
+class MIBlockSet::const_iterator
+{
+ const MIBlockSet &set;
+ // ### These two members could go into a union, but clang won't compile
+ // (https://codereview.qt-project.org/#change,74259)
+ QVarLengthArray<MIBlock::Index, 8>::const_iterator numberIt;
+ MIBlock::Index flagIt;
+
+ friend class MIBlockSet;
+ const_iterator(const MIBlockSet &set, bool end)
+ : set(set)
+ {
+ if (end || !set.function) {
+ if (!set.blockFlags)
+ numberIt = set.blockNumbers.end();
+ else
+ flagIt = set.blockFlags->size();
+ } else {
+ if (!set.blockFlags)
+ numberIt = set.blockNumbers.begin();
+ else
+ findNextWithFlags(0);
+ }
+ }
+
+ void findNextWithFlags(int start)
+ {
+ flagIt = MIBlock::Index(set.blockFlags->findNext(start, true, /*wrapAround = */false));
+ Q_ASSERT(flagIt <= MIBlock::Index(set.blockFlags->size()));
+ }
+
+public:
+ MIBlock *operator*() const
+ {
+ if (!set.blockFlags)
+ return set.function->block(*numberIt);
+
+ Q_ASSERT(flagIt <= set.function->blockCount());
+ return set.function->block(flagIt);
+
+ }
+
+ bool operator==(const const_iterator &other) const
+ {
+ if (&set != &other.set)
+ return false;
+ if (!set.blockFlags)
+ return numberIt == other.numberIt;
+ return flagIt == other.flagIt;
+ }
+
+ bool operator!=(const const_iterator &other) const
+ { return !(*this == other); }
+
+ const_iterator &operator++()
+ {
+ if (!set.blockFlags)
+ ++numberIt;
+ else
+ findNextWithFlags(flagIt + 1);
+
+ return *this;
+ }
+};
+
+inline bool MIBlockSet::empty() const
+{ return begin() == end(); }
+
+inline MIBlockSet::const_iterator MIBlockSet::begin() const
+{ return const_iterator(*this, false); }
+
+inline MIBlockSet::const_iterator MIBlockSet::end() const
+{ return const_iterator(*this, true); }
+
+inline void MIBlockSet::collectValues(std::vector<MIBlock *> &bbs) const
+{
+ Q_ASSERT(function);
+
+ for (auto it : *this)
+ bbs.push_back(it);
+}
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4MIBLOCKSET_P_H
diff --git a/src/qml/jit/qv4node_p.h b/src/qml/jit/qv4node_p.h
index 679a29764a..76065fb1bc 100644
--- a/src/qml/jit/qv4node_p.h
+++ b/src/qml/jit/qv4node_p.h
@@ -472,17 +472,6 @@ public:
m_worklist.push_back(n);
}
- void reEnqueueLate(Node *n)
- {
- if (!n)
- return;
- State &s = nodeState(n);
- if (s == Queued)
- return;
- s = Queued;
- m_worklist.insert(m_worklist.begin(), n);
- }
-
void enqueueAllInputs(Node *n)
{
for (Node *input : n->inputs())
@@ -519,19 +508,14 @@ public:
Node *n = m_worklist.back();
m_worklist.pop_back();
State &s = nodeState(n);
- if (s == Queued) {
- s = Visited;
- return n;
- }
- Q_UNREACHABLE();
+ Q_ASSERT(s == Queued);
+ s = Visited;
+ return n;
}
return nullptr;
}
- void markAsVisited(Node *n)
- { nodeState(n) = Visited; }
-
bool isVisited(Node *n) const
{ return nodeState(n) == Visited; }
diff --git a/src/qml/jit/qv4operation.cpp b/src/qml/jit/qv4operation.cpp
index 8356a35098..acd5328fd0 100644
--- a/src/qml/jit/qv4operation.cpp
+++ b/src/qml/jit/qv4operation.cpp
@@ -223,11 +223,7 @@ inline Operation *createOperation(Operation::Kind kind, QQmlJS::MemoryPool *stat
case K::JSDeleteName: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow);
case K::JSIn: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
case K::JSInstanceOf: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
- case K::QMLLoadScopeObjectProperty: return get(3, 1, 1, 1, 1, 2, any, F::CanThrow);
- case K::QMLStoreScopeObjectProperty: return get(3, 1, 1, 0, 1, 2, none, F::CanThrow);
- case K::QMLLoadContextObjectProperty: return get(3, 1, 1, 1, 1, 2, any, F::CanThrow);
- case K::QMLStoreContextObjectProperty: return get(3, 1, 1, 1, 1, 2, none, F::CanThrow);
- case K::QMLLoadIdObject: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::QMLLoadQmlContextPropertyLookup: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow);
case K::JSEqual: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow);
case K::JSGreaterThan: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow);
@@ -263,8 +259,6 @@ inline Operation *createOperation(Operation::Kind kind, QQmlJS::MemoryPool *stat
case K::JSTypeofValue: return get(1, 0, 0, 1, 0, 0, any, F::Pure);
case K::JSDeclareVar: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
case K::JSDestructureRestElement: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow);
- case K::QMLLoadContext: return get(0, 0, 0, 1, 0, 0, any, F::NoFlags);
- case K::QMLLoadImportedScripts: return get(0, 0, 0, 1, 0, 0, any, F::NoFlags);
case K::JSCreateCallContext: return get(0, 1, 1, 0, 1, 1, none, F::NoFlags);
case K::JSCreateCatchContext: return get(2, 1, 1, 1, 1, 1, none, F::NoFlags);
@@ -423,7 +417,7 @@ QString ConstantPayload::debugString() const
QString ConstantPayload::debugString(QV4::Value v)
{
if (v.isManaged())
- return QString().sprintf("Ptr: %p", v.heapObject());
+ return QString::asprintf("Ptr: %p", v.heapObject());
if (v.isEmpty())
return QStringLiteral("empty");
return v.toQStringNoThrow();
@@ -518,18 +512,12 @@ static ReturnValue operateOnRuntimeCall(Operation::Kind kind, bool abortOnMissin
case K::JSDeleteName: return M<R::DeleteName>::doIt();
case K::JSIn: return M<R::In>::doIt();
case K::JSInstanceOf: return M<R::Instanceof>::doIt();
- case K::QMLLoadScopeObjectProperty: return M<R::LoadQmlScopeObjectProperty>::doIt();
- case K::QMLStoreScopeObjectProperty: return M<R::StoreQmlScopeObjectProperty>::doIt();
- case K::QMLLoadContextObjectProperty: return M<R::LoadQmlContextObjectProperty>::doIt();
- case K::QMLStoreContextObjectProperty: return M<R::StoreQmlContextObjectProperty>::doIt();
- case K::QMLLoadIdObject: return M<R::LoadQmlIdObject>::doIt();
+ case K::QMLLoadQmlContextPropertyLookup: return M<R::LoadQmlContextPropertyLookup>::doIt();
case K::JSTypeofName: return M<R::TypeofName>::doIt();
case K::JSTypeofValue: return M<R::TypeofValue>::doIt();
case K::JSDeclareVar: return M<R::DeclareVar>::doIt();
case K::JSDestructureRestElement: return M<R::DestructureRestElement>::doIt();
- case K::QMLLoadContext: return M<R::LoadQmlContext>::doIt();
- case K::QMLLoadImportedScripts: return M<R::LoadQmlImportedScripts>::doIt();
case K::JSThisToObject: return M<R::ConvertThisToObject>::doIt();
case K::JSCreateMappedArgumentsObject: return M<R::CreateMappedArgumentsObject>::doIt();
case K::JSCreateUnmappedArgumentsObject: return M<R::CreateUnmappedArgumentsObject>::doIt();
diff --git a/src/qml/jit/qv4operation_p.h b/src/qml/jit/qv4operation_p.h
index 8b66e58d4b..43214023e8 100644
--- a/src/qml/jit/qv4operation_p.h
+++ b/src/qml/jit/qv4operation_p.h
@@ -123,12 +123,10 @@ enum OpKind: uint16_t {
JSDeleteName,
JSIn,
JSInstanceOf,
- /* ok, these are qml object ops, but we don't care for now and treat them a s JS */
- QMLLoadScopeObjectProperty,
- QMLStoreScopeObjectProperty,
- QMLLoadContextObjectProperty,
- QMLStoreContextObjectProperty,
- QMLLoadIdObject,
+
+ /* ok, these are qml object ops, but we don't care for now and treat them as JS */
+ QMLLoadQmlContextPropertyLookup,
+ QMLCallQmlContextPropertyLookup,
JSEqual,
JSGreaterThan,
@@ -168,16 +166,11 @@ enum OpKind: uint16_t {
JSCreateClass,
JSConstruct,
JSConstructWithSpread,
- /* ok, these are qml vararg calls, but we don't care for now and treat them as JS */
- QMLCallScopeObjectProperty,
- QMLCallContextObjectProperty,
JSTypeofName,
JSTypeofValue,
JSDeclareVar,
JSDestructureRestElement,
- QMLLoadContext,
- QMLLoadImportedScripts,
JSThisToObject,
JSCreateMappedArgumentsObject,
JSCreateUnmappedArgumentsObject,
diff --git a/src/qml/jit/qv4schedulers.cpp b/src/qml/jit/qv4schedulers.cpp
new file mode 100644
index 0000000000..0dffefa951
--- /dev/null
+++ b/src/qml/jit/qv4schedulers.cpp
@@ -0,0 +1,912 @@
+/****************************************************************************
+**
+** 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 <QtCore/qloggingcategory.h>
+
+#include "qv4schedulers_p.h"
+#include "qv4util_p.h"
+#include "qv4graph_p.h"
+#include "qv4blockscheduler_p.h"
+#include "qv4stackframe_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcSched, "qt.v4.ir.scheduling")
+Q_LOGGING_CATEGORY(lcDotCFG, "qt.v4.ir.scheduling.cfg")
+
+static bool needsScheduling(Node *n)
+{
+ if (n->operation()->isConstant())
+ return false;
+ switch (n->opcode()) {
+ case Meta::Function: Q_FALLTHROUGH();
+ case Meta::CppFrame:
+ case Meta::Phi:
+ case Meta::EffectPhi:
+ return false;
+ default:
+ return true;
+ }
+}
+
+bool NodeScheduler::canStartBlock(Node *node) const
+{
+ switch (node->operation()->kind()) {
+ case Meta::Start: Q_FALLTHROUGH();
+ case Meta::IfTrue:
+ case Meta::IfFalse:
+ case Meta::Region:
+ case Meta::HandleUnwind:
+ case Meta::OnException:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool NodeScheduler::isControlFlowSplit(Node *node) const
+{
+ int nOutputs = node->operation()->controlOutputCount();
+ if (nOutputs == 2) {
+ // if there is a "missing" control output, it's for exception flow without unwinder
+ int controlUses = 0;
+ auto uses = node->uses();
+ for (auto it = uses.begin(), eit = uses.end(); it != eit; ++it) {
+ if (isLive(*it) && it.isUsedAsControl())
+ ++controlUses;
+ }
+ return controlUses == 2;
+ }
+ return nOutputs > 2;
+}
+
+bool NodeScheduler::isBlockTerminator(Node *node) const
+{
+ switch (node->operation()->kind()) {
+ case Meta::Branch: Q_FALLTHROUGH();
+ case Meta::Jump:
+ case Meta::Return:
+ case Meta::TailCall:
+ case Meta::UnwindDispatch:
+ case Meta::End:
+ return true;
+ case Meta::Call:
+ return isControlFlowSplit(node);
+ default:
+ return false;
+ }
+}
+
+MIBlock *NodeScheduler::getCommonDominator(MIBlock *one, MIBlock *other) const
+{
+ MIBlock::Index a = one->index();
+ MIBlock::Index b = other->index();
+
+ while (a != b) {
+ if (m_dominatorDepthForBlock[a] < m_dominatorDepthForBlock[b])
+ b = m_domTree->immediateDominator(b);
+ else
+ a = m_domTree->immediateDominator(a);
+ }
+
+ return m_miFunction->block(a);
+}
+
+// For Nodes that end up inside loops, it'd be great if we can move (hoist) them out of the loop.
+// To do that, we need a block that preceeds the loop. (So the block before the loop header.)
+// This function calculates that hoist block if the original block is in a loop.
+MIBlock *NodeScheduler::getHoistBlock(MIBlock *block) const
+{
+ if (m_loopInfo->isLoopHeader(block))
+ return m_miFunction->block(m_domTree->immediateDominator(block->index()));
+
+ // make the loop header a candidate:
+ MIBlock *loopHeader = m_loopInfo->loopHeaderFor(block);
+ if (loopHeader == nullptr)
+ return nullptr; // block is not in a loop
+
+ // And now the tricky part: block has to dominate all exits from the loop. If it does not do
+ // that, it meanse that there is an exit from the loop that can be reached before block. In
+ // that case, hoisting from "block" to "loopHeader" would mean there now is an extra calculation
+ // that is not needed for a certain loop exit.
+ for (MIBlock *outEdge : m_loopInfo->loopExitsForLoop(loopHeader)) {
+ if (getCommonDominator(block, outEdge) != block)
+ return nullptr;
+ }
+
+ return m_miFunction->block(m_domTree->immediateDominator(loopHeader->index()));
+}
+
+NodeScheduler::NodeScheduler(Function *irFunction)
+ : m_irFunction(irFunction)
+ , m_vregs(irFunction->graph()->nodeCount(), std::numeric_limits<unsigned>::max())
+ , m_live(irFunction->graph(), /*collectUses =*/ false /* do explicitly NOT collect uses! */)
+{
+}
+
+MIFunction *NodeScheduler::buildMIFunction()
+{
+ m_miFunction = new MIFunction(m_irFunction);
+
+ // step 1: build the CFG
+ auto roots = buildCFG();
+ m_miFunction->renumberBlocks();
+ m_miFunction->dump(QStringLiteral("CFG after renumbering"));
+
+ Q_ASSERT(m_miFunction->block(MIFunction::StartBlockIndex)->index()
+ == MIFunction::StartBlockIndex);
+ Q_ASSERT(m_miFunction->block(MIFunction::StartBlockIndex)->instructions().front().opcode()
+ == Meta::Start);
+
+ // step 2: build the dominator tree
+ if (lcDotCFG().isDebugEnabled())
+ dumpDotCFG();
+ m_domTree.reset(new DominatorTree(m_miFunction));
+ m_dominatorDepthForBlock = m_domTree->calculateNodeDepths();
+
+ // step 3: find loops
+ m_loopInfo.reset(new LoopInfo(*m_domTree.data()));
+ m_loopInfo->detectLoops();
+
+ // step 4: schedule early
+ scheduleEarly(roots);
+ showNodesByBlock(QStringLiteral("nodes per block after early scheduling"));
+
+ // step 5: schedule late
+ scheduleLate(roots);
+ showNodesByBlock(QStringLiteral("nodes per block after late scheduling"));
+
+ // step 6: schedule instructions in each block
+ scheduleNodesInBlock();
+
+ m_miFunction->dump(QStringLiteral("MI before block scheduling"));
+
+ // step 7: order the basic blocks in the CFG
+ BlockScheduler blockScheduler(*m_domTree.data(), *m_loopInfo.data());
+ m_miFunction->setBlockOrder(blockScheduler.scheduledBlockSequence());
+
+ // we're done
+ m_miFunction->renumberInstructions();
+ m_miFunction->setVregCount(m_nextVReg);
+ m_miFunction->dump(QStringLiteral("MI after scheduling"));
+ return m_miFunction;
+}
+
+static Node *splitEdge(Function *irFunction, Node *node, unsigned inputIndex)
+{
+ Graph *g = irFunction->graph();
+ Node *in = node->input(inputIndex);
+ Node *region = g->createNode(g->opBuilder()->getRegion(1), &in, 1);
+ Node *jump = g->createNode(g->opBuilder()->get<Meta::Jump>(), &region, 1);
+
+ qCDebug(lcSched) << "splitting critical edge from node" << node->id()
+ << "to node" << node->input(inputIndex)->id()
+ << "by inserting jump node" << jump->id()
+ << "and region node" << region->id();
+
+ node->replaceInput(inputIndex, jump);
+ return jump;
+}
+
+// See Chapter 6.3.1 of https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf for
+// a description of the algorithm.
+std::vector<Node *> NodeScheduler::buildCFG()
+{
+ std::vector<Node *> roots;
+ roots.reserve(32);
+ NodeWorkList todo(m_irFunction->graph());
+
+ auto enqueueControlInputs = [this, &todo](Node *node) {
+ for (unsigned i = 0, ei = node->operation()->controlInputCount(); i != ei; ++i) {
+ const auto inputIndex = node->operation()->indexOfFirstControl() + i;
+ Node *input = node->input(inputIndex);
+ Q_ASSERT(input);
+ if (node->operation()->kind() == Meta::Region
+ && node->operation()->controlInputCount() > 1
+ && isControlFlowSplit(input)) {
+ // critical edge!
+ input = splitEdge(m_irFunction, node, inputIndex);
+ m_live.markReachable(input);
+ m_live.markReachable(input->controlInput(0));
+ }
+ if (!isBlockTerminator(input)) {
+ auto g = m_irFunction->graph();
+ Node *jump = g->createNode(g->opBuilder()->get<Meta::Jump>(), &input, 1);
+ node->replaceInput(inputIndex, jump);
+ m_live.markReachable(jump);
+ qCDebug(lcSched) << "inserting jump node" << jump->id()
+ << "between node" << node->id()
+ << "and node" << input->id();
+ input = jump;
+ }
+ todo.enqueue(input);
+ }
+ };
+
+ // create the CFG by scheduling control dependencies that start/end blocks:
+ todo.enqueue(m_irFunction->graph()->endNode());
+ while (Node *node = todo.dequeueNextNodeForVisiting()) {
+ Q_ASSERT(isBlockTerminator(node));
+
+ if (schedulerData(node)->minimumBlock)
+ continue;
+
+ MIBlock *b = m_miFunction->addBlock();
+
+ qCDebug(lcSched) << "scheduling node" << node->id() << "as terminator for new block"
+ << b->index();
+ b->instructions().push_front(createMIInstruction(node));
+ placeFixed(node, b, Schedule);
+ roots.push_back(node);
+
+ if (Node *framestate = node->frameStateInput()) {
+ placeFixed(framestate, b, DontSchedule);
+ qCDebug(lcSched) << ".. also scheduling framestate dependency node" << node->id()
+ << "in block" << b->index();
+ }
+
+ if (node->opcode() == Meta::End) {
+ enqueueControlInputs(node);
+ continue;
+ }
+
+ while (true) {
+ Node *controlDependency = node->controlInput(0);
+ if (!controlDependency)
+ break;
+ if (todo.isVisited(controlDependency))
+ break;
+ if (schedulerData(controlDependency)->isFixed)
+ break;
+
+ if (controlDependency->opcode() == Meta::Start) {
+ qCDebug(lcSched) << "placing start node" << controlDependency->id()
+ << "in block" << b->index();
+ handleStartNode(controlDependency, b);
+ placeFixed(controlDependency, b, Schedule);
+ roots.push_back(controlDependency);
+ break; // we're done with this block
+ }
+ if (isBlockTerminator(controlDependency)) {
+ qCDebug(lcSched) << "found terminator node" << controlDependency->id()
+ << "for another block, so finish block" << b->index();
+ Node *merge = m_irFunction->graph()->createNode(
+ m_irFunction->graph()->opBuilder()->getRegion(1), &controlDependency, 1);
+ node->replaceInput(node->operation()->indexOfFirstControl(), merge);
+ addBlockStart(roots, merge, b);
+ placeFixed(merge, b, Schedule);
+ m_live.markReachable(merge);
+ todo.enqueue(controlDependency);
+ break; // we're done with this block
+ }
+ if (canStartBlock(controlDependency)
+ || schedulerData(controlDependency->controlInput())->isFixed) {
+ qCDebug(lcSched) << "found block start node" << controlDependency->id()
+ << "for this block, so finish block" << b->index();
+ addBlockStart(roots, controlDependency, b);
+ placeFixed(controlDependency, b, Schedule);
+ roots.push_back(controlDependency);
+ enqueueControlInputs(controlDependency);
+ break; // we're done with this block
+ }
+ qCDebug(lcSched) << "skipping node" << controlDependency->id();
+ node = controlDependency;
+ }
+ }
+
+ // link the edges of the MIBlocks, and add basic-block arguments:
+ for (MIBlock *toBlock : m_miFunction->blocks()) {
+ Q_ASSERT(!toBlock->instructions().empty());
+ MIInstr &instr = toBlock->instructions().front();
+ Node *toNode = instr.irNode();
+ const auto opcode = toNode->operation()->kind();
+ if (opcode == Meta::Region) {
+ unsigned inputNr = 0;
+ for (Node *input : toNode->inputs()) {
+ MIBlock *fromBlock = schedulerData(input)->minimumBlock;
+ fromBlock->addOutEdge(toBlock);
+ toBlock->addInEdge(fromBlock);
+ MIInstr &fromTerminator = fromBlock->instructions().back();
+ if (fromTerminator.irNode()->opcode() == Meta::Jump ||
+ fromTerminator.irNode()->opcode() == Meta::UnwindDispatch) {
+ unsigned arg = 0;
+ for (const MIOperand &bbArg : toBlock->arguments()) {
+ fromTerminator.setOperand(arg++,
+ createMIOperand(bbArg.irNode()->input(inputNr)));
+ }
+ }
+ ++inputNr;
+ }
+ } else if (opcode == Meta::End) {
+ for (Node *input : toNode->inputs()) {
+ MIBlock *fromBlock = schedulerData(input)->minimumBlock;
+ fromBlock->addOutEdge(toBlock);
+ toBlock->addInEdge(fromBlock);
+ }
+ } else if (Node *fromNode = toNode->controlInput()) {
+ MIBlock *fromBlock = schedulerData(fromNode)->minimumBlock;
+ fromBlock->addOutEdge(toBlock);
+ toBlock->addInEdge(fromBlock);
+ }
+ }
+
+ m_irFunction->dump(QStringLiteral("graph after building CFG"));
+
+ auto startBlock = schedulerData(m_irFunction->graph()->startNode())->minimumBlock;
+ m_miFunction->setStartBlock(startBlock);
+
+ if (lcSched().isDebugEnabled())
+ m_miFunction->dump(QStringLiteral("control flow graph before renumbering"));
+ m_miFunction->verifyCFG();
+
+ return roots;
+}
+
+// See Chapter 6.3.3 of https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf for
+// a description of the algorithm.
+void NodeScheduler::scheduleEarly(const std::vector<Node *> &roots)
+{
+ // scheduling one node might have the effect of queueing its dependencies
+ NodeWorkList todo(m_irFunction->graph());
+ for (Node *root : roots) {
+ todo.enqueue(root);
+ while (Node *node = todo.dequeueNextNodeForVisiting())
+ scheduleEarly(node, todo);
+ }
+}
+
+void NodeScheduler::scheduleEarly(Node *node, NodeWorkList &todo)
+{
+ qCDebug(lcSched) << "Scheduling node" << node->id() << "early...";
+
+ SchedulerData *sd = schedulerData(node);
+
+ if (sd->isFixed) {
+ // Fixed nodes already know their schedule early position.
+ qCDebug(lcSched) << ".. Fixed node" << node->id() << "is on minimum block"
+ << sd->minimumBlock->index()
+ << "which has dominator depth"
+ << m_dominatorDepthForBlock[sd->minimumBlock->index()];
+ }
+
+ for (Node *use : node->uses()) {
+ if (isLive(use))
+ propagateMinimumPosition(sd->minimumBlock, use, todo);
+ else
+ qCDebug(lcSched) << ".. Skipping node" << use->id() << "as it's not live";
+ }
+}
+
+void NodeScheduler::propagateMinimumPosition(MIBlock *newMinimumPosition, Node *toNode,
+ NodeWorkList &todo)
+{
+ Q_ASSERT(newMinimumPosition);
+
+ SchedulerData *sd = schedulerData(toNode);
+ if (sd->isFixed) // nothing to do
+ return;
+
+ MIBlock::Index minimumBlockIndex = sd->minimumBlock
+ ? sd->minimumBlock->index()
+ : MIFunction::StartBlockIndex;
+ Q_ASSERT(m_domTree->insideSameDominatorChain(newMinimumPosition->index(), minimumBlockIndex));
+ if (sd->minimumBlock == nullptr
+ || m_dominatorDepthForBlock[newMinimumPosition->index()]
+ > m_dominatorDepthForBlock[minimumBlockIndex]) {
+ // ok, some input for toNode is scheduled *after* our current minimum depth, so we need
+ // to adjust out minimal position. (This might involve rescheduling toNode's uses.)
+ place(toNode, newMinimumPosition);
+ todo.reEnqueue(toNode);
+ qCDebug(lcSched) << ".. Propagating minimum block" << sd->minimumBlock->index()
+ << "which has dominator depth"
+ << m_dominatorDepthForBlock[newMinimumPosition->index()]
+ << "to use node" << toNode->id();
+ } else {
+ qCDebug(lcSched) << ".. Minimum position" << newMinimumPosition->index()
+ << "is not better than" << minimumBlockIndex
+ << "for node" << toNode->id();
+ }
+}
+
+// See Chapter 6.3.4 of https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf for
+// a description of the algorithm.
+//
+// There is one extra detail not described in the thesis mentioned above: loop hoisting. Before we
+// place a node in the latest block that dominates all uses, we check if we accidentally sink it
+// *into* a loop (meaning the latest block is inside a loop, where it is not if the earliest
+// possible block would be chosen). If we detect that a nodes is going to sink into a loop, we walk
+// the dominator path from the latest block up to the earliest block, and pick the first block that
+// is in the same loop (if any) as the earlieast block.
+//
+// As noted in the thesis, this strategy might enlongen life times, which could be harmful for
+// values that are simple to re-materialized or re-calculate.
+void NodeScheduler::scheduleLate(const std::vector<Node *> &roots)
+{
+ NodeWorkList todo(m_irFunction->graph());
+ for (Node *root : roots)
+ todo.enqueue(root);
+
+ while (Node *node = todo.dequeueNextNodeForVisiting())
+ scheduleNodeLate(node, todo);
+}
+
+void NodeScheduler::scheduleNodeLate(Node *node, NodeWorkList &todo)
+{
+ if (!needsScheduling(node))
+ return;
+ qCDebug(lcSched) << "Scheduling node" << node->id() << "late...";
+
+ auto sd = schedulerData(node);
+ if (sd->unscheduledUses == SchedulerData::NotYetCalculated) {
+ sd->unscheduledUses = 0;
+ for (Node *use : node->uses()) {
+ if (!isLive(use))
+ continue;
+ if (!needsScheduling(use))
+ continue;
+ if (schedulerData(use)->isFixed)
+ continue;
+ todo.enqueue(use);
+ ++sd->unscheduledUses;
+ }
+ }
+
+ if (sd->isFixed) {
+ qCDebug(lcSched) << ".. it's fixed";
+ enqueueInputs(node, todo);
+ return;
+ }
+
+ if (sd->unscheduledUses) {
+ qCDebug(lcSched).noquote() << ".. not all uses are fixed, postpone it."<< todo.status(node);
+ return;
+ }
+
+ MIBlock *&minBlock = sd->minimumBlock;
+ if (minBlock == nullptr)
+ minBlock = m_miFunction->block(MIFunction::StartBlockIndex);
+ MIBlock *commonUseDominator = commonDominatorOfUses(node);
+ qCDebug(lcSched) << ".. common use dominator: block" << commonUseDominator->index();
+
+ // the minBlock has to dominate the uses, *and* the common dominator of the uses.
+ Q_ASSERT(minBlock->index() == commonUseDominator->index() ||
+ m_domTree->dominates(minBlock->index(), commonUseDominator->index()));
+
+ // we now found the deepest block, so use it as the target block:
+ MIBlock *targetBlock = commonUseDominator;
+
+ if (node->opcode() == Meta::FrameState) {
+ // never hoist framestates: they're used (among other things) to keep their inputs alive, so
+ // hoisting them out would end the life-time of those inputs prematurely
+ } else {
+ // but we want to prevent blocks sinking into loops unnecessary
+ MIBlock *hoistBlock = getHoistBlock(targetBlock);
+ while (hoistBlock
+ && m_dominatorDepthForBlock[hoistBlock->index()]
+ >= m_dominatorDepthForBlock[minBlock->index()]) {
+ qCDebug(lcSched) << ".. hoisting node" << node->id() << "from block"
+ << targetBlock->index() << "to block" << hoistBlock->index();
+ // ok, so there a) is a hoist block and b) it's deeper than the minimum block,
+ // so lift it up one level ...
+ targetBlock = hoistBlock;
+ // ... and see if we can lift it one more level
+ hoistBlock = getHoistBlock(targetBlock);
+ }
+ }
+
+ qCDebug(lcSched) << ".. fixating it in block" << targetBlock->index()
+ << "where the minimum block was" << minBlock->index();
+
+ placeFixed(node, targetBlock, DontSchedule);
+ enqueueInputs(node, todo);
+}
+
+void NodeScheduler::enqueueInputs(Node *node, NodeWorkList &todo)
+{
+ for (Node *input : node->inputs()) {
+ if (!input)
+ continue;
+ if (!needsScheduling(input))
+ continue;
+ if (!isLive(input))
+ continue;
+ auto sd = schedulerData(input);
+ if (sd->isFixed)
+ continue;
+ qCDebug(lcSched).noquote() << "... enqueueing input node" << input->id()
+ << todo.status(input);
+ if (sd->unscheduledUses != SchedulerData::NotYetCalculated) {
+ if (sd->unscheduledUses > 0)
+ --sd->unscheduledUses;
+ if (sd->unscheduledUses == 0)
+ todo.reEnqueue(input);
+ } else {
+ todo.reEnqueue(input);
+ }
+ }
+}
+
+Node *NodeScheduler::firstNotFixedUse(Node *node)
+{
+ for (Node *use : node->uses()) {
+ if (!isLive(use))
+ continue;
+ if (!schedulerData(use)->isFixed)
+ return use;
+ }
+ return nullptr;
+}
+
+MIBlock *NodeScheduler::commonDominatorOfUses(Node *node)
+{
+ MIBlock *commonDominator = nullptr;
+ for (auto useIt = node->uses().begin(), useEIt = node->uses().end(); useIt != useEIt; ++useIt) {
+ Node *use = *useIt;
+ if (!isLive(use))
+ continue;
+ // region nodes use other nodes through their control dependency. But those nodes should
+ // already have been placed as block terminators before.
+ Q_ASSERT(use->opcode() != Meta::Region);
+ if (use->opcode() == Meta::Phi || use->opcode() == Meta::EffectPhi) {
+ // find the predecessor block defining this input
+ Node *region = use->controlInput(0);
+ Node *input = region->controlInput(useIt.inputIndex());
+ use = input;
+ }
+ auto minBlock = schedulerData(use)->minimumBlock;
+ if (commonDominator == nullptr)
+ commonDominator = minBlock;
+ else
+ commonDominator = getCommonDominator(commonDominator, minBlock);
+ }
+ return commonDominator;
+}
+
+void NodeScheduler::scheduleNodesInBlock()
+{
+ auto startBlock = m_miFunction->block(MIFunction::StartBlockIndex);
+ for (Node *n : m_live.reachable()) {
+ auto sd = schedulerData(n);
+ if (!sd->minimumBlock)
+ sd->minimumBlock = startBlock;
+ }
+
+ std::vector<std::vector<SchedulerData *>> nodesForBlock;
+ nodesForBlock.resize(m_miFunction->blockCount());
+
+ for (auto sd : m_schedulerData) {
+ if (sd == nullptr)
+ continue;
+ if (!isLive(sd->node))
+ continue;
+ sd->unscheduledUses = 0;
+ for (Node *use : sd->node->uses()) {
+ if (!needsScheduling(use))
+ continue;
+ if (schedulerData(use)->isScheduledInBlock)
+ continue;
+ if (schedulerData(use)->minimumBlock == sd->minimumBlock)
+ ++sd->unscheduledUses;
+ }
+ if (sd->unscheduledUses == 0)
+ nodesForBlock[sd->minimumBlock->index()].push_back(sd);
+ }
+
+ NodeWorkList todo(m_irFunction->graph());
+ for (MIBlock *b : m_miFunction->blocks()) {
+ qCDebug(lcSched) << "Scheduling inside block" << b->index();
+ MIInstr *insertionPoint = &b->instructions().back();
+ todo.enqueue(insertionPoint->irNode());
+ scheduleNodesInBlock(insertionPoint, b, todo);
+ Q_ASSERT(todo.isEmpty());
+ for (auto sd : nodesForBlock[b->index()]) {
+ if (!sd->isScheduledInBlock)
+ todo.enqueue(sd->node);
+ }
+ scheduleNodesInBlock(insertionPoint, b, todo);
+ Q_ASSERT(todo.isEmpty());
+ todo.reset();
+ }
+}
+
+void NodeScheduler::scheduleNodesInBlock(MIInstr *&insertionPoint, MIBlock *b, NodeWorkList &todo)
+{
+ while (Node *n = todo.dequeueNextNodeForVisiting())
+ scheduleNodeInBlock(n, insertionPoint, b, todo);
+}
+
+void NodeScheduler::scheduleNodeInBlock(Node *node, MIInstr *&insertionPoint, MIBlock *b,
+ NodeWorkList &todo)
+{
+ Q_ASSERT(!node->isDead());
+
+ if (!isLive(node))
+ return;
+
+ if (!needsScheduling(node))
+ return;
+
+ auto nodeData = schedulerData(node);
+ if (nodeData->minimumBlock != b)
+ return;
+
+ const bool wasAlreadyScheduled = nodeData->isScheduledInBlock;
+ if (!wasAlreadyScheduled) {
+ if (nodeData->unscheduledUses)
+ return;
+
+ scheduleNodeNow(node, insertionPoint);
+ }
+
+ if (Node *framestate = node->frameStateInput())
+ scheduleNodeInBlock(framestate, insertionPoint, b, todo);
+
+ for (Node *input : node->inputs()) {
+ if (!input)
+ continue;
+ if (!needsScheduling(input))
+ continue;
+ if (!isLive(input))
+ continue;
+ auto inputInfo = schedulerData(input);
+ if (inputInfo->isScheduledInBlock)
+ continue;
+ Q_ASSERT(inputInfo->minimumBlock != nullptr);
+ if (inputInfo->minimumBlock != b)
+ continue;
+ Q_ASSERT(!input->isDead());
+ Q_ASSERT(inputInfo->unscheduledUses != SchedulerData::NotYetCalculated);
+ if (!wasAlreadyScheduled && inputInfo->unscheduledUses > 0)
+ --inputInfo->unscheduledUses;
+ if (inputInfo->unscheduledUses == 0)
+ todo.enqueue(input);
+ }
+}
+
+void NodeScheduler::scheduleNodeNow(Node *node, MIInstr *&insertionPoint)
+{
+ qCDebug(lcSched) << ".. scheduling node" << node->id()
+ << "in block" << insertionPoint->parent()->index()
+ << "before node" << insertionPoint->irNode()->id();
+
+ MIInstr *newInstr = createMIInstruction(node);
+ newInstr->insertBefore(insertionPoint);
+ insertionPoint = newInstr;
+}
+
+void NodeScheduler::place(Node *node, MIBlock *b)
+{
+ Q_ASSERT(!node->isDead());
+
+ if (b == nullptr)
+ return;
+
+ schedulerData(node)->minimumBlock = b;
+}
+
+void NodeScheduler::placeFixed(Node *node, MIBlock *b, ScheduleOrNot markScheduled)
+{
+ place(node, b);
+ auto sd = schedulerData(node);
+ Q_ASSERT(!sd->isFixed);
+ sd->isFixed = true;
+ sd->isScheduledInBlock = markScheduled == Schedule;
+}
+
+unsigned NodeScheduler::vregForNode(Node *node)
+{
+ unsigned &vreg = m_vregs[unsigned(node->id())];
+ if (vreg == std::numeric_limits<unsigned>::max())
+ vreg = m_nextVReg++;
+ return vreg;
+}
+
+void NodeScheduler::addBlockStart(std::vector<Node *> &roots, Node *startNode, MIBlock *block)
+{
+ block->instructions().insert(block->instructions().begin(), createMIInstruction(startNode));
+ if (startNode->opcode() == Meta::Region) {
+ for (Node *use : startNode->uses()) {
+ if (use->opcode() == Meta::Phi && isLive(use)) {
+ block->addArgument(MIOperand::createVirtualRegister(use, vregForNode(use)));
+ placeFixed(use, block, Schedule);
+ roots.push_back(use);
+ } else if (use->opcode() == Meta::EffectPhi && isLive(use)) {
+ placeFixed(use, block, Schedule);
+ roots.push_back(use);
+ }
+ }
+ }
+}
+
+void NodeScheduler::handleStartNode(Node *startNode, MIBlock *startBlock)
+{
+ startBlock->instructions().push_front(createMIInstruction(startNode));
+
+ QVarLengthArray<Node *, 32> args;
+ for (Node *use : startNode->uses()) {
+ switch (use->opcode()) {
+ case Meta::Engine: Q_FALLTHROUGH();
+ case Meta::CppFrame:
+ case Meta::Function:
+ placeFixed(use, startBlock, Schedule);
+ break;
+ case Meta::Parameter: {
+ auto param = ParameterPayload::get(*use->operation());
+ int idx = int(param->parameterIndex());
+ if (args.size() <= idx)
+ args.resize(idx + 1);
+ args[int(idx)] = use;
+ placeFixed(use, startBlock, Schedule);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (unsigned i = 0, ei = unsigned(args.size()); i != ei; ++i) {
+ if (Node *arg = args.at(int(i)))
+ startBlock->addArgument(MIOperand::createJSStackSlot(arg, i));
+ }
+}
+
+static Node *firstControlOutput(Node *n)
+{
+ for (auto it = n->uses().begin(), eit = n->uses().end(); it != eit; ++it) {
+ if (it.isUsedAsControl())
+ return *it;
+ }
+ return nullptr;
+}
+
+MIInstr *NodeScheduler::createMIInstruction(Node *node)
+{
+ const auto opcode = node->operation()->kind();
+
+ unsigned nArgs = 0;
+ switch (opcode) {
+ case Meta::UnwindDispatch: Q_FALLTHROUGH();
+ case Meta::Jump: {
+ Node *target = firstControlOutput(node);
+ if (target->opcode() == Meta::Region) {
+ for (Node *n : target->uses()) {
+ if (n->opcode() == Meta::Phi && isLive(n))
+ ++nArgs;
+ }
+ }
+ }
+ break;
+ case Meta::Branch:
+ nArgs = 1;
+ break;
+ case Meta::Return:
+ nArgs = 1;
+ break;
+ default:
+ nArgs = node->operation()->valueInputCount();
+ break;
+ }
+
+ MIInstr *instr = MIInstr::create(m_irFunction->pool(), node, nArgs);
+ for (unsigned i = 0, ei = node->operation()->valueInputCount(); i != ei; ++i)
+ instr->setOperand(i, createMIOperand(node->input(i)));
+ if (node->opcode() != Meta::Start && node->operation()->valueOutputCount() > 0)
+ instr->setDestination(createMIOperand(node));
+
+ schedulerData(node)->isScheduledInBlock = true;
+ return instr;
+}
+
+MIOperand NodeScheduler::createMIOperand(Node *node)
+{
+ if (node->operation()->isConstant())
+ return MIOperand::createConstant(node);
+
+ auto opcode = node->operation()->kind();
+ switch (opcode) {
+ case Meta::Parameter:
+ return MIOperand::createJSStackSlot(
+ node, unsigned(ParameterPayload::get(*node->operation())->parameterIndex()));
+ case Meta::Engine:
+ return MIOperand::createEngineRegister(node);
+ case Meta::CppFrame:
+ return MIOperand::createCppFrameRegister(node);
+ case Meta::Function:
+ return MIOperand::createFunction(node);
+ default:
+ if ((node->opcode() == Meta::Call
+ && CallPayload::get(*node->operation())->callee() == Meta::JSThisToObject)
+ || node->opcode() == Meta::StoreThis) {
+ return MIOperand::createJSStackSlot(node, CallData::This);
+ } else {
+ return MIOperand::createVirtualRegister(node, vregForNode(node));
+ }
+ }
+}
+
+void NodeScheduler::showNodesByBlock(const QString &description) const
+{
+ if (!lcSched().isDebugEnabled())
+ return;
+
+ qCDebug(lcSched) << description;
+
+ for (MIBlock *b : m_miFunction->blocks()) {
+ QString s;
+ for (const SchedulerData *sd : m_schedulerData) {
+ if (!sd)
+ continue;
+ if (!isLive(sd->node))
+ continue;
+ if (sd->minimumBlock == b) {
+ if (!s.isEmpty())
+ s += QStringLiteral(", ");
+ s += QStringLiteral("%1 (%2)").arg(QString::number(sd->node->id()),
+ sd->node->operation()->debugString());
+ }
+ }
+ if (s.isEmpty())
+ s = QStringLiteral("<<none>>");
+ qCDebug(lcSched, "Nodes in block %u: %s", b->index(), s.toUtf8().constData());
+ }
+}
+
+void NodeScheduler::dumpDotCFG() const
+{
+ QString out;
+ out += QLatin1Char('\n');
+ out += QStringLiteral("digraph{root=\"L%1\" label=\"Control Flow Graph\";"
+ "node[shape=circle];edge[dir=forward fontsize=10]\n")
+ .arg(MIFunction::StartBlockIndex);
+ for (MIBlock *src : m_miFunction->blocks()) {
+ for (MIBlock *dst : src->outEdges()) {
+ out += QStringLiteral("L%1->L%2\n").arg(QString::number(src->index()),
+ QString::number(dst->index()));
+ }
+ }
+ out += QStringLiteral("}\n");
+ qCDebug(lcDotCFG).nospace().noquote() << out;
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4schedulers_p.h b/src/qml/jit/qv4schedulers_p.h
new file mode 100644
index 0000000000..f9179816df
--- /dev/null
+++ b/src/qml/jit/qv4schedulers_p.h
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** 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 QV4SCHEDULER_P_H
+#define QV4SCHEDULER_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 "qv4global_p.h"
+#include "qv4mi_p.h"
+#include "qv4node_p.h"
+#include "qv4domtree_p.h"
+#include "qv4loopinfo_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+// Node scheduling "flattens" the graph into basic blocks with an ordered list of instructions.
+//
+// The various steps are mentioned in buildMIFunction, but the general idea is described in
+// https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf in chapter 6.
+class NodeScheduler final
+{
+ Q_DISABLE_COPY_MOVE(NodeScheduler)
+
+ class SchedulerData final {
+ Q_DISABLE_COPY_MOVE(SchedulerData)
+ public:
+ static SchedulerData *create(QQmlJS::MemoryPool *pool)
+ { return pool->New<SchedulerData>(); }
+
+ SchedulerData() = default;
+ ~SchedulerData() = default;
+
+ Node *node = nullptr;
+ MIBlock *minimumBlock = nullptr;
+ bool isFixed = false;
+ bool isScheduledInBlock = false;
+ static constexpr unsigned NotYetCalculated = std::numeric_limits<unsigned>::max();
+ unsigned unscheduledUses = NotYetCalculated;
+ };
+
+public:
+ NodeScheduler(Function *irFunction);
+ ~NodeScheduler() = default;
+
+ MIFunction *buildMIFunction();
+
+private:
+ std::vector<Node *> buildCFG();
+ void scheduleEarly(const std::vector<Node *> &roots);
+ void scheduleEarly(Node *node, NodeWorkList &todo);
+ void propagateMinimumPosition(MIBlock *newMinimumPosition, Node *toNode, NodeWorkList &todo);
+ void scheduleLate(const std::vector<Node *> &roots);
+ void scheduleNodeLate(Node *node, NodeWorkList &todo);
+ void enqueueInputs(Node *node, NodeWorkList &todo);
+ Node *firstNotFixedUse(Node *node);
+ MIBlock *commonDominatorOfUses(Node *node);
+ void scheduleNodesInBlock();
+ void scheduleNodesInBlock(MIInstr *&insertionPoint, MIBlock *b, NodeWorkList &todo);
+ void scheduleNodeInBlock(Node *node, MIInstr *&insertionPoint, MIBlock *b, NodeWorkList &todo);
+ void scheduleNodeNow(Node *node, MIInstr *&insertionPoint);
+
+ void place(Node *node, MIBlock *b);
+ enum ScheduleOrNot { DontSchedule, Schedule };
+ void placeFixed(Node *node, MIBlock *b, ScheduleOrNot markScheduled);
+ unsigned vregForNode(Node *node);
+ void addBlockStart(std::vector<Node *> &roots, Node *startNode, MIBlock *block);
+ void enqueueControlInputs(Node *node);
+ void handleStartNode(Node *startNode, MIBlock *startBlock);
+ MIInstr *createMIInstruction(Node *node);
+ MIOperand createMIOperand(Node *node);
+ SchedulerData *schedulerData(Node *n)
+ {
+ if (Q_UNLIKELY(m_schedulerData.size() <= n->id()))
+ m_schedulerData.resize(n->id() + 8);
+ SchedulerData *&sd = m_schedulerData[n->id()];
+ if (Q_UNLIKELY(sd == nullptr)) {
+ sd = SchedulerData::create(m_irFunction->pool());
+ sd->node = n;
+ }
+ return sd;
+ }
+ bool isLive(Node *n) const
+ { return m_live.isReachable(n->id()); }
+ bool canStartBlock(Node *node) const;
+ bool isControlFlowSplit(Node *node) const;
+ bool isBlockTerminator(Node *node) const;
+ MIBlock *getCommonDominator(MIBlock *one, MIBlock *other) const;
+ MIBlock *getHoistBlock(MIBlock *block) const;
+
+ void showNodesByBlock(const QString &description) const;
+
+ void dumpDotCFG() const;
+
+private:
+ Function *m_irFunction = nullptr;
+ MIFunction *m_miFunction = nullptr;
+ QScopedPointer<LoopInfo> m_loopInfo;
+ QScopedPointer<DominatorTree> m_domTree;
+ std::vector<int> m_dominatorDepthForBlock;
+ std::vector<unsigned> m_vregs;
+ std::vector<SchedulerData *> m_schedulerData;
+ NodeCollector m_live;
+ unsigned m_nextVReg = 0;
+};
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4SCHEDULER_P_H
diff --git a/src/qml/jit/qv4tracingjit.cpp b/src/qml/jit/qv4tracingjit.cpp
index ded2488905..c8974b3a1b 100644
--- a/src/qml/jit/qv4tracingjit.cpp
+++ b/src/qml/jit/qv4tracingjit.cpp
@@ -41,6 +41,9 @@
#include "qv4vme_moth_p.h"
#include "qv4graphbuilder_p.h"
+#include "qv4lowering_p.h"
+#include "qv4mi_p.h"
+#include "qv4schedulers_p.h"
QT_BEGIN_NAMESPACE
@@ -73,6 +76,15 @@ void Moth::runTracingJit(QV4::Function *function)
IR::GraphBuilder::buildGraph(&irFunction);
irFunction.dump(QStringLiteral("initial IR"));
irFunction.verify();
+
+ IR::GenericLowering(irFunction).lower();
+ irFunction.dump(QStringLiteral("after generic lowering"));
+ irFunction.verify();
+
+ IR::NodeScheduler scheduler(&irFunction);
+ QScopedPointer<IR::MIFunction> miFunction(scheduler.buildMIFunction());
+ miFunction->dump(QStringLiteral("initial MI"));
+ irFunction.verify();
}
} // QV4 namespace
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp
index e20317cff1..e0bd986920 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>
@@ -1368,7 +1367,7 @@ QObject *QJSValue::toQObject() const
\since 5.8
* If this QJSValue is a QMetaObject, returns the QMetaObject pointer
- * that the QJSValue represents; otherwise, returns 0.
+ * that the QJSValue represents; otherwise, returns \nullptr.
*
* \sa isQMetaObject()
*/
diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp
index 4a21f62cf2..98e0ef9e70 100644
--- a/src/qml/jsruntime/qv4argumentsobject.cpp
+++ b/src/qml/jsruntime/qv4argumentsobject.cpp
@@ -116,6 +116,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 +151,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 +195,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 b975d02d9d..b3e607d74a 100644
--- a/src/qml/jsruntime/qv4arrayobject.cpp
+++ b/src/qml/jsruntime/qv4arrayobject.cpp
@@ -103,7 +103,9 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor)
ctor->defineDefaultProperty(QStringLiteral("from"), method_from, 1);
ctor->addSymbolSpecies();
- ScopedObject unscopables(scope, engine->newObject(engine->classes[EngineBase::Class_Empty]->changeVTable(QV4::Object::staticVTable())));
+ Scoped<InternalClass> ic(scope, engine->classes[EngineBase::Class_Empty]
+ ->changeVTable(QV4::Object::staticVTable()));
+ ScopedObject unscopables(scope, engine->newObject(ic->d()));
ScopedString name(scope);
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(engine->id_toString(), method_toString, 0);
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index c04617dac0..18927c637c 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -470,11 +470,17 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
jsObjects[TypeError_Ctor] = memoryManager->allocate<TypeErrorCtor>(global);
jsObjects[URIError_Ctor] = memoryManager->allocate<URIErrorCtor>(global);
jsObjects[IteratorProto] = memoryManager->allocate<IteratorPrototype>();
- jsObjects[ForInIteratorProto] = memoryManager->allocObject<ForInIteratorPrototype>(newInternalClass(ForInIteratorPrototype::staticVTable(), iteratorPrototype()));
- jsObjects[MapIteratorProto] = memoryManager->allocObject<MapIteratorPrototype>(newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype()));
- jsObjects[SetIteratorProto] = memoryManager->allocObject<SetIteratorPrototype>(newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype()));
- jsObjects[ArrayIteratorProto] = memoryManager->allocObject<ArrayIteratorPrototype>(newInternalClass(ArrayIteratorPrototype::staticVTable(), iteratorPrototype()));
- jsObjects[StringIteratorProto] = memoryManager->allocObject<StringIteratorPrototype>(newInternalClass(StringIteratorPrototype::staticVTable(), iteratorPrototype()));
+
+ ic = newInternalClass(ForInIteratorPrototype::staticVTable(), iteratorPrototype());
+ jsObjects[ForInIteratorProto] = memoryManager->allocObject<ForInIteratorPrototype>(ic);
+ ic = newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype());
+ jsObjects[MapIteratorProto] = memoryManager->allocObject<MapIteratorPrototype>(ic);
+ ic = newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype());
+ jsObjects[SetIteratorProto] = memoryManager->allocObject<SetIteratorPrototype>(ic);
+ ic = newInternalClass(ArrayIteratorPrototype::staticVTable(), iteratorPrototype());
+ jsObjects[ArrayIteratorProto] = memoryManager->allocObject<ArrayIteratorPrototype>(ic);
+ ic = newInternalClass(StringIteratorPrototype::staticVTable(), iteratorPrototype());
+ jsObjects[StringIteratorProto] = memoryManager->allocObject<StringIteratorPrototype>(ic);
str = newString(QStringLiteral("get [Symbol.species]"));
jsObjects[GetSymbolSpecies] = FunctionObject::createBuiltinFunction(this, str, ArrayPrototype::method_get_species, 0);
@@ -1076,6 +1082,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);
@@ -1189,6 +1201,14 @@ ReturnedValue ExecutionEngine::throwTypeError(const QString &message)
return throwError(error);
}
+ReturnedValue ExecutionEngine::throwReferenceError(const QString &name)
+{
+ Scope scope(this);
+ QString msg = name + QLatin1String(" is not defined");
+ ScopedObject error(scope, newReferenceErrorObject(msg));
+ return throwError(error);
+}
+
ReturnedValue ExecutionEngine::throwReferenceError(const Value &value)
{
Scope scope(this);
@@ -1302,7 +1322,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>()) {
@@ -1333,7 +1353,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));
}
@@ -1524,6 +1544,10 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
case QMetaType::QLocale:
return QQmlLocale::wrap(this, *reinterpret_cast<const QLocale*>(ptr));
#endif
+ case QMetaType::QPixmap:
+ case QMetaType::QImage:
+ // Scarce value types
+ return QV4::Encode(newVariantObject(variant));
default:
break;
}
@@ -1639,9 +1663,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);
}
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index 3735c24601..6df4545014 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -572,6 +572,7 @@ public:
ReturnedValue throwTypeError();
ReturnedValue throwTypeError(const QString &message);
ReturnedValue throwReferenceError(const Value &value);
+ ReturnedValue throwReferenceError(const QString &name);
ReturnedValue throwReferenceError(const QString &value, const QString &fileName, int lineNumber, int column);
ReturnedValue throwRangeError(const Value &value);
ReturnedValue throwRangeError(const QString &message);
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp
index 7702939e23..1bd4329fe8 100644
--- a/src/qml/jsruntime/qv4function.cpp
+++ b/src/qml/jsruntime/qv4function.cpp
@@ -96,7 +96,6 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit,
, codeData(function->code())
, jittedCode(nullptr)
, codeRef(nullptr)
- , hasQmlDependencies(function->hasQmlDependencies())
{
Scope scope(engine);
Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext));
diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h
index 374e46b929..f8125a58f8 100644
--- a/src/qml/jsruntime/qv4function_p.h
+++ b/src/qml/jsruntime/qv4function_p.h
@@ -94,7 +94,6 @@ public:
Heap::InternalClass *internalClass;
uint nFormals;
int interpreterCallCount = 0;
- bool hasQmlDependencies;
bool isEval = false;
static Function *create(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function);
diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp
index 566db6fd4e..14caa6953f 100644
--- a/src/qml/jsruntime/qv4generatorobject.cpp
+++ b/src/qml/jsruntime/qv4generatorobject.cpp
@@ -139,7 +139,9 @@ void GeneratorPrototype::init(ExecutionEngine *engine, Object *ctor)
Scope scope(engine);
ScopedValue v(scope);
- ScopedObject ctorProto(scope, engine->newObject(engine->newInternalClass(Object::staticVTable(), engine->functionPrototype())));
+ Scoped<InternalClass> ic(scope, engine->newInternalClass(
+ Object::staticVTable(), engine->functionPrototype()));
+ ScopedObject ctorProto(scope, engine->newObject(ic->d()));
ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
ctor->defineReadonlyProperty(engine->id_prototype(), ctorProto);
diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h
index 44adac26cd..d47393b3bb 100644
--- a/src/qml/jsruntime/qv4global_p.h
+++ b/src/qml/jsruntime/qv4global_p.h
@@ -94,7 +94,8 @@ inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); }
#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)
+#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.
diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp
index e476baa886..ae937b2889 100644
--- a/src/qml/jsruntime/qv4identifiertable.cpp
+++ b/src/qml/jsruntime/qv4identifiertable.cpp
@@ -70,7 +70,7 @@ IdentifierTable::~IdentifierTable()
{
free(entriesByHash);
free(entriesById);
- for (auto &h : idHashes)
+ for (const auto &h : qAsConst(idHashes))
h->identifierTable = nullptr;
}
@@ -216,9 +216,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..3c732bc555 100644
--- a/src/qml/jsruntime/qv4include.cpp
+++ b/src/qml/jsruntime/qv4include.cpp
@@ -214,7 +214,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 +224,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 +255,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 ddb8542e07..a10fda79f2 100644
--- a/src/qml/jsruntime/qv4internalclass.cpp
+++ b/src/qml/jsruntime/qv4internalclass.cpp
@@ -44,6 +44,7 @@
#include "qv4object_p.h"
#include "qv4identifiertable_p.h"
#include "qv4value_p.h"
+#include "qv4mm_p.h"
QT_BEGIN_NAMESPACE
@@ -144,7 +145,7 @@ SharedInternalClassDataPrivate<PropertyKey>::SharedInternalClassDataPrivate(cons
data(nullptr)
{
if (other.alloc()) {
- int s = other.size();
+ const uint s = other.size();
data = MemberData::allocate(engine, other.alloc(), other.data);
setSize(s);
}
@@ -163,8 +164,8 @@ SharedInternalClassDataPrivate<PropertyKey>::SharedInternalClassDataPrivate(cons
void SharedInternalClassDataPrivate<PropertyKey>::grow()
{
- uint a = alloc() * 2;
- int s = size();
+ const uint a = alloc() * 2;
+ const uint s = size();
data = MemberData::allocate(engine, a, data);
setSize(s);
Q_ASSERT(alloc() >= a);
@@ -204,7 +205,70 @@ void SharedInternalClassDataPrivate<PropertyKey>::mark(MarkStack *s)
data->mark(s);
}
+SharedInternalClassDataPrivate<PropertyAttributes>::SharedInternalClassDataPrivate(
+ const SharedInternalClassDataPrivate<PropertyAttributes> &other, uint pos,
+ PropertyAttributes value)
+ : refcount(1),
+ m_alloc(qMin(other.m_alloc, pos + 8)),
+ m_size(pos + 1),
+ m_engine(other.m_engine)
+{
+ Q_ASSERT(m_size <= m_alloc);
+ m_engine->memoryManager->changeUnmanagedHeapSizeUsage(m_alloc * sizeof(PropertyAttributes));
+ data = new PropertyAttributes[m_alloc];
+ if (other.data)
+ memcpy(data, other.data, (m_size - 1) * sizeof(PropertyAttributes));
+ data[pos] = value;
+}
+
+SharedInternalClassDataPrivate<PropertyAttributes>::SharedInternalClassDataPrivate(
+ const SharedInternalClassDataPrivate<PropertyAttributes> &other)
+ : refcount(1),
+ m_alloc(other.m_alloc),
+ m_size(other.m_size),
+ m_engine(other.m_engine)
+{
+ if (m_alloc) {
+ m_engine->memoryManager->changeUnmanagedHeapSizeUsage(m_alloc * sizeof(PropertyAttributes));
+ data = new PropertyAttributes[m_alloc];
+ memcpy(data, other.data, m_size*sizeof(PropertyAttributes));
+ } else {
+ data = nullptr;
+ }
+}
+
+SharedInternalClassDataPrivate<PropertyAttributes>::~SharedInternalClassDataPrivate()
+{
+ m_engine->memoryManager->changeUnmanagedHeapSizeUsage(
+ -qptrdiff(m_alloc * sizeof(PropertyAttributes)));
+ delete [] data;
+}
+void SharedInternalClassDataPrivate<PropertyAttributes>::grow() {
+ uint alloc;
+ if (!m_alloc) {
+ alloc = 8;
+ m_engine->memoryManager->changeUnmanagedHeapSizeUsage(alloc * sizeof(PropertyAttributes));
+ } else {
+ // yes, signed. We don't want to deal with stuff > 2G
+ const uint currentSize = m_alloc * sizeof(PropertyAttributes);
+ if (currentSize < uint(std::numeric_limits<int>::max() / 2))
+ alloc = m_alloc * 2;
+ else
+ alloc = std::numeric_limits<int>::max() / sizeof(PropertyAttributes);
+
+ m_engine->memoryManager->changeUnmanagedHeapSizeUsage(
+ (alloc - m_alloc) * sizeof(PropertyAttributes));
+ }
+
+ auto *n = new PropertyAttributes[alloc];
+ if (data) {
+ memcpy(n, data, m_alloc*sizeof(PropertyAttributes));
+ delete [] data;
+ }
+ data = n;
+ m_alloc = alloc;
+}
namespace Heap {
@@ -257,11 +321,15 @@ void InternalClass::init(Heap::InternalClass *other)
void InternalClass::destroy()
{
-#ifndef QT_NO_DEBUG
for (const auto &t : transitions) {
- Q_ASSERT(!t.lookup || !t.lookup->isMarked());
- }
+ if (t.lookup) {
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(t.lookup->parent == this);
#endif
+ t.lookup->parent = nullptr;
+ }
+ }
+
if (parent && parent->engine && parent->isMarked())
parent->removeChildEntry(this);
@@ -598,11 +666,10 @@ Heap::InternalClass *InternalClass::frozen()
return f;
}
-Heap::InternalClass *InternalClass::propertiesFrozen() const
+Heap::InternalClass *InternalClass::propertiesFrozen()
{
Scope scope(engine);
- Scoped<QV4::InternalClass> frozen(scope, engine->internalClasses(EngineBase::Class_Empty)->changeVTable(vtable));
- frozen = frozen->changePrototype(prototype);
+ Scoped<QV4::InternalClass> frozen(scope, this);
for (uint i = 0; i < size; ++i) {
PropertyAttributes attrs = propertyData.at(i);
if (!nameMap.at(i).isValid())
@@ -611,7 +678,7 @@ Heap::InternalClass *InternalClass::propertiesFrozen() const
attrs.setWritable(false);
attrs.setConfigurable(false);
}
- frozen = frozen->addMember(nameMap.at(i), attrs);
+ frozen = frozen->changeMember(nameMap.at(i), attrs);
}
return frozen->d();
}
@@ -659,8 +726,6 @@ void InternalClass::markObjects(Heap::Base *b, MarkStack *stack)
Heap::InternalClass *ic = static_cast<Heap::InternalClass *>(b);
if (ic->prototype)
ic->prototype->mark(stack);
- if (ic->parent)
- ic->parent->mark(stack);
ic->nameMap.mark(stack);
}
diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h
index 681cbda5f9..7bb10f47a3 100644
--- a/src/qml/jsruntime/qv4internalclass_p.h
+++ b/src/qml/jsruntime/qv4internalclass_p.h
@@ -149,55 +149,31 @@ inline PropertyHash::Entry *PropertyHash::lookup(PropertyKey identifier) const
}
}
-template<typename T>
-struct SharedInternalClassDataPrivate {
- SharedInternalClassDataPrivate(ExecutionEngine *)
+template<class T>
+struct SharedInternalClassDataPrivate {};
+
+template<>
+struct SharedInternalClassDataPrivate<PropertyAttributes> {
+ SharedInternalClassDataPrivate(ExecutionEngine *engine)
: refcount(1),
m_alloc(0),
m_size(0),
- data(nullptr)
+ data(nullptr),
+ m_engine(engine)
{ }
- SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &other)
- : refcount(1),
- m_alloc(other.m_alloc),
- m_size(other.m_size)
- {
- if (m_alloc) {
- data = new T[m_alloc];
- memcpy(data, other.data, m_size*sizeof(T));
- }
- }
- SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &other, uint pos, T value)
- : refcount(1),
- m_alloc(pos + 8),
- m_size(pos + 1)
- {
- data = new T[m_alloc];
- if (other.data)
- memcpy(data, other.data, (m_size - 1)*sizeof(T));
- data[pos] = value;
- }
- ~SharedInternalClassDataPrivate() { delete [] data; }
-
+ SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate<PropertyAttributes> &other);
+ SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate<PropertyAttributes> &other,
+ uint pos, PropertyAttributes value);
+ ~SharedInternalClassDataPrivate();
- void grow() {
- if (!m_alloc)
- m_alloc = 4;
- T *n = new T[m_alloc * 2];
- if (data) {
- memcpy(n, data, m_alloc*sizeof(T));
- delete [] data;
- }
- data = n;
- m_alloc *= 2;
- }
+ void grow();
uint alloc() const { return m_alloc; }
uint size() const { return m_size; }
void setSize(uint s) { m_size = s; }
- T at(uint i) { Q_ASSERT(data && i < m_alloc); return data[i]; }
- void set(uint i, T t) { Q_ASSERT(data && i < m_alloc); data[i] = t; }
+ PropertyAttributes at(uint i) { Q_ASSERT(data && i < m_alloc); return data[i]; }
+ void set(uint i, PropertyAttributes t) { Q_ASSERT(data && i < m_alloc); data[i] = t; }
void mark(MarkStack *) {}
@@ -205,7 +181,8 @@ struct SharedInternalClassDataPrivate {
private:
uint m_alloc;
uint m_size;
- T *data;
+ PropertyAttributes *data;
+ ExecutionEngine *m_engine;
};
template<>
@@ -270,8 +247,12 @@ struct SharedInternalClassData {
Q_ASSERT(pos == d->size());
if (pos == d->alloc())
d->grow();
- d->setSize(d->size() + 1);
- d->set(pos, value);
+ if (pos >= d->alloc()) {
+ qBadAlloc();
+ } else {
+ d->setSize(d->size() + 1);
+ d->set(pos, value);
+ }
}
void set(uint pos, T value) {
@@ -451,7 +432,7 @@ struct InternalClass : Base {
Q_REQUIRED_RESULT InternalClass *sealed();
Q_REQUIRED_RESULT InternalClass *frozen();
- Q_REQUIRED_RESULT InternalClass *propertiesFrozen() const;
+ Q_REQUIRED_RESULT InternalClass *propertiesFrozen();
Q_REQUIRED_RESULT InternalClass *asProtoClass();
@@ -476,7 +457,7 @@ private:
InternalClass *addMemberImpl(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry);
void removeChildEntry(InternalClass *child);
- friend struct ExecutionEngine;
+ friend struct ::QV4::ExecutionEngine;
};
inline
diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp
index 1b6cdcbd14..c2c3fa0474 100644
--- a/src/qml/jsruntime/qv4lookup.cpp
+++ b/src/qml/jsruntime/qv4lookup.cpp
@@ -69,37 +69,7 @@ void Lookup::resolveProtoGetter(PropertyKey name, const Heap::Object *proto)
ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *object)
{
- Heap::Object *obj = object->d();
- PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- if (name.isArrayIndex()) {
- indexedLookup.index = name.asArrayIndex();
- getter = getterIndexed;
- return getter(this, engine, *object);
- }
-
- auto index = obj->internalClass->findValueOrGetter(name);
- if (index.isValid()) {
- PropertyAttributes attrs = index.attrs;
- uint nInline = obj->vtable()->nInlineProperties;
- if (attrs.isData()) {
- if (index.index < obj->vtable()->nInlineProperties) {
- index.index += obj->vtable()->inlinePropertyOffset;
- getter = getter0Inline;
- } else {
- index.index -= nInline;
- getter = getter0MemberData;
- }
- } else {
- getter = getterAccessor;
- }
- objectLookup.ic = obj->internalClass;
- objectLookup.offset = index.index;
- return getter(this, engine, *object);
- }
-
- protoLookup.protoId = obj->internalClass->protoId;
- resolveProtoGetter(name, obj->prototype());
- return getter(this, engine, *object);
+ return object->resolveLookupGetter(engine, this);
}
ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Value &object)
@@ -409,7 +379,7 @@ ReturnedValue Lookup::getterIndexed(Lookup *l, ExecutionEngine *engine, const Va
ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object)
{
- if (object.type() == l->primitiveLookup.type) {
+ if (object.type() == l->primitiveLookup.type && !object.isObject()) {
Heap::Object *o = l->primitiveLookup.proto;
if (l->primitiveLookup.protoId == o->internalClass->protoId)
return l->primitiveLookup.data->asReturnedValue();
@@ -420,7 +390,7 @@ ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, c
ReturnedValue Lookup::primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object)
{
- if (object.type() == l->primitiveLookup.type) {
+ if (object.type() == l->primitiveLookup.type && !object.isObject()) {
Heap::Object *o = l->primitiveLookup.proto;
if (l->primitiveLookup.protoId == o->internalClass->protoId) {
const Value *getter = l->primitiveLookup.data;
@@ -473,56 +443,7 @@ ReturnedValue Lookup::globalGetterProtoAccessor(Lookup *l, ExecutionEngine *engi
bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value &value)
{
- Scope scope(engine);
- ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
-
- Heap::InternalClass *c = object->internalClass();
- PropertyKey key = name->toPropertyKey();
- auto idx = c->findValueOrSetter(key);
- if (idx.isValid()) {
- if (object->isArrayObject() && idx.index == Heap::ArrayObject::LengthPropertyIndex) {
- Q_ASSERT(!idx.attrs.isAccessor());
- setter = arrayLengthSetter;
- return setter(this, engine, *object, value);
- } else if (idx.attrs.isData() && idx.attrs.isWritable()) {
- objectLookup.ic = object->internalClass();
- objectLookup.index = idx.index;
- const auto nInline = object->d()->vtable()->nInlineProperties;
- if (idx.index < nInline) {
- setter = Lookup::setter0Inline;
- objectLookup.offset = idx.index + object->d()->vtable()->inlinePropertyOffset;
- } else {
- setter = Lookup::setter0MemberData;
- objectLookup.offset = idx.index - nInline;
- }
- return setter(this, engine, *object, value);
- } else {
- // ### handle setter
- setter = setterFallback;
- }
- return setter(this, engine, *object, value);
- }
-
- insertionLookup.protoId = c->protoId;
- if (!object->put(key, value)) {
- setter = Lookup::setterFallback;
- return false;
- }
-
- if (object->internalClass() == c) {
- // ### setter in the prototype, should handle this
- setter = setterFallback;
- return true;
- }
- idx = object->internalClass()->findValueOrSetter(key);
- if (!idx.isValid() || idx.attrs.isAccessor()) { // ### can this even happen?
- setter = setterFallback;
- return false;
- }
- insertionLookup.newClass = object->internalClass();
- insertionLookup.offset = idx.index;
- setter = setterInsert;
- return true;
+ return object->resolveLookupSetter(engine, this, value);
}
bool Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h
index bfe2354427..7309749a81 100644
--- a/src/qml/jsruntime/qv4lookup_p.h
+++ b/src/qml/jsruntime/qv4lookup_p.h
@@ -68,8 +68,10 @@ struct Lookup {
union {
ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object);
ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine);
+ ReturnedValue (*qmlContextPropertyGetter)(Lookup *l, ExecutionEngine *engine, Value *thisObject);
bool (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v);
};
+ // NOTE: gc assumes the first two entries in the struct are pointers to heap objects or null
union {
struct {
Heap::Base *h1;
@@ -119,6 +121,39 @@ struct Lookup {
uint index;
uint unused;
} indexedLookup;
+ struct {
+ Heap::InternalClass *ic;
+ Heap::QObjectWrapper *staticQObject;
+ QQmlPropertyCache *propertyCache;
+ QQmlPropertyData *propertyData;
+ } qobjectLookup;
+ struct {
+ Heap::InternalClass *ic;
+ quintptr unused;
+ QQmlPropertyCache *propertyCache;
+ QQmlPropertyData *propertyData;
+ } qgadgetLookup;
+ struct {
+ quintptr unused1;
+ quintptr unused2;
+ int scriptIndex;
+ } qmlContextScriptLookup;
+ struct {
+ Heap::Object *singleton;
+ quintptr unused;
+ } qmlContextSingletonLookup;
+ struct {
+ quintptr unused1;
+ quintptr unused2;
+ int objectId;
+ } qmlContextIdObjectLookup;
+ struct {
+ // Same as protoLookup, as used for global lookups
+ quintptr reserved1;
+ quintptr reserved2;
+ quintptr reserved3;
+ ReturnedValue (*getterTrampoline)(Lookup *l, ExecutionEngine *engine);
+ } qmlContextGlobalLookup;
};
uint nameIndex;
diff --git a/src/qml/jsruntime/qv4memberdata.cpp b/src/qml/jsruntime/qv4memberdata.cpp
index 246f857643..f327c85001 100644
--- a/src/qml/jsruntime/qv4memberdata.cpp
+++ b/src/qml/jsruntime/qv4memberdata.cpp
@@ -69,12 +69,25 @@ Heap::MemberData *MemberData::allocate(ExecutionEngine *e, uint n, Heap::MemberD
size_t alloc = MemoryManager::align(sizeof(Heap::MemberData) + (n - 1)*sizeof(Value));
// round up to next power of two to avoid quadratic behaviour for very large objects
alloc = nextPowerOfTwo(alloc);
- Heap::MemberData *m = e->memoryManager->allocManaged<MemberData>(alloc);
- if (old)
+
+ // The above code can overflow in a number of interesting ways. All of those are unsigned,
+ // and therefore defined behavior. Still, apply some sane bounds.
+ if (alloc > std::numeric_limits<int>::max())
+ alloc = std::numeric_limits<int>::max();
+
+ Heap::MemberData *m;
+ if (old) {
+ const size_t oldSize = sizeof(Heap::MemberData) + (old->values.size - 1) * sizeof(Value);
+ if (oldSize > alloc)
+ alloc = oldSize;
+ m = e->memoryManager->allocManaged<MemberData>(alloc);
// no write barrier required here
- memcpy(m, old, sizeof(Heap::MemberData) + (old->values.size - 1) * sizeof(Value));
- else
+ memcpy(m, old, oldSize);
+ } else {
+ m = e->memoryManager->allocManaged<MemberData>(alloc);
m->init();
+ }
+
m->values.alloc = static_cast<uint>((alloc - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value));
m->values.size = m->values.alloc;
return m;
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index 3d2d54f651..206b410cf4 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -320,8 +320,11 @@ bool Object::virtualDeleteProperty(Managed *m, PropertyKey id)
PropertyKey ObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
{
if (arrayIndex != UINT_MAX && o->arrayData()) {
- if (!arrayIndex)
- arrayNode = o->sparseBegin();
+ SparseArrayNode *arrayNode = nullptr;
+ if (o->arrayType() == Heap::ArrayData::Sparse) {
+ SparseArray *sparse = o->arrayData()->sparse;
+ arrayNode = arrayIndex ? sparse->lowerBound(arrayIndex) : sparse->begin();
+ }
// sparse arrays
if (arrayNode) {
@@ -339,7 +342,6 @@ PropertyKey ObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, Pr
*attrs = a;
return PropertyKey::fromArrayIndex(k);
}
- arrayNode = nullptr;
arrayIndex = UINT_MAX;
}
// dense arrays
@@ -404,8 +406,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);
@@ -429,8 +431,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()) {
@@ -468,14 +468,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()) {
@@ -544,12 +543,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;
}
@@ -734,6 +732,95 @@ ReturnedValue Object::virtualInstanceOf(const Object *typeObject, const Value &v
return checkedInstanceOf(engine, function, var);
}
+ReturnedValue Object::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
+{
+ Heap::Object *obj = object->d();
+ PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
+ if (name.isArrayIndex()) {
+ lookup->indexedLookup.index = name.asArrayIndex();
+ lookup->getter = Lookup::getterIndexed;
+ return lookup->getter(lookup, engine, *object);
+ }
+
+ auto index = obj->internalClass->findValueOrGetter(name);
+ if (index.isValid()) {
+ PropertyAttributes attrs = index.attrs;
+ uint nInline = obj->vtable()->nInlineProperties;
+ if (attrs.isData()) {
+ if (index.index < obj->vtable()->nInlineProperties) {
+ index.index += obj->vtable()->inlinePropertyOffset;
+ lookup->getter = Lookup::getter0Inline;
+ } else {
+ index.index -= nInline;
+ lookup->getter = Lookup::getter0MemberData;
+ }
+ } else {
+ lookup->getter = Lookup::getterAccessor;
+ }
+ lookup->objectLookup.ic = obj->internalClass;
+ lookup->objectLookup.offset = index.index;
+ return lookup->getter(lookup, engine, *object);
+ }
+
+ lookup->protoLookup.protoId = obj->internalClass->protoId;
+ lookup->resolveProtoGetter(name, obj->prototype());
+ return lookup->getter(lookup, engine, *object);
+}
+
+bool Object::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value)
+{
+ Scope scope(engine);
+ ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
+
+ Heap::InternalClass *c = object->internalClass();
+ PropertyKey key = name->toPropertyKey();
+ auto idx = c->findValueOrSetter(key);
+ if (idx.isValid()) {
+ if (object->isArrayObject() && idx.index == Heap::ArrayObject::LengthPropertyIndex) {
+ Q_ASSERT(!idx.attrs.isAccessor());
+ lookup->setter = Lookup::arrayLengthSetter;
+ return lookup->setter(lookup, engine, *object, value);
+ } else if (idx.attrs.isData() && idx.attrs.isWritable()) {
+ lookup->objectLookup.ic = object->internalClass();
+ 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
+ lookup->setter = Lookup::setterFallback;
+ }
+ return lookup->setter(lookup, engine, *object, value);
+ }
+
+ lookup->insertionLookup.protoId = c->protoId;
+ if (!object->put(key, value)) {
+ lookup->setter = Lookup::setterFallback;
+ return false;
+ }
+
+ if (object->internalClass() == c) {
+ // ### setter in the prototype, should handle this
+ lookup->setter = Lookup::setterFallback;
+ return true;
+ }
+ idx = object->internalClass()->findValueOrSetter(key);
+ if (!idx.isValid() || idx.attrs.isAccessor()) { // ### can this even happen?
+ lookup->setter = Lookup::setterFallback;
+ return false;
+ }
+ lookup->insertionLookup.newClass = object->internalClass();
+ lookup->insertionLookup.offset = idx.index;
+ lookup->setter = Lookup::setterInsert;
+ return true;
+}
+
ReturnedValue Object::checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *f, const Value &var)
{
Scope scope(engine);
diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h
index 72b6703554..567382cbc0 100644
--- a/src/qml/jsruntime/qv4object_p.h
+++ b/src/qml/jsruntime/qv4object_p.h
@@ -375,6 +375,11 @@ public:
bool setProtoFromNewTarget(const Value *newTarget);
+ ReturnedValue resolveLookupGetter(ExecutionEngine *engine, Lookup *lookup) const
+ { return vtable()->resolveLookupGetter(this, engine, lookup); }
+ ReturnedValue resolveLookupSetter(ExecutionEngine *engine, Lookup *lookup, const Value &value)
+ { return vtable()->resolveLookupSetter(this, engine, lookup, value); }
+
protected:
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver,bool *hasProperty);
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
@@ -389,6 +394,8 @@ protected:
static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
static qint64 virtualGetLength(const Managed *m);
static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var);
+ static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
+ static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
public:
// qv4runtime uses this directly
static ReturnedValue checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *typeObject, const Value &var);
@@ -408,7 +415,6 @@ struct ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator
uint arrayIndex = 0;
uint memberIndex = 0;
bool iterateOverSymbols = false;
- SparseArrayNode *arrayNode = nullptr;
~ObjectOwnPropertyKeyIterator() override = default;
PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
diff --git a/src/qml/jsruntime/qv4propertykey_p.h b/src/qml/jsruntime/qv4propertykey_p.h
index 47867765db..523afd4ccf 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; }
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp
index 88b0822f42..d2aa334805 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>
@@ -55,6 +54,8 @@
#include <private/qjsvalue_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qv4module_p.h>
+#include <private/qv4lookup_p.h>
+#include <private/qv4identifiertable_p.h>
QT_BEGIN_NAMESPACE
@@ -77,14 +78,58 @@ void Heap::QQmlContextWrapper::destroy()
Object::destroy();
}
-ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
+static OptionalReturnedValue searchContextProperties(QV4::ExecutionEngine *v4, QQmlContextData *context, String *name,
+ bool *hasProperty, Value *base, QV4::Lookup *lookup,
+ QV4::Lookup *originalLookup, QQmlEnginePrivate *ep)
{
- Q_ASSERT(m->as<QQmlContextWrapper>());
+ const QV4::IdentifierHash &properties = context->propertyNames();
+ if (properties.count() == 0)
+ return OptionalReturnedValue();
+
+ const int propertyIdx = properties.value(name);
+
+ if (propertyIdx == -1)
+ return OptionalReturnedValue();
+
+ if (propertyIdx < context->idValueCount) {
+ if (hasProperty)
+ *hasProperty = true;
+
+ if (lookup) {
+ lookup->qmlContextIdObjectLookup.objectId = propertyIdx;
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupIdObject;
+ return OptionalReturnedValue(lookup->qmlContextPropertyGetter(lookup, v4, base));
+ } else if (originalLookup) {
+ originalLookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupInParentContextHierarchy;
+ }
+
+ if (ep->propertyCapture)
+ ep->propertyCapture->captureProperty(&context->idValues[propertyIdx].bindings);
+ return OptionalReturnedValue(QV4::QObjectWrapper::wrap(v4, context->idValues[propertyIdx]));
+ }
+
+ QQmlContextPrivate *cp = context->asQQmlContextPrivate();
+
+ if (ep->propertyCapture)
+ ep->propertyCapture->captureProperty(context->asQQmlContext(), -1, propertyIdx + cp->notifyIndex);
+
+ const QVariant &value = cp->propertyValues.at(propertyIdx);
+ if (hasProperty)
+ *hasProperty = true;
+ if (value.userType() == qMetaTypeId<QList<QObject*> >()) {
+ QQmlListProperty<QObject> prop(context->asQQmlContext(), (void*) qintptr(propertyIdx),
+ QQmlContextPrivate::context_count,
+ QQmlContextPrivate::context_at);
+ return OptionalReturnedValue(QmlListWrapper::create(v4, prop, qMetaTypeId<QQmlListProperty<QObject> >()));
+ }
+ return OptionalReturnedValue(v4->fromVariant(cp->propertyValues.at(propertyIdx)));
+}
+ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, bool *hasProperty, Value *base, Lookup *lookup)
+{
if (!id.isString())
- return Object::virtualGet(m, id, receiver, hasProperty);
+ return Object::virtualGet(resource, id, receiver, hasProperty);
- const QQmlContextWrapper *resource = static_cast<const QQmlContextWrapper *>(m);
QV4::ExecutionEngine *v4 = resource->engine();
QV4::Scope scope(v4);
@@ -100,11 +145,11 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
}
}
- return Object::virtualGet(m, id, receiver, hasProperty);
+ return Object::virtualGet(resource, id, receiver, hasProperty);
}
bool hasProp = false;
- ScopedValue result(scope, Object::virtualGet(m, id, receiver, &hasProp));
+ ScopedValue result(scope, Object::virtualGet(resource, id, receiver, &hasProp));
if (hasProp) {
if (hasProperty)
*hasProperty = hasProp;
@@ -160,6 +205,8 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
// Note: The scope object is only a QADMO for example when somebody registers a QQmlPropertyMap
// sub-class as QML type and then instantiates it in .qml.
if (scopeObject && QQmlPropertyCache::isDynamicMetaObject(scopeObject->metaObject())) {
+ // all bets are off, so don't try to optimize any lookups
+ lookup = nullptr;
if (performGobalLookUp())
return result->asReturnedValue();
}
@@ -172,11 +219,35 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
if (hasProperty)
*hasProperty = true;
if (r.scriptIndex != -1) {
+ if (lookup) {
+ lookup->qmlContextScriptLookup.scriptIndex = r.scriptIndex;
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScript;
+ return lookup->qmlContextPropertyGetter(lookup, v4, base);
+ }
QV4::ScopedObject scripts(scope, context->importedScripts.valueRef());
if (scripts)
return scripts->get(r.scriptIndex);
return QV4::Encode::null();
} 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)) {
+ 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)));
+ lookup->qmlContextSingletonLookup.singleton = o->d();
+ }
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupSingleton;
+ return lookup->qmlContextPropertyGetter(lookup, v4, base);
+ }
+ }
return QQmlTypeWrapper::create(v4, scopeObject, r.type);
} else if (r.importNamespace) {
return QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace);
@@ -188,52 +259,47 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
}
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(v4->qmlEngine());
+ Lookup * const originalLookup = lookup;
+
+ decltype(lookup->qmlContextPropertyGetter) contextGetterFunction = QQmlContextWrapper::lookupContextObjectProperty;
+
+ // minor optimization so we don't potentially try two property lookups on the same object
+ if (scopeObject == context->contextObject) {
+ scopeObject = nullptr;
+ contextGetterFunction = QQmlContextWrapper::lookupScopeObjectProperty;
+ }
while (context) {
- // Search context properties
- const QV4::IdentifierHash &properties = context->propertyNames();
- if (properties.count()) {
- int propertyIdx = properties.value(name);
-
- if (propertyIdx != -1) {
-
- if (propertyIdx < context->idValueCount) {
-
- if (ep->propertyCapture)
- ep->propertyCapture->captureProperty(&context->idValues[propertyIdx].bindings);
- if (hasProperty)
- *hasProperty = true;
- return QV4::QObjectWrapper::wrap(v4, context->idValues[propertyIdx]);
- } else {
-
- QQmlContextPrivate *cp = context->asQQmlContextPrivate();
-
- if (ep->propertyCapture)
- ep->propertyCapture->captureProperty(context->asQQmlContext(), -1, propertyIdx + cp->notifyIndex);
-
- const QVariant &value = cp->propertyValues.at(propertyIdx);
- if (hasProperty)
- *hasProperty = true;
- if (value.userType() == qMetaTypeId<QList<QObject*> >()) {
- QQmlListProperty<QObject> prop(context->asQQmlContext(), (void*) qintptr(propertyIdx),
- QQmlContextPrivate::context_count,
- QQmlContextPrivate::context_at);
- return QmlListWrapper::create(v4, prop, qMetaTypeId<QQmlListProperty<QObject> >());
- } else {
- return scope.engine->fromVariant(cp->propertyValues.at(propertyIdx));
- }
- }
- }
- }
+ if (auto property = searchContextProperties(v4, context, name, hasProperty, base, lookup, originalLookup, ep))
+ return *property;
// Search scope object
if (scopeObject) {
bool hasProp = false;
+
+ QQmlPropertyData *propertyData = nullptr;
QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty(v4, context, scopeObject,
- name, QV4::QObjectWrapper::CheckRevision, &hasProp));
+ name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData));
if (hasProp) {
if (hasProperty)
*hasProperty = true;
+ if (base)
+ *base = QV4::QObjectWrapper::wrap(v4, scopeObject);
+
+ if (lookup && propertyData) {
+ QQmlData *ddata = QQmlData::get(scopeObject, false);
+ if (ddata && ddata->propertyCache) {
+ ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, scopeObject)));
+ const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue());
+ lookup->qobjectLookup.ic = That->internalClass();
+ lookup->qobjectLookup.staticQObject = nullptr;
+ lookup->qobjectLookup.propertyCache = ddata->propertyCache;
+ lookup->qobjectLookup.propertyCache->addref();
+ lookup->qobjectLookup.propertyData = propertyData;
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScopeObjectProperty;
+ }
+ }
+
return result->asReturnedValue();
}
}
@@ -243,27 +309,77 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
// Search context object
if (context->contextObject) {
bool hasProp = false;
- result = QV4::QObjectWrapper::getQmlProperty(v4, context, context->contextObject, name, QV4::QObjectWrapper::CheckRevision, &hasProp);
+ QQmlPropertyData *propertyData = nullptr;
+ result = QV4::QObjectWrapper::getQmlProperty(v4, context, context->contextObject,
+ name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData);
if (hasProp) {
if (hasProperty)
*hasProperty = true;
+ if (base)
+ *base = QV4::QObjectWrapper::wrap(v4, context->contextObject);
+
+ if (propertyData) {
+ if (lookup) {
+ QQmlData *ddata = QQmlData::get(context->contextObject, false);
+ if (ddata && ddata->propertyCache) {
+ ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, context->contextObject)));
+ const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue());
+ lookup->qobjectLookup.ic = That->internalClass();
+ lookup->qobjectLookup.staticQObject = nullptr;
+ lookup->qobjectLookup.propertyCache = ddata->propertyCache;
+ lookup->qobjectLookup.propertyCache->addref();
+ lookup->qobjectLookup.propertyData = propertyData;
+ lookup->qmlContextPropertyGetter = contextGetterFunction;
+ }
+ } else if (originalLookup) {
+ originalLookup->qmlContextPropertyGetter = lookupInParentContextHierarchy;
+ }
+ }
+
return result->asReturnedValue();
}
}
context = context->parent;
+
+ // As the hierarchy of contexts is not stable, we can't do accelerated lookups beyond
+ // the immediate QML context (of the .qml file).
+ lookup = nullptr;
}
// Do a lookup in the global object here to avoid expressionContext->unresolvedNames becoming
// true if we access properties of the global object.
- if (performGobalLookUp())
- return result->asReturnedValue();
+ if (originalLookup) {
+ // Try a lookup in the global object. It's theoretically possible to first find a property
+ // in the global object and then later a context property with the same name is added, but that
+ // never really worked as we used to detect access to global properties at type compile time anyway.
+ lookup = originalLookup;
+ result = lookup->resolveGlobalGetter(v4);
+ if (lookup->globalGetter != Lookup::globalGetterGeneric) {
+ if (hasProperty)
+ *hasProperty = true;
+ lookup->qmlContextGlobalLookup.getterTrampoline = lookup->globalGetter;
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupInGlobalObject;
+ return result->asReturnedValue();
+ }
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
+ } else {
+ if (performGobalLookUp())
+ return result->asReturnedValue();
+ }
expressionContext->unresolvedNames = true;
return Encode::undefined();
}
+ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
+{
+ Q_ASSERT(m->as<QQmlContextWrapper>());
+ const QQmlContextWrapper *This = static_cast<const QQmlContextWrapper *>(m);
+ return getPropertyAndBase(This, id, receiver, hasProperty, /*base*/nullptr);
+}
+
bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
Q_ASSERT(m->as<QQmlContextWrapper>());
@@ -298,8 +414,16 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val
while (context) {
const QV4::IdentifierHash &properties = context->propertyNames();
// Search context properties
- if (properties.count() && properties.value(name) != -1)
- return false;
+ if (properties.count()) {
+ const int propertyIndex = properties.value(name);
+ if (propertyIndex != -1) {
+ if (propertyIndex < context->idValueCount) {
+ v4->throwError(QLatin1String("left-hand side of assignment operator is not an lvalue"));
+ return false;
+ }
+ return false;
+ }
+ }
// Search scope object
if (scopeObject &&
@@ -323,6 +447,208 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val
return false;
}
+ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Scope scope(engine);
+ PropertyKey name =engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->
+ runtimeStrings[l->nameIndex]);
+
+ // Special hack for bounded signal expressions, where the parameters of signals are injected
+ // into the handler expression through the locals of the call context. So for onClicked: { ... }
+ // the parameters of the clicked signal are injected and we must allow for them to be found here
+ // before any other property from the QML context.
+ ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context);
+ if (ctx.d()->type == Heap::ExecutionContext::Type_CallContext) {
+ uint index = ctx.d()->internalClass->indexOfValueOrGetter(name);
+ if (index < UINT_MAX)
+ return static_cast<Heap::CallContext*>(ctx.d())->locals[index].asReturnedValue();
+ }
+
+ bool hasProperty = false;
+ ScopedValue result(scope);
+
+ Scoped<QmlContext> callingQmlContext(scope, engine->qmlContext());
+ if (callingQmlContext) {
+ Scoped<QQmlContextWrapper> qmlContextWrapper(scope, callingQmlContext->d()->qml());
+ result = QQmlContextWrapper::getPropertyAndBase(qmlContextWrapper, name, /*receiver*/nullptr, &hasProperty,
+ base, l);
+ } else {
+ // Code path typical to worker scripts, compiled with lookups but no qml context.
+ result = l->resolveGlobalGetter(engine);
+ if (l->globalGetter != Lookup::globalGetterGeneric) {
+ hasProperty = true;
+ l->qmlContextGlobalLookup.getterTrampoline = l->globalGetter;
+ l->qmlContextPropertyGetter = QQmlContextWrapper::lookupInGlobalObject;
+ }
+ }
+ if (!hasProperty)
+ return engine->throwReferenceError(name.toQString());
+ return result->asReturnedValue();
+}
+
+ReturnedValue QQmlContextWrapper::lookupScript(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Q_UNUSED(base)
+ Scope scope(engine);
+ Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
+ if (!qmlContext)
+ return QV4::Encode::null();
+
+ QQmlContextData *context = qmlContext->qmlContext();
+ if (!context)
+ return QV4::Encode::null();
+
+ QV4::ScopedObject scripts(scope, context->importedScripts.valueRef());
+ if (!scripts)
+ return QV4::Encode::null();
+ return scripts->get(l->qmlContextScriptLookup.scriptIndex);
+}
+
+ReturnedValue QQmlContextWrapper::lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Q_UNUSED(engine)
+ Q_UNUSED(base)
+ return Value::fromHeapObject(l->qmlContextSingletonLookup.singleton).asReturnedValue();
+}
+
+ReturnedValue QQmlContextWrapper::lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Q_UNUSED(base)
+ Scope scope(engine);
+ Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
+ if (!qmlContext)
+ return QV4::Encode::null();
+
+ QQmlContextData *context = qmlContext->qmlContext();
+ if (!context)
+ return QV4::Encode::null();
+
+ QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine->qmlEngine());
+ const int objectId = l->qmlContextIdObjectLookup.objectId;
+
+ if (qmlEngine->propertyCapture)
+ qmlEngine->propertyCapture->captureProperty(&context->idValues[objectId].bindings);
+
+ return QV4::QObjectWrapper::wrap(engine, context->idValues[objectId]);
+}
+
+ReturnedValue QQmlContextWrapper::lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Q_UNUSED(base)
+ Scope scope(engine);
+ Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
+ if (!qmlContext)
+ return QV4::Encode::undefined();
+
+ QObject *scopeObject = qmlContext->qmlScope();
+ if (!scopeObject)
+ return QV4::Encode::undefined();
+
+ if (QQmlData::wasDeleted(scopeObject))
+ return QV4::Encode::undefined();
+
+ const auto revertLookup = [l, engine, base]() {
+ l->qobjectLookup.propertyCache->release();
+ l->qobjectLookup.propertyCache = nullptr;
+ l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
+ return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base);
+ };
+
+ ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, scopeObject));
+ return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup);
+}
+
+ReturnedValue QQmlContextWrapper::lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Q_UNUSED(base)
+ Scope scope(engine);
+ Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
+ if (!qmlContext)
+ return QV4::Encode::undefined();
+
+ QQmlContextData *context = qmlContext->qmlContext();
+ if (!context)
+ return QV4::Encode::undefined();
+
+ QObject *contextObject = context->contextObject;
+ if (!contextObject)
+ return QV4::Encode::undefined();
+
+ if (QQmlData::wasDeleted(contextObject))
+ return QV4::Encode::undefined();
+
+ const auto revertLookup = [l, engine, base]() {
+ l->qobjectLookup.propertyCache->release();
+ l->qobjectLookup.propertyCache = nullptr;
+ l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
+ return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base);
+ };
+
+ ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, contextObject));
+ return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup);
+}
+
+ReturnedValue QQmlContextWrapper::lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Q_UNUSED(base);
+ ReturnedValue result = l->qmlContextGlobalLookup.getterTrampoline(l, engine);
+ // In the unlikely event of mutation of the global object, update the trampoline.
+ if (l->qmlContextPropertyGetter != lookupInGlobalObject) {
+ l->qmlContextGlobalLookup.getterTrampoline = l->globalGetter;
+ l->qmlContextPropertyGetter = QQmlContextWrapper::lookupInGlobalObject;
+ }
+ return result;
+}
+
+ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Scope scope(engine);
+ Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
+ if (!qmlContext)
+ return QV4::Encode::undefined();
+
+ QQmlContextData *context = qmlContext->qmlContext();
+ if (!context)
+ return QV4::Encode::undefined();
+
+ QQmlContextData *expressionContext = context;
+
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->qmlEngine());
+
+ PropertyKey id =engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->
+ runtimeStrings[l->nameIndex]);
+ ScopedString name(scope, id.asStringOrSymbol());
+
+ ScopedValue result(scope);
+
+ for (context = context->parent; context; context = context->parent) {
+ if (auto property = searchContextProperties(engine, context, name, nullptr, base, nullptr, nullptr, ep))
+ return *property;
+
+ // Search context object
+ if (context->contextObject) {
+ bool hasProp = false;
+ result = QV4::QObjectWrapper::getQmlProperty(engine, context, context->contextObject,
+ name, QV4::QObjectWrapper::CheckRevision, &hasProp);
+ if (hasProp) {
+ if (base)
+ *base = QV4::QObjectWrapper::wrap(engine, context->contextObject);
+
+ return result->asReturnedValue();
+ }
+ }
+ }
+
+ bool hasProp = false;
+ result = engine->globalObject->get(name, &hasProp);
+ if (hasProp)
+ return result->asReturnedValue();
+
+ expressionContext->unresolvedNames = true;
+
+ return Encode::undefined();
+}
+
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 dd6de3323d..4c8287ef2f 100644
--- a/src/qml/jsruntime/qv4qmlcontext_p.h
+++ b/src/qml/jsruntime/qv4qmlcontext_p.h
@@ -99,8 +99,19 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object
inline QObject *getScopeObject() const { return d()->scopeObject; }
inline QQmlContextData *getContext() const { return *d()->context; }
+ static ReturnedValue getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver,
+ bool *hasProperty, Value *base, Lookup *lookup = nullptr);
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
+
+ static ReturnedValue resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupScript(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupInParentContextHierarchy(Lookup *l, ExecutionEngine *engine, Value *base);
};
struct Q_QML_EXPORT QmlContext : public ExecutionContext
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 15f064ba7a..ba9029bd4d 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -50,12 +50,13 @@
#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>
#include <private/qv4runtime_p.h>
#include <private/qv4variantobject_p.h>
+#include <private/qv4identifiertable_p.h>
+#include <private/qv4lookup_p.h>
#if QT_CONFIG(qml_sequence_object)
#include <private/qv4sequenceobject_p.h>
@@ -163,10 +164,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);
@@ -231,7 +228,7 @@ QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QObject
return result;
}
-ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired)
+ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property)
{
QQmlData::flushPendingBinding(object, QQmlPropertyIndex(property->coreIndex()));
@@ -257,7 +254,7 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr;
- if (captureRequired && ep && ep->propertyCapture && !property->isConstant())
+ if (ep && ep->propertyCapture && !property->isConstant())
ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex());
if (property->isVarProperty()) {
@@ -269,9 +266,53 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
}
}
+static OptionalReturnedValue getDestroyOrToStringMethod(ExecutionEngine *v4, String *name, QObject *qobj, bool *hasProperty = nullptr)
+{
+ int index = 0;
+ if (name->equals(v4->id_destroy()))
+ index = QV4::QObjectMethod::DestroyMethod;
+ else if (name->equals(v4->id_toString()))
+ index = QV4::QObjectMethod::ToStringMethod;
+ else
+ return OptionalReturnedValue();
+
+ if (hasProperty)
+ *hasProperty = true;
+ ExecutionContext *global = v4->rootContext();
+ return OptionalReturnedValue(QV4::QObjectMethod::create(global, qobj, index));
+}
+
+static OptionalReturnedValue getPropertyFromImports(ExecutionEngine *v4, String *name, QQmlContextData *qmlContext, QObject *qobj,
+ bool *hasProperty = nullptr)
+{
+ if (!qmlContext || !qmlContext->imports)
+ return OptionalReturnedValue();
+
+ QQmlTypeNameCache::Result r = qmlContext->imports->query(name);
+
+ if (hasProperty)
+ *hasProperty = true;
+
+ if (!r.isValid())
+ return OptionalReturnedValue();
+
+ if (r.scriptIndex != -1) {
+ return OptionalReturnedValue(QV4::Encode::undefined());
+ } else if (r.type.isValid()) {
+ return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums));
+ } else if (r.importNamespace) {
+ return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj, qmlContext->imports, r.importNamespace,
+ Heap::QQmlTypeWrapper::ExcludeEnums));
+ }
+ Q_UNREACHABLE();
+ return OptionalReturnedValue();
+}
+
ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String *name, QObjectWrapper::RevisionMode revisionMode,
bool *hasProperty, bool includeImports) const
{
+ // Keep this code in sync with ::virtualResolveLookupGetter
+
if (QQmlData::wasDeleted(d()->object())) {
if (hasProperty)
*hasProperty = false;
@@ -280,39 +321,17 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String
ExecutionEngine *v4 = engine();
- if (name->equals(v4->id_destroy()) || name->equals(v4->id_toString())) {
- int index = name->equals(v4->id_destroy()) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod;
- if (hasProperty)
- *hasProperty = true;
- ExecutionContext *global = v4->rootContext();
- return QV4::QObjectMethod::create(global, d()->object(), index);
- }
+ if (auto methodValue = getDestroyOrToStringMethod(v4, name, d()->object(), hasProperty))
+ return *methodValue;
QQmlPropertyData local;
QQmlPropertyData *result = findProperty(v4, qmlContext, name, revisionMode, &local);
if (!result) {
+ // Check for attached properties
if (includeImports && name->startsWithUpper()) {
- // Check for attached properties
- if (qmlContext && qmlContext->imports) {
- QQmlTypeNameCache::Result r = qmlContext->imports->query(name);
-
- if (hasProperty)
- *hasProperty = true;
-
- if (r.isValid()) {
- if (r.scriptIndex != -1) {
- return QV4::Encode::undefined();
- } else if (r.type.isValid()) {
- return QQmlTypeWrapper::create(v4, d()->object(),
- r.type, Heap::QQmlTypeWrapper::ExcludeEnums);
- } else if (r.importNamespace) {
- return QQmlTypeWrapper::create(v4, d()->object(),
- qmlContext->imports, r.importNamespace, Heap::QQmlTypeWrapper::ExcludeEnums);
- }
- Q_ASSERT(!"Unreachable");
- }
- }
+ if (auto importProperty = getPropertyFromImports(v4, name, qmlContext, d()->object(), hasProperty))
+ return *importProperty;
}
return QV4::Object::virtualGet(this, name->propertyKey(), this, hasProperty);
}
@@ -333,27 +352,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String
return getProperty(v4, d()->object(), result);
}
-ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired)
-{
- if (QQmlData::wasDeleted(object))
- return QV4::Encode::null();
- QQmlData *ddata = QQmlData::get(object, /*create*/false);
- if (!ddata)
- return QV4::Encode::undefined();
-
- if (Q_UNLIKELY(!ddata->propertyCache)) {
- ddata->propertyCache = QQmlEnginePrivate::get(engine)->cache(object->metaObject());
- ddata->propertyCache->addref();
- }
-
- QQmlPropertyCache *cache = ddata->propertyCache;
- Q_ASSERT(cache);
- QQmlPropertyData *property = cache->property(propertyIndex);
- Q_ASSERT(property); // We resolved this property earlier, so it better exist!
- return getProperty(engine, object, property, captureRequired);
-}
-
-ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty)
+ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, QQmlPropertyData **property)
{
if (QQmlData::wasDeleted(object)) {
if (hasProperty)
@@ -361,13 +360,8 @@ ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlC
return QV4::Encode::null();
}
- if (name->equals(engine->id_destroy()) || name->equals(engine->id_toString())) {
- int index = name->equals(engine->id_destroy()) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod;
- if (hasProperty)
- *hasProperty = true;
- ExecutionContext *global = engine->rootContext();
- return QV4::QObjectMethod::create(global, object, index);
- }
+ if (auto methodValue = getDestroyOrToStringMethod(engine, name, object, hasProperty))
+ return *methodValue;
QQmlData *ddata = QQmlData::get(object, false);
QQmlPropertyData local;
@@ -385,6 +379,9 @@ ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlC
if (hasProperty)
*hasProperty = true;
+ if (property)
+ *property = result;
+
return getProperty(engine, object, result);
} else {
// Check if this object is already wrapped.
@@ -829,6 +826,71 @@ OwnPropertyKeyIterator *QObjectWrapper::virtualOwnPropertyKeys(const Object *m,
return new QObjectWrapperOwnPropertyKeyIterator;
}
+ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
+{
+ // Keep this code in sync with ::getQmlProperty
+ PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->
+ runtimeStrings[lookup->nameIndex]);
+ if (!id.isString())
+ return Object::virtualResolveLookupGetter(object, engine, lookup);
+ Scope scope(engine);
+
+ const QObjectWrapper *This = static_cast<const QObjectWrapper *>(object);
+ ScopedString name(scope, id.asStringOrSymbol());
+ QQmlContextData *qmlContext = engine->callingQmlContext();
+
+ QObject * const qobj = This->d()->object();
+
+ if (QQmlData::wasDeleted(qobj))
+ return QV4::Encode::undefined();
+
+ if (auto methodValue = getDestroyOrToStringMethod(engine, name, qobj))
+ return *methodValue;
+
+ QQmlData *ddata = QQmlData::get(qobj, false);
+ if (!ddata || !ddata->propertyCache) {
+ QQmlPropertyData local;
+ QQmlPropertyData *property = QQmlPropertyCache::property(engine->jsEngine(), qobj, name, qmlContext, local);
+ return getProperty(engine, qobj, property);
+ }
+ QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobj, qmlContext);
+
+ if (!property) {
+ // Check for attached properties
+ if (name->startsWithUpper()) {
+ if (auto importProperty = getPropertyFromImports(engine, name, qmlContext, qobj))
+ return *importProperty;
+ }
+ return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
+ }
+
+ lookup->qobjectLookup.ic = This->internalClass();
+ lookup->qobjectLookup.staticQObject = nullptr;
+ lookup->qobjectLookup.propertyCache = ddata->propertyCache;
+ lookup->qobjectLookup.propertyCache->addref();
+ lookup->qobjectLookup.propertyData = property;
+ lookup->getter = QV4::QObjectWrapper::lookupGetter;
+ return lookup->getter(lookup, engine, *object);
+}
+
+ReturnedValue QObjectWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object)
+{
+ const auto revertLookup = [lookup, engine, &object]() {
+ lookup->qobjectLookup.propertyCache->release();
+ lookup->qobjectLookup.propertyCache = nullptr;
+ lookup->getter = Lookup::getterGeneric;
+ return Lookup::getterGeneric(lookup, engine, object);
+ };
+
+ return lookupGetterImpl(lookup, engine, object, /*useOriginalProperty*/ false, revertLookup);
+}
+
+bool QObjectWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup,
+ const Value &value)
+{
+ return Object::virtualResolveLookupSetter(object, engine, lookup, value);
+}
+
namespace QV4 {
struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
@@ -1127,15 +1189,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 {
@@ -1168,14 +1229,13 @@ private:
std::vector<QUrl> *stdVectorQUrlPtr;
std::vector<QModelIndex> *stdVectorQModelIndexPtr;
- 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;
};
@@ -1186,7 +1246,6 @@ private:
QVariant *qvariantPtr;
QList<QObject *> *qlistPtr;
QJSValue *qjsValuePtr;
- QQmlV4Handle *handlePtr;
QJsonArray *jsonArrayPtr;
QJsonObject *jsonObjectPtr;
QJsonValue *jsonValuePtr;
@@ -1658,9 +1717,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();
@@ -1761,9 +1817,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));
@@ -1888,8 +1941,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) {
@@ -1923,13 +1974,13 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, in
return method.asReturnedValue();
}
-ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index)
+ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index)
{
Scope valueScope(scope);
Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
- method->d()->setPropertyCache(valueType->d()->propertyCache());
+ method->d()->setPropertyCache(valueType->propertyCache());
method->d()->index = index;
- method->d()->valueTypeWrapper.set(valueScope.engine, valueType->d());
+ method->d()->valueTypeWrapper.set(valueScope.engine, valueType);
return method.asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index 43a53ac673..795bf241f2 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -60,6 +60,7 @@
#include <private/qv4value_p.h>
#include <private/qv4functionobject_p.h>
+#include <private/qv4lookup_p.h>
QT_BEGIN_NAMESPACE
@@ -165,7 +166,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
QObject *object() const { return d()->object(); }
ReturnedValue getQmlProperty(QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, bool includeImports = false) const;
- static ReturnedValue getQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr);
+ static ReturnedValue getQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, QQmlPropertyData **property = nullptr);
static bool setQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, const Value &value);
@@ -174,13 +175,18 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
using Object::get;
- static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired);
static void setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value);
void setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value);
void destroyObject(bool lastCall);
- static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired = true);
+ static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property);
+
+ static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
+ static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object);
+ template <typename ReversalFunctor> static ReturnedValue lookupGetterImpl(Lookup *l, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revert);
+ static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
+
protected:
static void setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value);
@@ -216,6 +222,47 @@ inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *obje
return wrap_slowPath(engine, object);
}
+template <typename ReversalFunctor>
+inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revertLookup)
+{
+ // we can safely cast to a QV4::Object here. If object is something else,
+ // the internal class won't match
+ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
+ if (!o || o->internalClass != lookup->qobjectLookup.ic)
+ return revertLookup();
+
+ const Heap::QObjectWrapper *This = lookup->qobjectLookup.staticQObject ? lookup->qobjectLookup.staticQObject :
+ static_cast<const Heap::QObjectWrapper *>(o);
+ QObject *qobj = This->object();
+ if (QQmlData::wasDeleted(qobj))
+ return QV4::Encode::undefined();
+
+ QQmlData *ddata = QQmlData::get(qobj, /*create*/false);
+ if (!ddata)
+ return revertLookup();
+
+ QQmlPropertyData *property = lookup->qobjectLookup.propertyData;
+ if (ddata->propertyCache != lookup->qobjectLookup.propertyCache) {
+ if (property->isOverridden() && (!useOriginalProperty || property->isFunction() || property->isSignalHandler()))
+ return revertLookup();
+
+ QQmlPropertyCache *fromMo = ddata->propertyCache;
+ QQmlPropertyCache *toMo = lookup->qobjectLookup.propertyCache;
+ bool canConvert = false;
+ while (fromMo) {
+ if (fromMo == toMo) {
+ canConvert = true;
+ break;
+ }
+ fromMo = fromMo->parent();
+ }
+ if (!canConvert)
+ return revertLookup();
+ }
+
+ return getProperty(engine, qobj, property);
+}
+
struct QQmlValueTypeWrapper;
struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
@@ -226,7 +273,7 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
enum { DestroyMethod = -1, ToStringMethod = -2 };
static ReturnedValue create(QV4::ExecutionContext *scope, QObject *object, int index);
- static ReturnedValue create(QV4::ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index);
+ static ReturnedValue create(QV4::ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index);
int methodIndex() const { return d()->index; }
QObject *object() const { return d()->object(); }
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 9753ee4b1d..8f2b162106 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -63,7 +63,6 @@
#include "qv4qobjectwrapper_p.h"
#include "qv4symbol_p.h"
#include "qv4generatorobject_p.h"
-#include <private/qv8engine_p.h>
#endif
#include <QtCore/QDebug>
@@ -1130,6 +1129,12 @@ ReturnedValue Runtime::LoadGlobalLookup::call(ExecutionEngine *engine, Function
return l->globalGetter(l, engine);
}
+ReturnedValue Runtime::LoadQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index)
+{
+ Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
+ return l->qmlContextPropertyGetter(l, engine, nullptr);
+}
+
ReturnedValue Runtime::GetLookup::call(ExecutionEngine *engine, Function *f, const Value &base, int index)
{
Lookup *l = f->compilationUnit->runtimeLookups + index;
@@ -1390,18 +1395,43 @@ uint Runtime::CompareIn::call(ExecutionEngine *engine, const Value &left, const
return v->booleanValue();
}
+static ReturnedValue throwPropertyIsNotAFunctionTypeError(ExecutionEngine *engine, Value *thisObject, const QString &propertyName)
+{
+ QString objectAsString = QStringLiteral("[null]");
+ if (!thisObject->isUndefined())
+ objectAsString = thisObject->toQStringNoThrow();
+ QString msg = QStringLiteral("Property '%1' of object %2 is not a function")
+ .arg(propertyName, objectAsString);
+ return engine->throwTypeError(msg);
+}
ReturnedValue Runtime::CallGlobalLookup::call(ExecutionEngine *engine, uint index, Value argv[], int argc)
{
+ Scope scope(engine);
Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
Value function = Value::fromReturnedValue(l->globalGetter(l, engine));
+ Value thisObject = Value::undefinedValue();
if (!function.isFunctionObject())
- return engine->throwTypeError();
+ return throwPropertyIsNotAFunctionTypeError(engine, &thisObject,
+ engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString());
- Value thisObject = Value::undefinedValue();
return static_cast<FunctionObject &>(function).call(&thisObject, argv, 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;
+ Value function = Value::fromReturnedValue(l->qmlContextPropertyGetter(l, engine, thisObject));
+ 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::CallPossiblyDirectEval::call(ExecutionEngine *engine, Value *argv, int argc)
{
Scope scope(engine);
@@ -1412,13 +1442,8 @@ ReturnedValue Runtime::CallPossiblyDirectEval::call(ExecutionEngine *engine, Val
if (engine->hasException)
return Encode::undefined();
- if (!function) {
- QString objectAsString = QStringLiteral("[null]");
- if (!thisObject->isUndefined())
- objectAsString = thisObject->toQStringNoThrow();
- QString msg = QStringLiteral("Property 'eval' of object %2 is not a function").arg(objectAsString);
- return engine->throwTypeError(msg);
- }
+ if (!function)
+ return throwPropertyIsNotAFunctionTypeError(engine, thisObject, QLatin1String("eval"));
if (function->d() == engine->evalFunction()->d())
return static_cast<EvalFunction *>(function.getPointer())->evalCall(thisObject, argv, argc, true);
@@ -1437,15 +1462,9 @@ ReturnedValue Runtime::CallName::call(ExecutionEngine *engine, int nameIndex, Va
if (engine->hasException)
return Encode::undefined();
- if (!f) {
- QString objectAsString = QStringLiteral("[null]");
- if (!thisObject->isUndefined())
- objectAsString = thisObject->toQStringNoThrow();
- QString msg = QStringLiteral("Property '%1' of object %2 is not a function")
- .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(),
- objectAsString);
- return engine->throwTypeError(msg);
- }
+ if (!f)
+ return throwPropertyIsNotAFunctionTypeError(engine, thisObject,
+ engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString());
return f->call(thisObject, argv, argc);
}
@@ -1482,7 +1501,7 @@ ReturnedValue Runtime::CallProperty::call(ExecutionEngine *engine, const Value &
if (!f) {
QString error = QStringLiteral("Property '%1' of object %2 is not a function")
- .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(),
+ .arg(name->toQString(),
base->toQStringNoThrow());
return engine->throwTypeError(error);
}
@@ -1536,41 +1555,6 @@ ReturnedValue Runtime::CallWithReceiver::call(ExecutionEngine *engine, const Val
return static_cast<const FunctionObject &>(func).call(&thisObject, argv, argc);
}
-ReturnedValue Runtime::CallQmlScopeObjectProperty::call(ExecutionEngine *engine, const Value &base,
- int propertyIndex, Value argv[], int argc)
-{
- Scope scope(engine);
- ScopedFunctionObject fo(scope, LoadQmlScopeObjectProperty::call(engine, base, propertyIndex,
- /*captureRequired*/true));
- if (!fo) {
- QString error = QStringLiteral("Property '%1' of scope object is not a function").arg(propertyIndex);
- return engine->throwTypeError(error);
- }
-
- QObject *qmlScopeObj = static_cast<const QmlContext *>(&base)->d()->qml()->scopeObject;
- ScopedValue qmlScopeValue(scope, QObjectWrapper::wrap(engine, qmlScopeObj));
- return fo->call(qmlScopeValue, argv, argc);
-}
-
-ReturnedValue Runtime::CallQmlContextObjectProperty::call(ExecutionEngine *engine,
- const Value &base,
- int propertyIndex,
- Value argv[],
- int argc)
-{
- Scope scope(engine);
- ScopedFunctionObject fo(scope, LoadQmlContextObjectProperty::call(engine, base, propertyIndex,
- /*captureRequired*/true));
- if (!fo) {
- QString error = QStringLiteral("Property '%1' of context object is not a function").arg(propertyIndex);
- return engine->throwTypeError(error);
- }
-
- QObject *qmlContextObj = static_cast<const QmlContext *>(&base)->d()->qml()->context->contextData()->contextObject;
- ScopedValue qmlContextValue(scope, QObjectWrapper::wrap(engine, qmlContextObj));
- return fo->call(qmlContextValue, argv, argc);
-}
-
struct CallArgs {
Value *argv;
int argc;
@@ -2013,66 +1997,12 @@ QV4::ReturnedValue Runtime::CreateRestParameter::call(ExecutionEngine *engine, i
return engine->newArrayObject(values, nValues)->asReturnedValue();
}
-
-ReturnedValue Runtime::LoadQmlContext::call(ExecutionEngine *engine)
-{
- Heap::QmlContext *ctx = engine->qmlContext();
- Q_ASSERT(ctx);
- return ctx->asReturnedValue();
-}
-
ReturnedValue Runtime::RegexpLiteral::call(ExecutionEngine *engine, int id)
{
Heap::RegExpObject *ro = engine->newRegExpObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id].as<RegExp>());
return ro->asReturnedValue();
}
-ReturnedValue Runtime::LoadQmlScopeObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, Bool captureRequired)
-{
- const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::getProperty(engine, c.d()->qml()->scopeObject, propertyIndex, captureRequired);
-}
-
-ReturnedValue Runtime::LoadQmlContextObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, Bool captureRequired)
-{
- const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::getProperty(engine, (*c.d()->qml()->context)->contextObject, propertyIndex, captureRequired);
-}
-
-ReturnedValue Runtime::LoadQmlIdObject::call(ExecutionEngine *engine, const Value &c, uint index)
-{
- const QmlContext &qmlContext = static_cast<const QmlContext &>(c);
- QQmlContextData *context = *qmlContext.d()->qml()->context;
- if (!context || index >= (uint)context->idValueCount)
- return Encode::undefined();
-
- QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr;
- if (ep && ep->propertyCapture)
- ep->propertyCapture->captureProperty(&context->idValues[index].bindings);
-
- return QObjectWrapper::wrap(engine, context->idValues[index].data());
-}
-
-void Runtime::StoreQmlScopeObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)
-{
- const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::setProperty(engine, c.d()->qml()->scopeObject, propertyIndex, value);
-}
-
-void Runtime::StoreQmlContextObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)
-{
- const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::setProperty(engine, (*c.d()->qml()->context)->contextObject, propertyIndex, value);
-}
-
-ReturnedValue Runtime::LoadQmlImportedScripts::call(ExecutionEngine *engine)
-{
- QQmlContextData *context = engine->callingQmlContext();
- if (!context)
- return Encode::undefined();
- return context->importedScripts.value();
-}
-
ReturnedValue Runtime::ToObject::call(ExecutionEngine *engine, const Value &obj)
{
if (obj.isObject())
@@ -2155,6 +2085,7 @@ ReturnedValue Runtime::Div::call(const Value &left, const Value &right)
int lval = left.integerValue();
int rval = right.integerValue();
if (rval != 0 // division by zero should result in a NaN
+ && !(lval == std::numeric_limits<int>::min() && rval == -1) // doesn't fit in int
&& (lval % rval == 0) // fractions can't be stored in an int
&& !(lval == 0 && rval < 0)) // 0 / -something results in -0.0
return Encode(int(lval / rval));
diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h
index 0312522d90..86cbccde23 100644
--- a/src/qml/jsruntime/qv4runtimeapi_p.h
+++ b/src/qml/jsruntime/qv4runtimeapi_p.h
@@ -58,7 +58,6 @@ namespace QV4 {
typedef uint Bool;
-
struct Q_QML_PRIVATE_EXPORT Runtime {
typedef ReturnedValue (*UnaryOperation)(const Value &value);
typedef ReturnedValue (*BinaryOperation)(const Value &left, const Value &right);
@@ -87,6 +86,10 @@ struct Q_QML_PRIVATE_EXPORT Runtime {
{
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);
@@ -187,6 +190,10 @@ struct Q_QML_PRIVATE_EXPORT Runtime {
{
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);
@@ -495,45 +502,6 @@ struct Q_QML_PRIVATE_EXPORT Runtime {
static ReturnedValue call(Function *, int);
};
- /* qml */
- struct Q_QML_PRIVATE_EXPORT LoadQmlContext : Method<Throws::No>
- {
- static ReturnedValue call(ExecutionEngine *);
- };
- struct Q_QML_PRIVATE_EXPORT LoadQmlImportedScripts : Method<Throws::No>
- {
- static ReturnedValue call(ExecutionEngine *);
- };
- struct Q_QML_PRIVATE_EXPORT LoadQmlScopeObjectProperty : Method<Throws::Yes>
- {
- static ReturnedValue call(ExecutionEngine *, const Value &, int, Bool);
- };
- struct Q_QML_PRIVATE_EXPORT LoadQmlContextObjectProperty : Method<Throws::Yes>
- {
- static ReturnedValue call(ExecutionEngine *, const Value &, int, Bool);
- };
- struct Q_QML_PRIVATE_EXPORT LoadQmlIdObject : Method<Throws::Yes>
- {
- static ReturnedValue call(ExecutionEngine *, const Value &, uint);
- };
- struct Q_QML_PRIVATE_EXPORT CallQmlScopeObjectProperty : Method<Throws::Yes>
- {
- static ReturnedValue call(ExecutionEngine *, const Value &, int, Value[], int);
- };
- struct Q_QML_PRIVATE_EXPORT CallQmlContextObjectProperty : Method<Throws::Yes>
- {
- static ReturnedValue call(ExecutionEngine *, const Value &, int, Value[], int);
- };
-
- struct Q_QML_PRIVATE_EXPORT StoreQmlScopeObjectProperty : Method<Throws::Yes>
- {
- static void call(ExecutionEngine *, const Value &, int, const Value &);
- };
- struct Q_QML_PRIVATE_EXPORT StoreQmlContextObjectProperty : Method<Throws::Yes>
- {
- static void call(ExecutionEngine *, const Value &, int, const Value &);
- };
-
struct StackOffsets {
static const int tailCall_function = -1;
static const int tailCall_thisObject = -2;
diff --git a/src/qml/jsruntime/qv4runtimecodegen_p.h b/src/qml/jsruntime/qv4runtimecodegen_p.h
index be66dc57ca..006a6a3cde 100644
--- a/src/qml/jsruntime/qv4runtimecodegen_p.h
+++ b/src/qml/jsruntime/qv4runtimecodegen_p.h
@@ -71,6 +71,7 @@ public:
void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) override;
void throwReferenceError(const AST::SourceLocation &loc, const QString &detail) override;
+
private:
ExecutionEngine *engine;
};
diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp
index 7bbef3335e..6cb2e95cdc 100644
--- a/src/qml/jsruntime/qv4script.cpp
+++ b/src/qml/jsruntime/qv4script.cpp
@@ -201,7 +201,6 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compi
}
Codegen cg(unitGenerator, /*strict mode*/false);
- cg.setUseFastLookups(false);
cg.generateFromProgram(fileName, finalUrl, source, program, module, contextType);
errors = cg.qmlErrors();
if (!errors.isEmpty()) {
diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp
index 50871a4d87..a84521e205 100644
--- a/src/qml/jsruntime/qv4serialize.cpp
+++ b/src/qml/jsruntime/qv4serialize.cpp
@@ -39,7 +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>
@@ -375,7 +374,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
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);
+ 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);
diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp
index 8186153ba4..227df4014e 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -130,7 +130,7 @@ PropertyKey StringObjectOwnPropertyKeyIterator::next(const QV4::Object *o, Prope
return PropertyKey::fromArrayIndex(index);
} else if (arrayIndex == slen) {
if (s->arrayData()) {
- arrayNode = s->sparseBegin();
+ SparseArrayNode *arrayNode = s->sparseBegin();
// iterate until we're past the end of the string
while (arrayNode && arrayNode->key() < slen)
arrayNode = arrayNode->nextNode();
@@ -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);
}
@@ -765,9 +766,8 @@ static void appendReplacementString(QString *result, const QString &input, const
i += skip;
if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch)
*result += input.midRef(substStart, substEnd - substStart);
- else {
+ else if (skip == 0) // invalid capture reference. Taken as literal value
*result += replaceValue.at(i);
- }
} else {
*result += replaceValue.at(i);
}
diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp
index d83f021450..43e1dabb6d 100644
--- a/src/qml/jsruntime/qv4typedarray.cpp
+++ b/src/qml/jsruntime/qv4typedarray.cpp
@@ -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;
diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h
index b1cae8796f..da08841026 100644
--- a/src/qml/jsruntime/qv4value_p.h
+++ b/src/qml/jsruntime/qv4value_p.h
@@ -877,6 +877,22 @@ struct ValueArray {
// have wrong offsets between host and target.
Q_STATIC_ASSERT(offsetof(ValueArray<0>, values) == 8);
+class OptionalReturnedValue {
+ ReturnedValue value;
+public:
+
+ OptionalReturnedValue() : value(Value::emptyValue().asReturnedValue()) {}
+ explicit OptionalReturnedValue(ReturnedValue v)
+ : value(v)
+ {
+ Q_ASSERT(!Value::fromReturnedValue(v).isEmpty());
+ }
+
+ ReturnedValue operator->() const { return value; }
+ ReturnedValue operator*() const { return value; }
+ explicit operator bool() const { return !Value::fromReturnedValue(value).isEmpty(); }
+};
+
}
QT_END_NAMESPACE
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 3098837e1c..98e4f4f7b9 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -388,6 +388,18 @@ static inline void traceValue(ReturnedValue acc, Function *f, int slot)
#endif
}
+static inline void traceIntValue(Function *f, int slot)
+{
+#if QT_CONFIG(qml_tracing)
+ quint8 *traceInfo = f->traceInfo(slot);
+ Q_ASSERT(traceInfo);
+ *traceInfo |= quint8(ObservedTraceValues::Integer);
+#else
+ Q_UNUSED(f);
+ Q_UNUSED(slot);
+#endif
+}
+
static inline void traceDoubleValue(Function *f, int slot)
{
#if QT_CONFIG(qml_tracing)
@@ -657,6 +669,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
traceValue(acc, function, traceSlot);
MOTH_END_INSTR(LoadGlobalLookup)
+ MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup)
+ STORE_IP();
+ QV4::Lookup *l = function->compilationUnit->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();
@@ -707,7 +727,17 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(GetLookup)
STORE_IP();
STORE_ACC();
+
QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
+
+ if (accumulator.isNullOrUndefined()) {
+ QString message = QStringLiteral("Cannot read property '%1' of %2")
+ .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
+ .arg(accumulator.toQStringNoThrow());
+ acc = engine->throwTypeError(message);
+ goto handleUnwind;
+ }
+
acc = l->getter(l, engine, accumulator);
CHECK_EXCEPTION;
traceValue(acc, function, traceSlot);
@@ -743,37 +773,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreSuperProperty)
- MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
- STORE_ACC();
- Runtime::StoreQmlScopeObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, accumulator);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(StoreScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadScopeObjectProperty)
- STORE_IP();
- acc = Runtime::LoadQmlScopeObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, captureRequired);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(StoreContextObjectProperty)
- STORE_IP();
- STORE_ACC();
- Runtime::StoreQmlContextObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, accumulator);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(StoreContextObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadContextObjectProperty)
- STORE_IP();
- acc = Runtime::LoadQmlContextObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, captureRequired);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadContextObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadIdObject)
- STORE_IP();
- acc = Runtime::LoadQmlIdObject::call(engine, STACK_VALUE(base), index);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadIdObject)
-
MOTH_BEGIN_INSTR(Yield)
frame->yield = code;
frame->yieldIsIterator = false;
@@ -840,11 +839,23 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(CallPropertyLookup)
STORE_IP();
Lookup *l = function->compilationUnit->runtimeLookups + lookupIndex;
+
+ if (stack[base].isNullOrUndefined()) {
+ QString message = QStringLiteral("Cannot call method '%1' of %2")
+ .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
+ .arg(stack[base].toQStringNoThrow());
+ acc = engine->throwTypeError(message);
+ goto handleUnwind;
+ }
+
// ok to have the value on the stack here
Value f = Value::fromReturnedValue(l->getter(l, engine, stack[base]));
if (Q_UNLIKELY(!f.isFunctionObject())) {
- acc = engine->throwTypeError();
+ QString message = QStringLiteral("Property '%1' of object %2 is not a function")
+ .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
+ .arg(stack[base].toQStringNoThrow());
+ acc = engine->throwTypeError(message);
goto handleUnwind;
}
@@ -881,19 +892,12 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallGlobalLookup)
- MOTH_BEGIN_INSTR(CallScopeObjectProperty)
- STORE_IP();
- acc = Runtime::CallQmlScopeObjectProperty::call(engine, stack[base], name, stack + argv, argc);
- CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
- MOTH_END_INSTR(CallScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(CallContextObjectProperty)
+ MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup)
STORE_IP();
- acc = Runtime::CallQmlContextObjectProperty::call(engine, stack[base], name, stack + argv, argc);
+ acc = Runtime::CallQmlContextPropertyLookup::call(engine, index, stack + argv, argc);
CHECK_EXCEPTION;
traceValue(acc, function, traceSlot);
- MOTH_END_INSTR(CallContextObjectProperty)
+ MOTH_END_INSTR(CallQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(CallWithSpread)
STORE_IP();
@@ -1285,7 +1289,12 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_END_INSTR(UNot)
MOTH_BEGIN_INSTR(UPlus)
- if (Q_UNLIKELY(!ACC.isNumber())) {
+ if (Q_LIKELY(ACC.isNumber())) {
+ if (ACC.isDouble())
+ traceDoubleValue(function, traceSlot);
+ else
+ traceIntValue(function, traceSlot);
+ } else {
acc = Encode(ACC.toNumberImpl());
CHECK_EXCEPTION;
}
@@ -1503,14 +1512,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
#endif // QT_CONFIG(qml_debug)
MOTH_END_INSTR(Debug)
- MOTH_BEGIN_INSTR(LoadQmlContext)
- STACK_VALUE(result) = Runtime::LoadQmlContext::call(engine);
- MOTH_END_INSTR(LoadQmlContext)
-
- MOTH_BEGIN_INSTR(LoadQmlImportedScripts)
- STACK_VALUE(result) = Runtime::LoadQmlImportedScripts::call(engine);
- MOTH_END_INSTR(LoadQmlImportedScripts)
-
handleUnwind:
Q_ASSERT(engine->hasException || frame->unwindLevel);
if (!frame->unwindHandler) {
diff --git a/src/qml/jsruntime/qv4vtable_p.h b/src/qml/jsruntime/qv4vtable_p.h
index 00dcb962d3..a4d91640c5 100644
--- a/src/qml/jsruntime/qv4vtable_p.h
+++ b/src/qml/jsruntime/qv4vtable_p.h
@@ -56,6 +56,8 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
+struct Lookup;
+
struct OwnPropertyKeyIterator {
virtual ~OwnPropertyKeyIterator() = 0;
virtual PropertyKey next(const Object *o, Property *p = nullptr, PropertyAttributes *attrs = nullptr) = 0;
@@ -84,6 +86,9 @@ struct VTable
typedef ReturnedValue (*Call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
typedef ReturnedValue (*CallAsConstructor)(const FunctionObject *, const Value *argv, int argc, const Value *newTarget);
+ typedef ReturnedValue (*ResolveLookupGetter)(const Object *, ExecutionEngine *, Lookup *);
+ typedef bool (*ResolveLookupSetter)(Object *, ExecutionEngine *, Lookup *, const Value &);
+
const VTable * const parent;
quint16 inlinePropertyOffset;
quint16 nInlineProperties;
@@ -118,6 +123,9 @@ struct VTable
Call call;
CallAsConstructor callAsConstructor;
+
+ ResolveLookupGetter resolveLookupGetter;
+ ResolveLookupSetter resolveLookupSetter;
};
@@ -142,6 +150,9 @@ protected:
static constexpr VTable::Call virtualCall = nullptr;
static constexpr VTable::CallAsConstructor virtualCallAsConstructor = nullptr;
+
+ static constexpr VTable::ResolveLookupGetter virtualResolveLookupGetter = nullptr;
+ static constexpr VTable::ResolveLookupSetter virtualResolveLookupSetter = nullptr;
};
#define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \
@@ -181,6 +192,9 @@ protected:
\
classname::virtualCall, \
classname::virtualCallAsConstructor, \
+ \
+ classname::virtualResolveLookupGetter, \
+ classname::virtualResolveLookupSetter \
}
#define DEFINE_MANAGED_VTABLE(classname) \
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp
index 203f1f424f..34d334a24d 100644
--- a/src/qml/memory/qv4mm.cpp
+++ b/src/qml/memory/qv4mm.cpp
@@ -93,8 +93,6 @@
#include <pthread_np.h>
#endif
-#define MIN_UNMANAGED_HEAPSIZE_GC_LIMIT std::size_t(128 * 1024)
-
Q_LOGGING_CATEGORY(lcGcStats, "qt.qml.gc.statistics")
Q_DECLARE_LOGGING_CATEGORY(lcGcStats)
Q_LOGGING_CATEGORY(lcGcAllocatorStats, "qt.qml.gc.allocatorStats")
@@ -759,7 +757,7 @@ MemoryManager::MemoryManager(ExecutionEngine *engine)
, hugeItemAllocator(chunkAllocator, engine)
, m_persistentValues(new PersistentValueStorage(engine))
, m_weakValues(new PersistentValueStorage(engine))
- , unmanagedHeapSizeGCLimit(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT)
+ , unmanagedHeapSizeGCLimit(MinUnmanagedHeapSizeGCLimit)
, aggressiveGC(!qEnvironmentVariableIsEmpty("QV4_MM_AGGRESSIVE_GC"))
, gcStats(lcGcStats().isDebugEnabled())
, gcCollectorStats(lcGcAllocatorStats().isDebugEnabled())
@@ -779,35 +777,9 @@ Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize)
lastAllocRequestedSlots = stringSize >> Chunk::SlotSizeShift;
++allocationCount;
#endif
-
- bool didGCRun = false;
- if (aggressiveGC) {
- runGC();
- didGCRun = true;
- }
-
unmanagedHeapSize += unmanagedSize;
- if (unmanagedHeapSize > unmanagedHeapSizeGCLimit) {
- if (!didGCRun)
- runGC();
-
- if (3*unmanagedHeapSizeGCLimit <= 4*unmanagedHeapSize)
- // more than 75% full, raise limit
- unmanagedHeapSizeGCLimit = std::max(unmanagedHeapSizeGCLimit, unmanagedHeapSize) * 2;
- else if (unmanagedHeapSize * 4 <= unmanagedHeapSizeGCLimit)
- // less than 25% full, lower limit
- unmanagedHeapSizeGCLimit = qMax(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT, unmanagedHeapSizeGCLimit/2);
- didGCRun = true;
- }
-
- HeapItem *m = blockAllocator.allocate(stringSize);
- if (!m) {
- if (!didGCRun && shouldRunGC())
- runGC();
- m = blockAllocator.allocate(stringSize, true);
- }
-// qDebug() << "allocated string" << m;
+ HeapItem *m = allocate(&blockAllocator, stringSize);
memset(m, 0, stringSize);
return *m;
}
@@ -819,32 +791,11 @@ Heap::Base *MemoryManager::allocData(std::size_t size)
++allocationCount;
#endif
- bool didRunGC = false;
- if (aggressiveGC) {
- runGC();
- didRunGC = true;
- }
-
Q_ASSERT(size >= Chunk::SlotSize);
Q_ASSERT(size % Chunk::SlotSize == 0);
-// qDebug() << "unmanagedHeapSize:" << unmanagedHeapSize << "limit:" << unmanagedHeapSizeGCLimit << "unmanagedSize:" << unmanagedSize;
-
- if (size > Chunk::DataSize) {
- HeapItem *h = hugeItemAllocator.allocate(size);
-// qDebug() << "allocating huge item" << h;
- return *h;
- }
-
- HeapItem *m = blockAllocator.allocate(size);
- if (!m) {
- if (!didRunGC && shouldRunGC())
- runGC();
- m = blockAllocator.allocate(size, true);
- }
-
+ HeapItem *m = allocate(&blockAllocator, size);
memset(m, 0, size);
-// qDebug() << "allocating data" << m;
return *m;
}
@@ -1048,12 +999,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];
@@ -1062,7 +1013,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";
@@ -1072,7 +1023,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;
}
@@ -1113,7 +1064,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();
@@ -1131,7 +1083,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.";
@@ -1153,7 +1106,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) {
@@ -1175,7 +1129,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/qv4mm_p.h b/src/qml/memory/qv4mm_p.h
index bbbbb1aef6..6dfdd81cb2 100644
--- a/src/qml/memory/qv4mm_p.h
+++ b/src/qml/memory/qv4mm_p.h
@@ -264,13 +264,13 @@ public:
size_t getLargeItemsMem() const;
// called when a JS object grows itself. Specifically: Heap::String::append
+ // and InternalClassDataPrivate<PropertyAttributes>.
void changeUnmanagedHeapSizeUsage(qptrdiff delta) { unmanagedHeapSize += delta; }
template<typename ManagedType>
typename ManagedType::Data *allocIC()
{
- size_t size = align(sizeof(typename ManagedType::Data));
- Heap::Base *b = *icAllocator.allocate(size, true);
+ Heap::Base *b = *allocate(&icAllocator, align(sizeof(typename ManagedType::Data)));
return static_cast<typename ManagedType::Data *>(b);
}
@@ -284,12 +284,52 @@ protected:
Heap::Object *allocObjectWithMemberData(const QV4::VTable *vtable, uint nMembers);
private:
+ enum {
+ MinUnmanagedHeapSizeGCLimit = 128 * 1024
+ };
+
void collectFromJSStack(MarkStack *markStack) const;
void mark();
void sweep(bool lastSweep = false, ClassDestroyStatsCallback classCountPtr = nullptr);
bool shouldRunGC() const;
void collectRoots(MarkStack *markStack);
+ HeapItem *allocate(BlockAllocator *allocator, std::size_t size)
+ {
+ bool didGCRun = false;
+ if (aggressiveGC) {
+ runGC();
+ didGCRun = true;
+ }
+
+ if (unmanagedHeapSize > unmanagedHeapSizeGCLimit) {
+ if (!didGCRun)
+ runGC();
+
+ if (3*unmanagedHeapSizeGCLimit <= 4 * unmanagedHeapSize) {
+ // more than 75% full, raise limit
+ unmanagedHeapSizeGCLimit = std::max(unmanagedHeapSizeGCLimit,
+ unmanagedHeapSize) * 2;
+ } else if (unmanagedHeapSize * 4 <= unmanagedHeapSizeGCLimit) {
+ // less than 25% full, lower limit
+ unmanagedHeapSizeGCLimit = qMax(std::size_t(MinUnmanagedHeapSizeGCLimit),
+ unmanagedHeapSizeGCLimit/2);
+ }
+ didGCRun = true;
+ }
+
+ if (size > Chunk::DataSize)
+ return hugeItemAllocator.allocate(size);
+
+ if (HeapItem *m = allocator->allocate(size))
+ return m;
+
+ if (!didGCRun && shouldRunGC())
+ runGC();
+
+ return allocator->allocate(size, true);
+ }
+
public:
QV4::ExecutionEngine *engine;
ChunkAllocator *chunkAllocator;
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g
index b86dba6daa..0c947b541b 100644
--- a/src/qml/parser/qqmljs.g
+++ b/src/qml/parser/qqmljs.g
@@ -249,7 +249,7 @@
#include <QtCore/qlist.h>
#include <QtCore/qstring.h>
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS {
@@ -486,7 +486,7 @@ protected:
using namespace QQmlJS;
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
void Parser::reallocateStack()
{
@@ -614,16 +614,8 @@ bool Parser::parse(int startToken)
program = 0;
do {
- if (++tos == stack_size) {
+ if (++tos == stack_size)
reallocateStack();
- if (stack_size > 10000) {
- // We're now in some serious right-recursive stuff, which will probably result in
- // an AST that's so deep that recursively visiting it will run out of stack space.
- const QString msg = QCoreApplication::translate("QQmlParser", "Maximum statement or expression depth exceeded");
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
- return false;
- }
- }
state_stack[tos] = action;
@@ -4484,12 +4476,12 @@ ExportSpecifier: IdentifierName T_AS IdentifierName;
return false;
}
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
./
/:
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp
index 4ebb2d3b5c..e5817ab763 100644
--- a/src/qml/parser/qqmljsast.cpp
+++ b/src/qml/parser/qqmljsast.cpp
@@ -41,7 +41,7 @@
#include "qqmljsastvisitor_p.h"
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS { namespace AST {
@@ -65,21 +65,6 @@ ClassExpression *asAnonymousClassDefinition(Node *n)
return c;
}
-
-void Node::accept(Visitor *visitor)
-{
- if (visitor->preVisit(this)) {
- accept0(visitor);
- }
- visitor->postVisit(this);
-}
-
-void Node::accept(Node *node, Visitor *visitor)
-{
- if (node)
- node->accept(visitor);
-}
-
ExpressionNode *Node::expressionCast()
{
return nullptr;
@@ -1489,6 +1474,6 @@ LeftHandSideExpression *LeftHandSideExpression::leftHandSideExpressionCast()
} } // namespace QQmlJS::AST
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h
index 43aeec6525..b81553776d 100644
--- a/src/qml/parser/qqmljsast_p.h
+++ b/src/qml/parser/qqmljsast_p.h
@@ -57,7 +57,7 @@
#include <QtCore/qstring.h>
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
#define QQMLJS_DECLARE_AST_NODE(name) \
enum { K = Kind_##name };
@@ -271,11 +271,29 @@ public:
virtual FunctionExpression *asFunctionDefinition();
virtual ClassExpression *asClassDefinition();
- void accept(Visitor *visitor);
- static void accept(Node *node, Visitor *visitor);
+ inline void accept(Visitor *visitor)
+ {
+ Visitor::RecursionDepthCheck recursionCheck(visitor);
+ if (recursionCheck()) {
+ if (visitor->preVisit(this))
+ accept0(visitor);
+ visitor->postVisit(this);
+ } else {
+ visitor->throwRecursionDepthError();
+ }
+ }
+ inline static void accept(Node *node, Visitor *visitor)
+ {
+ if (node)
+ node->accept(visitor);
+ }
+
+ // ### Remove when we can. This is part of the qmldevtools library, though.
inline static void acceptChild(Node *node, Visitor *visitor)
- { return accept(node, visitor); } // ### remove
+ {
+ return accept(node, visitor);
+ }
virtual void accept0(Visitor *visitor) = 0;
virtual SourceLocation firstSourceLocation() const = 0;
@@ -3377,6 +3395,6 @@ public:
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
#endif
diff --git a/src/qml/parser/qqmljsastfwd_p.h b/src/qml/parser/qqmljsastfwd_p.h
index 7795e0ce71..e9caa918d5 100644
--- a/src/qml/parser/qqmljsastfwd_p.h
+++ b/src/qml/parser/qqmljsastfwd_p.h
@@ -56,7 +56,7 @@
// We mean it.
//
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS { namespace AST {
@@ -181,6 +181,6 @@ class UiEnumMemberList;
} } // namespace AST
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
#endif
diff --git a/src/qml/parser/qqmljsastvisitor.cpp b/src/qml/parser/qqmljsastvisitor.cpp
index eec151298e..5ecac36423 100644
--- a/src/qml/parser/qqmljsastvisitor.cpp
+++ b/src/qml/parser/qqmljsastvisitor.cpp
@@ -39,11 +39,11 @@
#include "qqmljsastvisitor_p.h"
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS { namespace AST {
-Visitor::Visitor()
+Visitor::Visitor(quint16 parentRecursionDepth) : m_recursionDepth(parentRecursionDepth)
{
}
@@ -53,4 +53,4 @@ Visitor::~Visitor()
} } // namespace QQmlJS::AST
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
diff --git a/src/qml/parser/qqmljsastvisitor_p.h b/src/qml/parser/qqmljsastvisitor_p.h
index c925096de6..9115449a46 100644
--- a/src/qml/parser/qqmljsastvisitor_p.h
+++ b/src/qml/parser/qqmljsastvisitor_p.h
@@ -54,14 +54,40 @@
#include "qqmljsastfwd_p.h"
#include "qqmljsglobal_p.h"
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS { namespace AST {
class QML_PARSER_EXPORT Visitor
{
public:
- Visitor();
+ class RecursionDepthCheck
+ {
+ Q_DISABLE_COPY(RecursionDepthCheck)
+ public:
+ RecursionDepthCheck(RecursionDepthCheck &&) = delete;
+ RecursionDepthCheck &operator=(RecursionDepthCheck &&) = delete;
+
+ RecursionDepthCheck(Visitor *visitor) : m_visitor(visitor)
+ {
+ ++(m_visitor->m_recursionDepth);
+ }
+
+ ~RecursionDepthCheck()
+ {
+ --(m_visitor->m_recursionDepth);
+ }
+
+ bool operator()() const {
+ return m_visitor->m_recursionDepth < s_recursionLimit;
+ }
+
+ private:
+ static const quint16 s_recursionLimit = 4096;
+ Visitor *m_visitor;
+ };
+
+ Visitor(quint16 parentRecursionDepth = 0);
virtual ~Visitor();
virtual bool preVisit(Node *) { return true; }
@@ -374,10 +400,18 @@ public:
virtual bool visit(DebuggerStatement *) { return true; }
virtual void endVisit(DebuggerStatement *) {}
+
+ virtual void throwRecursionDepthError() = 0;
+
+ quint16 recursionDepth() const { return m_recursionDepth; }
+
+protected:
+ quint16 m_recursionDepth = 0;
+ friend class RecursionDepthCheck;
};
} } // namespace AST
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
#endif // QQMLJSASTVISITOR_P_H
diff --git a/src/qml/parser/qqmljsengine_p.cpp b/src/qml/parser/qqmljsengine_p.cpp
index 97ce6ebea3..bb27f3992e 100644
--- a/src/qml/parser/qqmljsengine_p.cpp
+++ b/src/qml/parser/qqmljsengine_p.cpp
@@ -44,7 +44,7 @@
#include <QtCore/qhash.h>
#include <QtCore/qdebug.h>
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS {
@@ -155,4 +155,4 @@ QStringRef Engine::newStringRef(const QChar *chars, int size)
} // end of namespace QQmlJS
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
diff --git a/src/qml/parser/qqmljsengine_p.h b/src/qml/parser/qqmljsengine_p.h
index 07b5026eb9..b7f7da9478 100644
--- a/src/qml/parser/qqmljsengine_p.h
+++ b/src/qml/parser/qqmljsengine_p.h
@@ -58,7 +58,7 @@
#include <QtCore/qstring.h>
#include <QtCore/qset.h>
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS {
@@ -150,6 +150,6 @@ double integerFromString(const char *buf, int size, int radix);
} // end of namespace QQmlJS
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
#endif // QQMLJSENGINE_P_H
diff --git a/src/qml/parser/qqmljsglobal_p.h b/src/qml/parser/qqmljsglobal_p.h
index 0e195994b4..bf8155c6ec 100644
--- a/src/qml/parser/qqmljsglobal_p.h
+++ b/src/qml/parser/qqmljsglobal_p.h
@@ -53,8 +53,6 @@
#include <QtCore/qglobal.h>
#ifdef QT_CREATOR
-# define QT_QML_BEGIN_NAMESPACE
-# define QT_QML_END_NAMESPACE
# ifdef QDECLARATIVEJS_BUILD_DIR
# define QML_PARSER_EXPORT Q_DECL_EXPORT
@@ -65,8 +63,6 @@
# endif // QQMLJS_BUILD_DIR
#else // !QT_CREATOR
-# define QT_QML_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
-# define QT_QML_END_NAMESPACE QT_END_NAMESPACE
# ifndef QT_STATIC
# if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB)
// QmlDevTools is a static library
diff --git a/src/qml/parser/qqmljskeywords_p.h b/src/qml/parser/qqmljskeywords_p.h
index b0a4951ece..96b3709162 100644
--- a/src/qml/parser/qqmljskeywords_p.h
+++ b/src/qml/parser/qqmljskeywords_p.h
@@ -53,7 +53,7 @@
#include "qqmljslexer_p.h"
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS {
@@ -918,6 +918,6 @@ int Lexer::classify(const QChar *s, int n, int parseModeFlags) {
} // namespace QQmlJS
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
#endif // QQMLJSKEYWORDS_P_H
diff --git a/src/qml/parser/qqmljslexer_p.h b/src/qml/parser/qqmljslexer_p.h
index 03f33f6e06..51152bfd6e 100644
--- a/src/qml/parser/qqmljslexer_p.h
+++ b/src/qml/parser/qqmljslexer_p.h
@@ -57,7 +57,7 @@
#include <QtCore/qstring.h>
#include <QtCore/qstack.h>
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS {
@@ -257,6 +257,6 @@ private:
} // end of namespace QQmlJS
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
#endif // LEXER_H
diff --git a/src/qml/parser/qqmljsmemorypool_p.h b/src/qml/parser/qqmljsmemorypool_p.h
index bcd6d8672b..e7b1f46414 100644
--- a/src/qml/parser/qqmljsmemorypool_p.h
+++ b/src/qml/parser/qqmljsmemorypool_p.h
@@ -59,7 +59,7 @@
#include <cstring>
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS {
@@ -251,6 +251,6 @@ public:
} // namespace QQmlJS
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
#endif
diff --git a/src/qml/parser/qqmljssourcelocation_p.h b/src/qml/parser/qqmljssourcelocation_p.h
index dc307ba168..d76e701d49 100644
--- a/src/qml/parser/qqmljssourcelocation_p.h
+++ b/src/qml/parser/qqmljssourcelocation_p.h
@@ -55,7 +55,7 @@
// We mean it.
//
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS { namespace AST {
@@ -82,6 +82,6 @@ public:
} } // namespace AST
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
#endif
diff --git a/src/qml/qml.pro b/src/qml/qml.pro
index 94717a8f43..d96a1c285a 100644
--- a/src/qml/qml.pro
+++ b/src/qml/qml.pro
@@ -80,6 +80,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/qml.pri b/src/qml/qml/qml.pri
index 0895e5ae68..9f79bfacdf 100644
--- a/src/qml/qml/qml.pri
+++ b/src/qml/qml/qml.pri
@@ -102,7 +102,6 @@ HEADERS += \
$$PWD/qqmlpropertycachevector_p.h \
$$PWD/qqmlpropertydata_p.h \
$$PWD/qqmlpropertyindex_p.h \
- $$PWD/qqmlpropertyrawdata_p.h \
$$PWD/qqmlmetaobject_p.h \
$$PWD/qqmlnotifier_p.h \
$$PWD/qqmlobjectorgadget_p.h \
diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h
index 05a9f70247..3000f56601 100644
--- a/src/qml/qml/qqml.h
+++ b/src/qml/qml/qqml.h
@@ -584,7 +584,6 @@ namespace QtQml {
const QMetaObject *, bool create);
#ifndef Q_QDOC
}
-#endif
QT_WARNING_PUSH
QT_WARNING_DISABLE_CLANG("-Wheader-hygiene")
@@ -594,6 +593,8 @@ using namespace QtQml;
QT_WARNING_POP
+#endif // Q_QDOC
+
//The C++ version of protected namespaces in qmldir
Q_QML_EXPORT bool qmlProtectModule(const char* uri, int majVersion);
Q_QML_EXPORT void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor);
@@ -605,8 +606,6 @@ QObject *qmlAttachedPropertiesObject(const QObject *obj, bool create = true)
return qmlAttachedPropertiesObject(&idx, obj, &T::staticMetaObject, 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 *))
{
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index a949df4968..b164517011 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -260,8 +260,6 @@ protected:
} else {
clearError();
}
-
- cancelPermanentGuards();
}
ep->dereferenceScarceResources();
@@ -643,24 +641,22 @@ QVector<QQmlProperty> QQmlBinding::dependencies() const
if (!m_target.data())
return dependencies;
- for (const auto &guardList : { permanentGuards, activeGuards }) {
- for (QQmlJavaScriptExpressionGuard *guard = guardList.first(); guard; guard = guardList.next(guard)) {
- if (guard->signalIndex() == -1) // guard's sender is a QQmlNotifier, not a QObject*.
- continue;
+ for (QQmlJavaScriptExpressionGuard *guard = activeGuards.first(); guard; guard = activeGuards.next(guard)) {
+ if (guard->signalIndex() == -1) // guard's sender is a QQmlNotifier, not a QObject*.
+ continue;
- QObject *senderObject = guard->senderAsObject();
- if (!senderObject)
- continue;
+ QObject *senderObject = guard->senderAsObject();
+ if (!senderObject)
+ continue;
- const QMetaObject *senderMeta = senderObject->metaObject();
- if (!senderMeta)
- continue;
+ const QMetaObject *senderMeta = senderObject->metaObject();
+ if (!senderMeta)
+ continue;
- for (int i = 0; i < senderMeta->propertyCount(); i++) {
- QMetaProperty property = senderMeta->property(i);
- if (property.notifySignalIndex() == QMetaObjectPrivate::signal(senderMeta, guard->signalIndex()).methodIndex()) {
- dependencies.push_back(QQmlProperty(senderObject, QString::fromUtf8(senderObject->metaObject()->property(i).name())));
- }
+ for (int i = 0; i < senderMeta->propertyCount(); i++) {
+ QMetaProperty property = senderMeta->property(i);
+ if (property.notifySignalIndex() == QMetaObjectPrivate::signal(senderMeta, guard->signalIndex()).methodIndex()) {
+ dependencies.push_back(QQmlProperty(senderObject, QString::fromUtf8(senderObject->metaObject()->property(i).name())));
}
}
}
@@ -670,7 +666,7 @@ QVector<QQmlProperty> QQmlBinding::dependencies() const
bool QQmlBinding::hasDependencies() const
{
- return !permanentGuards.isEmpty() || !activeGuards.isEmpty() || translationsCaptured();
+ return !activeGuards.isEmpty() || translationsCaptured();
}
class QObjectPointerBinding: public QQmlNonbindingBinding
diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp
index cf6f831818..dc973630a7 100644
--- a/src/qml/qml/qqmlboundsignal.cpp
+++ b/src/qml/qml/qqmlboundsignal.cpp
@@ -209,8 +209,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 57ea685a5d..64d2a064df 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>
diff --git a/src/qml/qml/qqmldelayedcallqueue.cpp b/src/qml/qml/qqmldelayedcallqueue.cpp
index 61cb0a9065..857b5be8b8 100644
--- a/src/qml/qml/qqmldelayedcallqueue.cpp
+++ b/src/qml/qml/qqmldelayedcallqueue.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qqmldelayedcallqueue_p.h"
-#include <private/qv8engine_p.h>
#include <private/qqmlengine_p.h>
#include <private/qqmljavascriptexpression_p.h>
#include <private/qv4value_p.h>
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 0a26ed89cc..f070f16afd 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -86,19 +86,10 @@
#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 +107,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 +199,54 @@ 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
+ const char uri[] = "QtQml";
+
+ 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
+ qmlRegisterCustomType<QQmlConnections>(uri, 2, 0, "Connections", new QQmlConnectionsParser);
+ qmlRegisterCustomType<QQmlConnections, 1>(uri, 2, 3, "Connections", new QQmlConnectionsParser); // Only available in QtQml >= 2.3
#if QT_CONFIG(qml_animation)
- qmlRegisterType<QQmlTimer>(uri, versionMajor, versionMinor,"Timer");
+ qmlRegisterType<QQmlTimer>(uri, 2, 0, "Timer");
#endif
- qmlRegisterType<QQmlInstantiator>(uri, versionMajor, (versionMinor < 1 ? 1 : versionMinor), "Instantiator"); //Only available in >=2.1
- qmlRegisterType<QQmlInstanceModel>();
-
- qmlRegisterType<QQmlLoggingCategory>(uri, versionMajor, 8, "LoggingCategory"); //Only available in >=2.8
- qmlRegisterType<QQmlLoggingCategory,1>(uri, versionMajor, 12, "LoggingCategory"); //Only available in >=2.12
-}
+ qmlRegisterType<QQmlLoggingCategory>(uri, 2, 8, "LoggingCategory"); // Only available in >= 2.8
+ qmlRegisterType<QQmlLoggingCategory, 1>(uri, 2, 12, "LoggingCategory"); // Only available in >= 2.12
-// 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");
-#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");
+#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.
+
+ const char uri[] = "QtQuick";
- // register the QtQuick2 types which are implemented in the QtQml module.
- registerQtQuick2Types("QtQuick",2,0);
+ 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, 1>(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, 1>(uri, 2, 12, "LoggingCategory");
+#if QT_CONFIG(qml_worker_script)
+ qmlRegisterType<QQuickWorkerScript>(uri, 2, 0, "WorkerScript");
+#endif
#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()
{
@@ -976,14 +951,6 @@ void QQmlEnginePrivate::init()
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);
-
QQmlData::init();
baseModulesUninitialized = false;
}
@@ -994,7 +961,6 @@ void QQmlEnginePrivate::init()
qRegisterMetaType<QQmlComponent::Status>();
qRegisterMetaType<QList<QObject*> >();
qRegisterMetaType<QList<int> >();
- qRegisterMetaType<QQmlV4Handle>();
qRegisterMetaType<QQmlBinding*>();
v8engine()->setEngine(q);
diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index 20d451d607..dab4e54cd6 100644
--- a/src/qml/qml/qqmlengine_p.h
+++ b/src/qml/qml/qqmlengine_p.h
@@ -249,9 +249,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();
diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp
index ac2629979f..6a11583f18 100644
--- a/src/qml/qml/qqmlexpression.cpp
+++ b/src/qml/qml/qqmlexpression.cpp
@@ -45,7 +45,6 @@
#include "qqmlcontext_p.h"
#include "qqmlscriptstring_p.h"
#include "qqmlbinding_p.h"
-#include <private/qv8engine_p.h>
#include <private/qqmlsourcecoordinate_p.h>
#include <QtCore/qdebug.h>
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 e2d53ab555..53caffe040 100644
--- a/src/qml/qml/qqmlglobal_p.h
+++ b/src/qml/qml/qqmlglobal_p.h
@@ -233,7 +233,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 +250,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 90c2c1fb96..9d5801bbc6 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -141,6 +141,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
@@ -789,7 +796,7 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
*typeRecursionDetected = true;
} else {
QQmlType returnType = QQmlMetaType::typeForUrl(
- qmlUrl, type, registrationType == QQmlType::CompositeSingletonType, nullptr);
+ qmlUrl, type, registrationType == QQmlType::CompositeSingletonType, errors);
if (type_return)
*type_return = returnType;
return returnType.isValid();
@@ -1256,7 +1263,7 @@ bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin, QQ
QString url;
const QStringRef absolutePath = absoluteFilePath.leftRef(absoluteFilePath.lastIndexOf(Slash) + 1);
if (absolutePath.at(0) == Colon)
- url = QLatin1String("qrc://") + absolutePath.mid(1);
+ url = QLatin1String("qrc") + absolutePath;
else
url = QUrl::fromLocalFile(absolutePath.toString()).toString();
@@ -1471,7 +1478,7 @@ bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix
QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
Q_ASSERT(!localFileOrQrc.isEmpty());
- QString dir = QQmlFile::urlToLocalFileOrQrc(resolveLocalUrl(base, importUri));
+ const QString dir = localFileOrQrc.left(localFileOrQrc.lastIndexOf(Slash) + 1);
if (!typeLoader->directoryExists(dir)) {
if (!isImplicitImport) {
QQmlError error;
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index 380163202a..9a3a5218e0 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -109,7 +109,6 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression()
}
clearActiveGuards();
- clearPermanentGuards();
clearError();
if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion.
m_scopeObject.asT2()->_s = nullptr;
@@ -118,12 +117,8 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression()
void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v)
{
activeGuards.setFlagValue(v);
- permanentGuards.setFlagValue(v);
- if (!v) {
+ if (!v)
clearActiveGuards();
- clearPermanentGuards();
- m_permanentDependenciesRegistered = false;
- }
}
void QQmlJavaScriptExpression::resetNotifyOnValueChanged()
@@ -216,10 +211,6 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b
QV4::ReturnedValue res = v4Function->call(&callData->thisObject, callData->args, callData->argc(), static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef()));
QV4::Scope scope(v4);
QV4::ScopedValue result(scope, res);
- if (v4Function->hasQmlDependencies) {
- QV4::Heap::QmlContext *qc = m_qmlScope.as<QV4::QmlContext>()->d();
- QQmlPropertyCapture::registerQmlDependencies(qc, v4, v4Function->compiledFunction);
- }
if (scope.hasException()) {
if (watcher.wasDeleted())
@@ -254,7 +245,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b
return result->asReturnedValue();
}
-void QQmlPropertyCapture::captureProperty(QQmlNotifier *n, Duration duration)
+void QQmlPropertyCapture::captureProperty(QQmlNotifier *n)
{
if (watcher->wasDeleted())
return;
@@ -274,17 +265,14 @@ void QQmlPropertyCapture::captureProperty(QQmlNotifier *n, Duration duration)
g->connect(n);
}
- if (duration == Permanently)
- expression->permanentGuards.prepend(g);
- else
- expression->activeGuards.prepend(g);
+ expression->activeGuards.prepend(g);
}
/*! \internal
\a n is in the signal index range (see QObjectPrivate::signalIndex()).
*/
-void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration duration, bool doNotify)
+void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, bool doNotify)
{
if (watcher->wasDeleted())
return;
@@ -323,61 +311,8 @@ void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration dur
g->connect(o, n, engine, doNotify);
}
- if (duration == Permanently)
- expression->permanentGuards.prepend(g);
- else
- expression->activeGuards.prepend(g);
- }
-}
-
-void QQmlPropertyCapture::registerQmlDependencies(QV4::Heap::QmlContext *context, const QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction)
-{
- // Let the caller check and avoid the function call :)
- Q_ASSERT(compiledFunction->hasQmlDependencies());
-
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->qmlEngine());
- if (!ep)
- return;
- QQmlPropertyCapture *capture = ep->propertyCapture;
- if (!capture || capture->watcher->wasDeleted())
- return;
-
- if (capture->expression->m_permanentDependenciesRegistered)
- return;
-
- capture->expression->m_permanentDependenciesRegistered = true;
-
- QV4::Heap::QQmlContextWrapper *wrapper = context->qml();
- QQmlContextData *qmlContext = wrapper->context->contextData();
-
- const quint32_le *idObjectDependency = compiledFunction->qmlIdObjectDependencyTable();
- const int idObjectDependencyCount = compiledFunction->nDependingIdObjects;
- for (int i = 0; i < idObjectDependencyCount; ++i, ++idObjectDependency) {
- Q_ASSERT(int(*idObjectDependency) < qmlContext->idValueCount);
- capture->captureProperty(&qmlContext->idValues[*idObjectDependency].bindings,
- QQmlPropertyCapture::Permanently);
- }
-
- Q_ASSERT(qmlContext->contextObject);
- const quint32_le *contextPropertyDependency = compiledFunction->qmlContextPropertiesDependencyTable();
- const int contextPropertyDependencyCount = compiledFunction->nDependingContextProperties;
- for (int i = 0; i < contextPropertyDependencyCount; ++i) {
- const int propertyIndex = *contextPropertyDependency++;
- const int notifyIndex = *contextPropertyDependency++;
- capture->captureProperty(qmlContext->contextObject, propertyIndex, notifyIndex,
- QQmlPropertyCapture::Permanently);
- }
-
- QObject *scopeObject = wrapper->scopeObject;
- const quint32_le *scopePropertyDependency = compiledFunction->qmlScopePropertiesDependencyTable();
- const int scopePropertyDependencyCount = compiledFunction->nDependingScopeProperties;
- for (int i = 0; i < scopePropertyDependencyCount; ++i) {
- const int propertyIndex = *scopePropertyDependency++;
- const int notifyIndex = *scopePropertyDependency++;
- capture->captureProperty(scopeObject, propertyIndex, notifyIndex,
- QQmlPropertyCapture::Permanently);
+ expression->activeGuards.prepend(g);
}
-
}
QQmlError QQmlJavaScriptExpression::error(QQmlEngine *engine) const
@@ -471,13 +406,6 @@ void QQmlJavaScriptExpression::clearActiveGuards()
g->Delete();
}
-void QQmlJavaScriptExpression::clearPermanentGuards()
-{
- m_permanentDependenciesRegistered = false;
- while (QQmlJavaScriptExpressionGuard *g = permanentGuards.takeFirst())
- g->Delete();
-}
-
void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *e, void **)
{
QQmlJavaScriptExpression *expression =
diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h
index de3fba0774..453c8ab8a8 100644
--- a/src/qml/qml/qqmljavascriptexpression_p.h
+++ b/src/qml/qml/qqmljavascriptexpression_p.h
@@ -144,7 +144,6 @@ public:
QQmlError error(QQmlEngine *) const;
void clearError();
void clearActiveGuards();
- void clearPermanentGuards();
QQmlDelayedError *delayedError();
static QV4::ReturnedValue evalFunction(QQmlContextData *ctxt, QObject *scope,
@@ -153,14 +152,6 @@ public:
protected:
void createQmlBinding(QQmlContextData *ctxt, QObject *scope, const QString &code, const QString &filename, quint16 line);
- void cancelPermanentGuards() const
- {
- if (m_permanentDependenciesRegistered) {
- for (QQmlJavaScriptExpressionGuard *it = permanentGuards.first(); it; it = permanentGuards.next(it))
- it->cancelNotify();
- }
- }
-
void setupFunction(QV4::ExecutionContext *qmlContext, QV4::Function *f);
void setCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
@@ -169,7 +160,6 @@ protected:
// activeGuards:flag2 - useSharedContext
QBiPointer<QObject, DeleteWatcher> m_scopeObject;
QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> activeGuards;
- QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> permanentGuards;
void setTranslationsCaptured(bool captured) { m_error.setFlagValue(captured); }
bool translationsCaptured() const { return m_error.flag(); }
@@ -186,7 +176,6 @@ private:
QQmlContextData *m_context;
QQmlJavaScriptExpression **m_prevExpression;
QQmlJavaScriptExpression *m_nextExpression;
- bool m_permanentDependenciesRegistered = false;
QV4::PersistentValue m_qmlScope;
QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit;
@@ -204,14 +193,8 @@ public:
Q_ASSERT(errorString == nullptr);
}
- enum Duration {
- OnlyOnce,
- Permanently
- };
-
- static void registerQmlDependencies(QV4::Heap::QmlContext *context, const QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction);
- void captureProperty(QQmlNotifier *, Duration duration = OnlyOnce);
- void captureProperty(QObject *, int, int, Duration duration = OnlyOnce, bool doNotify = true);
+ void captureProperty(QQmlNotifier *);
+ void captureProperty(QObject *, int, int, bool doNotify = true);
void captureTranslation() { translationCaptured = true; }
QQmlEngine *engine;
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/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index 0da96f61e4..32f281b4f2 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -278,6 +278,7 @@ void QQmlMetaType::clearTypeRegistrations()
data->idToType.clear();
data->nameToType.clear();
data->urlToType.clear();
+ data->typePropertyCaches.clear();
data->urlToNonFileImportType.clear();
data->metaObjectToType.clear();
data->uriToModule.clear();
@@ -1206,6 +1207,7 @@ void QQmlMetaType::unregisterType(int typeIndex)
removeQQmlTypePrivate(data->metaObjectToType, d);
for (auto & module : data->uriToModule)
module->remove(d);
+ data->clearPropertyCachesForMinorVersion(typeIndex);
data->types[typeIndex] = QQmlType();
}
}
@@ -1232,6 +1234,7 @@ void QQmlMetaType::freeUnusedTypesAndCaches()
for (auto &module : data->uriToModule)
module->remove(d);
+ data->clearPropertyCachesForMinorVersion(d->index);
*it = QQmlType();
} else {
++it;
diff --git a/src/qml/qml/qqmlmetatypedata.cpp b/src/qml/qml/qqmlmetatypedata.cpp
index 13c46e4911..5dc0083f54 100644
--- a/src/qml/qml/qqmlmetatypedata.cpp
+++ b/src/qml/qml/qqmlmetatypedata.cpp
@@ -77,6 +77,27 @@ void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv)
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))
@@ -97,7 +118,7 @@ QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QQmlType &type, int min
{
Q_ASSERT(type.isValid());
- if (QQmlPropertyCache *pc = type.key()->propertyCacheForMinorVersion(minorVersion))
+ if (QQmlPropertyCache *pc = propertyCacheForMinorVersion(type.index(), minorVersion))
return pc;
QVector<QQmlType> types;
@@ -118,8 +139,8 @@ QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QQmlType &type, int min
metaObject = metaObject->superClass();
}
- if (QQmlPropertyCache *pc = type.key()->propertyCacheForMinorVersion(maxMinorVersion)) {
- const_cast<QQmlTypePrivate*>(type.key())->setPropertyCacheForMinorVersion(minorVersion, pc);
+ if (QQmlPropertyCache *pc = propertyCacheForMinorVersion(type.index(), maxMinorVersion)) {
+ setPropertyCacheForMinorVersion(type.index(), minorVersion, pc);
return pc;
}
@@ -190,13 +211,13 @@ QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QQmlType &type, int min
}
#endif
- const_cast<QQmlTypePrivate*>(type.key())->setPropertyCacheForMinorVersion(minorVersion, raw);
+ setPropertyCacheForMinorVersion(type.index(), minorVersion, raw);
if (hasCopied)
raw->release();
if (minorVersion != maxMinorVersion)
- const_cast<QQmlTypePrivate*>(type.key())->setPropertyCacheForMinorVersion(maxMinorVersion, raw);
+ setPropertyCacheForMinorVersion(type.index(), maxMinorVersion, raw);
return raw;
}
diff --git a/src/qml/qml/qqmlmetatypedata_p.h b/src/qml/qml/qqmlmetatypedata_p.h
index 2c5a32be1b..c45bc16280 100644
--- a/src/qml/qml/qqmlmetatypedata_p.h
+++ b/src/qml/qml/qqmlmetatypedata_p.h
@@ -83,6 +83,7 @@ struct QQmlMetaTypeData
MetaObjects metaObjectToType;
typedef QHash<int, QQmlMetaType::StringConverter> StringConverters;
StringConverters stringConverters;
+ QVector<QHash<int, QQmlRefPointer<QQmlPropertyCache>>> typePropertyCaches;
struct VersionedUri {
VersionedUri()
@@ -112,6 +113,11 @@ struct QQmlMetaTypeData
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);
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/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 46457a8d76..a6546ae330 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -42,7 +42,6 @@
#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>
@@ -99,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);
@@ -127,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);
}
@@ -152,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)
@@ -168,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());
@@ -192,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());
}
/*!
@@ -347,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;
@@ -545,7 +518,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);
@@ -553,7 +526,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;
@@ -595,7 +568,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);
}
@@ -626,7 +599,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);
@@ -652,7 +625,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)
@@ -663,7 +636,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()) {
@@ -698,7 +671,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);
}
}
@@ -875,7 +848,7 @@ void QQmlPropertyData::markAsOverrideOf(QQmlPropertyData *predecessor)
setOverrideIndexIsProperty(!predecessor->isFunction());
setOverrideIndex(predecessor->coreIndex());
- predecessor->_flags.isOverridden = true;
+ predecessor->m_flags.isOverridden = true;
}
QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int argc, const QList<QByteArray> &names)
diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h
index 4f47e5d351..72692ee522 100644
--- a/src/qml/qml/qqmlpropertycache_p.h
+++ b/src/qml/qml/qqmlpropertycache_p.h
@@ -89,19 +89,19 @@ public:
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>());
@@ -187,9 +187,9 @@ private:
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);
@@ -243,8 +243,6 @@ private:
QByteArray _checksum;
};
-typedef QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCachePtr;
-
inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) const
{
if (p && Q_UNLIKELY(p->notFullyResolved()))
diff --git a/src/qml/qml/qqmlpropertydata_p.h b/src/qml/qml/qqmlpropertydata_p.h
index a292bcc769..dec696226e 100644
--- a/src/qml/qml/qqmlpropertydata_p.h
+++ b/src/qml/qml/qqmlpropertydata_p.h
@@ -51,11 +51,12 @@
// We mean it.
//
-#include <private/qqmlpropertyrawdata_p.h>
+#include <private/qobject_p.h>
QT_BEGIN_NAMESPACE
-class QQmlPropertyData : public QQmlPropertyRawData
+class QQmlPropertyCacheMethodArguments;
+class QQmlPropertyData
{
public:
enum WriteFlag {
@@ -65,10 +66,193 @@ public:
};
Q_DECLARE_FLAGS(WriteFlags, WriteFlag)
- inline QQmlPropertyData();
- inline QQmlPropertyData(const QQmlPropertyRawData &);
+ typedef QObjectPrivate::StaticMetaCallFunction StaticMetaCallFunction;
- inline bool operator==(const QQmlPropertyRawData &);
+ 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 &);
@@ -128,27 +312,32 @@ private:
friend class QQmlPropertyCache;
void lazyLoad(const QMetaProperty &);
void lazyLoad(const QMetaMethod &);
- bool notFullyResolved() const { return _flags.notFullyResolved; }
-};
+ bool notFullyResolved() const { return m_flags.notFullyResolved; }
-QQmlPropertyData::QQmlPropertyData()
-{
- setCoreIndex(-1);
- setPropType(0);
- setNotifyIndex(-1);
- setOverrideIndex(-1);
- setRevision(0);
- setMetaObjectOffset(-1);
- setArguments(nullptr);
- trySetStaticMetaCallFunction(nullptr, 0);
-}
+ Flags m_flags;
+ qint16 m_coreIndex = -1;
+ quint16 m_propType = 0;
-QQmlPropertyData::QQmlPropertyData(const QQmlPropertyRawData &d)
-{
- *(static_cast<QQmlPropertyRawData *>(this)) = d;
-}
+ // 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;
-bool QQmlPropertyData::operator==(const QQmlPropertyRawData &other)
+ 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() &&
@@ -157,6 +346,64 @@ bool QQmlPropertyData::operator==(const QQmlPropertyRawData &other)
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
diff --git a/src/qml/qml/qqmlpropertyrawdata_p.h b/src/qml/qml/qqmlpropertyrawdata_p.h
deleted file mode 100644
index 833c0f6ad0..0000000000
--- a/src/qml/qml/qqmlpropertyrawdata_p.h
+++ /dev/null
@@ -1,341 +0,0 @@
-/****************************************************************************
-**
-** 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 QQMLPROPERTYRAWDATA_P_H
-#define QQMLPROPERTYRAWDATA_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
-
-// 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 QQmlPropertyCacheMethodArguments;
-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
-
-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;
- }
-}
-
-QT_END_NAMESPACE
-
-#endif // QQMLPROPERTYRAWDATA_P_H
diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp
index efe190cbcf..88eedec061 100644
--- a/src/qml/qml/qqmltype.cpp
+++ b/src/qml/qml/qqmltype.cpp
@@ -118,7 +118,8 @@ QJSValue QQmlType::SingletonInstanceInfo::scriptApi(QQmlEngine *e) const
QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type)
: regType(type), iid(nullptr), typeId(0), listId(0), revision(0),
containsRevisionedAttributes(false), baseMetaObject(nullptr),
- index(-1), isSetup(false), isEnumSetup(false), haveSuperType(false)
+ index(-1), isSetup(false), isEnumFromCacheSetup(false), isEnumFromBaseSetup(false),
+ haveSuperType(false)
{
switch (type) {
case QQmlType::CppType:
@@ -176,8 +177,8 @@ QQmlTypePrivate::~QQmlTypePrivate()
}
QQmlType::QQmlType() = default;
-QQmlType::QQmlType(const QQmlType &other) = default;
-QQmlType::QQmlType(QQmlType &&other) = 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) {}
@@ -347,19 +348,24 @@ void QQmlTypePrivate::init() const
void QQmlTypePrivate::initEnums(const QQmlPropertyCache *cache) const
{
- if (isEnumSetup) return;
+ if ((isEnumFromBaseSetup || !baseMetaObject)
+ && (isEnumFromCacheSetup || !cache)) {
+ return;
+ }
init();
QMutexLocker lock(QQmlMetaType::typeRegistrationLock());
- if (isEnumSetup) return;
- if (cache)
+ if (!isEnumFromCacheSetup && cache) {
insertEnumsFromPropertyCache(cache);
- if (baseMetaObject) // could be singleton type without metaobject
- insertEnums(baseMetaObject);
+ isEnumFromCacheSetup = true;
+ }
- isEnumSetup = true;
+ if (!isEnumFromBaseSetup && baseMetaObject) { // could be singleton type without metaobject
+ insertEnums(baseMetaObject);
+ isEnumFromBaseSetup = true;
+ }
}
void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const
@@ -503,25 +509,6 @@ void QQmlTypePrivate::setName(const QString &uri, const QString &element)
name = uri.isEmpty() ? element : (uri + QLatin1Char('/') + element);
}
-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) {
diff --git a/src/qml/qml/qqmltype_p.h b/src/qml/qml/qqmltype_p.h
index 1d1345a0cb..0e59b1be06 100644
--- a/src/qml/qml/qqmltype_p.h
+++ b/src/qml/qml/qqmltype_p.h
@@ -89,7 +89,6 @@ public:
}
bool isValid() const { return !d.isNull(); }
- const QQmlTypePrivate *key() const { return d.data(); }
QByteArray typeName() const;
QString qmlTypeName() const;
diff --git a/src/qml/qml/qqmltype_p_p.h b/src/qml/qml/qqmltype_p_p.h
index 380139f385..b317aff740 100644
--- a/src/qml/qml/qqmltype_p_p.h
+++ b/src/qml/qml/qqmltype_p_p.h
@@ -120,24 +120,14 @@ public:
int index;
mutable volatile bool isSetup:1;
- mutable volatile bool isEnumSetup: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);
-
void setName(const QString &uri, const QString &element);
private:
diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp
index 3ec828ea2d..86513b5ff8 100644
--- a/src/qml/qml/qqmltypewrapper.cpp
+++ b/src/qml/qml/qqmltypewrapper.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qqmltypewrapper_p.h"
-#include <private/qv8engine_p.h>
#include <private/qqmlengine_p.h>
#include <private/qqmlcontext_p.h>
@@ -48,6 +47,8 @@
#include <private/qv4functionobject_p.h>
#include <private/qv4objectproto_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4identifiertable_p.h>
+#include <private/qv4lookup_p.h>
QT_BEGIN_NAMESPACE
@@ -170,6 +171,7 @@ static ReturnedValue throwLowercaseEnumError(QV4::ExecutionEngine *v4, String *n
ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
+ // Keep this code in sync with ::virtualResolveLookupGetter
Q_ASSERT(m->as<QQmlTypeWrapper>());
if (!id.isString())
@@ -426,6 +428,64 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const
return QV4::Encode(QQmlMetaObject::canConvert(theirType, myQmlType));
}
+ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
+{
+ // Keep this code in sync with ::virtualGet
+ PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
+ if (!id.isString())
+ return Object::virtualResolveLookupGetter(object, engine, lookup);
+ Scope scope(engine);
+
+ const QQmlTypeWrapper *This = static_cast<const QQmlTypeWrapper *>(object);
+ ScopedString name(scope, id.asStringOrSymbol());
+ QQmlContextData *qmlContext = engine->callingQmlContext();
+
+ Scoped<QQmlTypeWrapper> w(scope, static_cast<const QQmlTypeWrapper *>(This));
+ QQmlType type = w->d()->type();
+
+ 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) {
+ ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton)));
+ QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext);
+ if (property) {
+ lookup->qobjectLookup.ic = This->internalClass();
+ lookup->qobjectLookup.staticQObject = static_cast<Heap::QObjectWrapper *>(val->heapObject());
+ lookup->qobjectLookup.propertyCache = ddata->propertyCache;
+ lookup->qobjectLookup.propertyCache->addref();
+ lookup->qobjectLookup.propertyData = property;
+ lookup->getter = QV4::QObjectWrapper::lookupGetter;
+ return lookup->getter(lookup, engine, *This);
+ }
+ // Fall through to base implementation
+ }
+ // Fall through to base implementation
+ }
+ // Fall through to base implementation
+ }
+ // Fall through to base implementation
+ }
+ // Fall through to base implementation
+ }
+ return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
+}
+
+bool QQmlTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value)
+{
+ return Object::virtualResolveLookupSetter(object, engine, lookup, value);
+}
+
void Heap::QQmlScopedEnumWrapper::destroy()
{
QQmlType::derefHandle(typePrivate);
diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h
index bc615e0f6c..c797a4ac10 100644
--- a/src/qml/qml/qqmltypewrapper_p.h
+++ b/src/qml/qml/qqmltypewrapper_p.h
@@ -111,6 +111,9 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object
static ReturnedValue create(ExecutionEngine *, QObject *, const QQmlRefPointer<QQmlTypeNameCache> &, const QQmlImportRef *,
Heap::QQmlTypeWrapper::TypeNameMode = Heap::QQmlTypeWrapper::IncludeEnums);
+ static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
+ static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
+
protected:
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp
index 21505754bb..f08005fd20 100644
--- a/src/qml/qml/qqmlvaluetype.cpp
+++ b/src/qml/qml/qqmlvaluetype.cpp
@@ -80,19 +80,26 @@ QQmlValueTypeFactoryImpl::~QQmlValueTypeFactoryImpl()
bool QQmlValueTypeFactoryImpl::isValueType(int idx)
{
- if (idx >= (int)QVariant::UserType) {
- return (valueType(idx) != nullptr);
- } else if (idx >= 0
- && idx != QVariant::StringList
- && idx != QMetaType::QObjectStar
- && idx != QMetaType::VoidStar
- && idx != QMetaType::Nullptr
- && idx != QMetaType::QVariant
- && idx != QMetaType::QLocale) {
+ if (idx >= QMetaType::User)
+ return valueType(idx) != nullptr;
+
+ if (idx < 0)
+ return false;
+
+ // Qt internal types
+ switch (idx) {
+ case QMetaType::QStringList:
+ case QMetaType::QObjectStar:
+ case QMetaType::VoidStar:
+ case QMetaType::Nullptr:
+ case QMetaType::QVariant:
+ case QMetaType::QLocale:
+ case QMetaType::QImage: // scarce type, keep as QVariant
+ case QMetaType::QPixmap: // scarce type, keep as QVariant
+ return false;
+ default:
return true;
}
-
- return false;
}
const QMetaObject *QQmlValueTypeFactoryImpl::metaObjectForMetaType(int t)
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index 9ce1c82f09..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>
@@ -51,6 +51,8 @@
#include <private/qv4stackframe_p.h>
#include <private/qv4objectiterator_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4identifiertable_p.h>
+#include <private/qv4lookup_p.h>
#include <QtCore/qloggingcategory.h>
QT_BEGIN_NAMESPACE
@@ -372,6 +374,117 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, con
return Encode(b->engine()->newString(result));
}
+Q_ALWAYS_INLINE static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
+ Heap::QQmlValueTypeWrapper *valueTypeWrapper,
+ QQmlPropertyData *property)
+{
+ if (property->isFunction()) {
+ // calling a Q_INVOKABLE function of a value type
+ return QV4::QObjectMethod::create(engine->rootContext(), valueTypeWrapper, property->coreIndex());
+ }
+
+#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
+ if (property->propType() == metatype) { \
+ cpptype v; \
+ void *args[] = { &v, nullptr }; \
+ metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr), \
+ QMetaObject::ReadProperty, index, args); \
+ return QV4::Encode(constructor(v)); \
+ }
+
+ const QMetaObject *metaObject = valueTypeWrapper->propertyCache()->metaObject();
+
+ int index = property->coreIndex();
+ QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index);
+
+ // These four types are the most common used by the value type wrappers
+ VALUE_TYPE_LOAD(QMetaType::QReal, qreal, qreal);
+ VALUE_TYPE_LOAD(QMetaType::Int || property->isEnum(), int, int);
+ VALUE_TYPE_LOAD(QMetaType::Int, int, int);
+ VALUE_TYPE_LOAD(QMetaType::QString, QString, engine->newString);
+ VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool);
+
+ QVariant v;
+ void *args[] = { nullptr, nullptr };
+ if (property->propType() == QMetaType::QVariant) {
+ args[0] = &v;
+ } else {
+ v = QVariant(property->propType(), static_cast<void *>(nullptr));
+ args[0] = v.data();
+ }
+ metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr), QMetaObject::ReadProperty,
+ index, args);
+ return engine->fromVariant(v);
+#undef VALUE_TYPE_LOAD
+}
+
+ReturnedValue QQmlValueTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine,
+ Lookup *lookup)
+{
+ PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->
+ runtimeStrings[lookup->nameIndex]);
+ if (!id.isString())
+ return Object::virtualResolveLookupGetter(object, engine, lookup);
+
+ const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(object);
+ QV4::ExecutionEngine *v4 = r->engine();
+ Scope scope(v4);
+ ScopedString name(scope, id.asStringOrSymbol());
+
+ // Note: readReferenceValue() can change the reference->type.
+ if (const QQmlValueTypeReference *reference = r->as<QQmlValueTypeReference>()) {
+ if (!reference->readReferenceValue())
+ return Value::undefinedValue().asReturnedValue();
+ }
+
+ QQmlPropertyData *result = r->d()->propertyCache()->property(name.getPointer(), nullptr, nullptr);
+ if (!result)
+ return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
+
+ lookup->qgadgetLookup.ic = r->internalClass();
+ lookup->qgadgetLookup.propertyCache = r->d()->propertyCache();
+ lookup->qgadgetLookup.propertyCache->addref();
+ lookup->qgadgetLookup.propertyData = result;
+ lookup->getter = QQmlValueTypeWrapper::lookupGetter;
+ return lookup->getter(lookup, engine, *object);
+}
+
+ReturnedValue QQmlValueTypeWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object)
+{
+ const auto revertLookup = [lookup, engine, &object]() {
+ lookup->qgadgetLookup.propertyCache->release();
+ lookup->qgadgetLookup.propertyCache = nullptr;
+ lookup->getter = Lookup::getterGeneric;
+ return Lookup::getterGeneric(lookup, engine, object);
+ };
+
+ // we can safely cast to a QV4::Object here. If object is something else,
+ // the internal class won't match
+ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
+ if (!o || o->internalClass != lookup->qgadgetLookup.ic)
+ return revertLookup();
+
+ Heap::QQmlValueTypeWrapper *valueTypeWrapper =
+ const_cast<Heap::QQmlValueTypeWrapper*>(static_cast<const Heap::QQmlValueTypeWrapper *>(o));
+ if (valueTypeWrapper->propertyCache() != lookup->qgadgetLookup.propertyCache)
+ return revertLookup();
+
+ if (lookup->qgadgetLookup.ic->vtable == QQmlValueTypeReference::staticVTable()) {
+ Scope scope(engine);
+ Scoped<QQmlValueTypeReference> referenceWrapper(scope, valueTypeWrapper);
+ referenceWrapper->readReferenceValue();
+ }
+
+ QQmlPropertyData *property = lookup->qgadgetLookup.propertyData;
+ return getGadgetProperty(engine, valueTypeWrapper, property);
+}
+
+bool QQmlValueTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup,
+ const Value &value)
+{
+ return Object::virtualResolveLookupSetter(object, engine, lookup, value);
+}
+
ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<QQmlValueTypeWrapper>());
@@ -397,43 +510,7 @@ ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id,
if (hasProperty)
*hasProperty = true;
- if (result->isFunction())
- // calling a Q_INVOKABLE function of a value type
- return QV4::QObjectMethod::create(v4->rootContext(), r, result->coreIndex());
-
-#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
- if (result->propType() == metatype) { \
- cpptype v; \
- void *args[] = { &v, 0 }; \
- metaObject->d.static_metacall(reinterpret_cast<QObject*>(gadget), QMetaObject::ReadProperty, index, args); \
- return QV4::Encode(constructor(v)); \
- }
-
- const QMetaObject *metaObject = r->d()->propertyCache()->metaObject();
-
- int index = result->coreIndex();
- QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index);
-
- void *gadget = r->d()->gadgetPtr;
-
- // These four types are the most common used by the value type wrappers
- VALUE_TYPE_LOAD(QMetaType::QReal, qreal, qreal);
- VALUE_TYPE_LOAD(QMetaType::Int || result->isEnum(), int, int);
- VALUE_TYPE_LOAD(QMetaType::Int, int, int);
- VALUE_TYPE_LOAD(QMetaType::QString, QString, v4->newString);
- VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool);
-
- QVariant v;
- void *args[] = { nullptr, nullptr };
- if (result->propType() == QMetaType::QVariant) {
- args[0] = &v;
- } else {
- v = QVariant(result->propType(), static_cast<void *>(nullptr));
- args[0] = v.data();
- }
- metaObject->d.static_metacall(reinterpret_cast<QObject*>(gadget), QMetaObject::ReadProperty, index, args);
- return v4->fromVariant(v);
-#undef VALUE_TYPE_ACCESSOR
+ return getGadgetProperty(v4, r->d(), result);
}
bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h
index 8db9474132..baac129afa 100644
--- a/src/qml/qml/qqmlvaluetypewrapper_p.h
+++ b/src/qml/qml/qqmlvaluetypewrapper_p.h
@@ -112,6 +112,9 @@ public:
static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p);
static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
static ReturnedValue method_toString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
+ static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
+ static ReturnedValue lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object);
static void initProto(ExecutionEngine *v4);
};
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index 6bc469c836..5d13415513 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -594,7 +594,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);
}
diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h
index 87dc9fb0b2..2371d70f10 100644
--- a/src/qml/qml/qqmlvmemetaobject_p.h
+++ b/src/qml/qml/qqmlvmemetaobject_p.h
@@ -66,7 +66,6 @@
#include "qqmlguard_p.h"
#include "qqmlcontext_p.h"
-#include <private/qv8engine_p.h>
#include <private/qflagpointer_p.h>
#include <private/qv4object_p.h>
diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp
index 9f629f974d..c6b7f2ab3f 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))); \
@@ -2067,6 +2063,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 64dc581a56..3bc588b50e 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
@@ -436,7 +436,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);
@@ -540,7 +540,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);
diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h
index 23559618ef..f9b69cfacc 100644
--- a/src/qml/qml/v8/qv8engine_p.h
+++ b/src/qml/qml/v8/qv8engine_p.h
@@ -132,19 +132,6 @@ private:
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;
@@ -238,6 +225,4 @@ inline QV8Engine::Deletable *QV8Engine::extensionData(int index) const
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QQmlV4Handle)
-
#endif // QQMLV8ENGINE_P_H
diff --git a/src/qml/qtqmlglobal_p.h b/src/qml/qtqmlglobal_p.h
index 60988d12e6..46f0e3f409 100644
--- a/src/qml/qtqmlglobal_p.h
+++ b/src/qml/qtqmlglobal_p.h
@@ -57,7 +57,7 @@
# include <QtQml/private/qtqml-config_p.h>
#endif
-#define Q_QML_PRIVATE_API_VERSION 3
+#define Q_QML_PRIVATE_API_VERSION 4
#define Q_QML_PRIVATE_EXPORT Q_QML_EXPORT
diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp
index 572f58339f..0e57119368 100644
--- a/src/qml/types/qqmldelegatemodel.cpp
+++ b/src/qml/types/qqmldelegatemodel.cpp
@@ -1127,7 +1127,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 +1139,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
@@ -2419,17 +2419,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();
@@ -2607,18 +2605,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);
@@ -2630,7 +2628,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);
@@ -2646,7 +2644,7 @@ QQmlV4Handle QQmlDelegateModelGroup::get(int index)
o->setPrototypeOf(p);
++cacheItem->scriptRef;
- return QQmlV4Handle(o);
+ return QJSValue(v4, o->asReturnedValue());
}
bool QQmlDelegateModelGroupPrivate::parseIndex(const QV4::Value &value, int *index, Compositor::Group *group) const
@@ -3338,9 +3336,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/qml/types/qqmldelegatemodel_p.h
index 0ad8939732..2684162514 100644
--- a/src/qml/types/qqmldelegatemodel_p.h
+++ b/src/qml/types/qqmldelegatemodel_p.h
@@ -59,7 +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);
@@ -114,7 +113,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;
@@ -179,7 +178,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 +194,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/qml/types/qqmldelegatemodel_p_p.h
index 0028849828..7f10bbf370 100644
--- a/src/qml/types/qqmldelegatemodel_p_p.h
+++ b/src/qml/types/qqmldelegatemodel_p_p.h
@@ -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/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp
index 27171b9bd4..5b5bcd8464 100644
--- a/src/qml/types/qqmllistmodel.cpp
+++ b/src/qml/types/qqmllistmodel.cpp
@@ -52,6 +52,7 @@
#include <private/qv4dateobject_p.h>
#include <private/qv4objectiterator_p.h>
#include <private/qv4alloca_p.h>
+#include <private/qv4lookup_p.h>
#include <qqmlcontext.h>
#include <qqmlinfo.h>
@@ -1604,8 +1605,7 @@ ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Va
if (QQmlEngine *qmlEngine = that->engine()->qmlEngine()) {
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlEngine);
if (ep && ep->propertyCapture)
- ep->propertyCapture->captureProperty(that->object(), -1, role->index,
- QQmlPropertyCapture::OnlyOnce, false);
+ ep->propertyCapture->captureProperty(that->object(), -1, role->index, /*doNotify=*/ false);
}
const int elementIndex = that->d()->elementIndex();
@@ -1613,6 +1613,12 @@ ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Va
return that->engine()->fromVariant(value);
}
+ReturnedValue ModelObject::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
+{
+ lookup->getter = Lookup::getterFallback;
+ return lookup->getter(lookup, engine, *object);
+}
+
struct ModelObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
{
int roleNameIndex = 0;
@@ -2539,7 +2545,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());
@@ -2562,7 +2568,7 @@ QQmlV4Handle QQmlListModel::get(int index) const
}
}
- return QQmlV4Handle(result);
+ return QJSValue(engine(), result->asReturnedValue());
}
/*!
@@ -2581,10 +2587,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");
diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h
index 95b797c898..471e33aa5a 100644
--- a/src/qml/types/qqmllistmodel_p.h
+++ b/src/qml/types/qqmllistmodel_p.h
@@ -100,8 +100,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();
diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h
index ff52ee049f..2876c71de6 100644
--- a/src/qml/types/qqmllistmodel_p_p.h
+++ b/src/qml/types/qqmllistmodel_p_p.h
@@ -181,6 +181,8 @@ struct ModelObject : public QObjectWrapper
protected:
static bool virtualPut(Managed *m, PropertyKey id, const Value& value, Value *receiver);
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
+ static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
+ static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object);
static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
};
diff --git a/src/qml/types/qqmllistmodelworkeragent.cpp b/src/qml/types/qqmllistmodelworkeragent.cpp
index fe3eaa3198..f7cb08dcf4 100644
--- a/src/qml/types/qqmllistmodelworkeragent.cpp
+++ b/src/qml/types/qqmllistmodelworkeragent.cpp
@@ -114,12 +114,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/qml/types/qqmllistmodelworkeragent_p.h
index ae2d4b11e0..69d1785618 100644
--- a/src/qml/types/qqmllistmodelworkeragent_p.h
+++ b/src/qml/types/qqmllistmodelworkeragent_p.h
@@ -85,8 +85,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();
diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp
index b7b9c9ee1c..840b435d18 100644
--- a/src/qml/types/qqmlmodelsmodule.cpp
+++ b/src/qml/types/qqmlmodelsmodule.cpp
@@ -48,9 +48,43 @@
#endif
#include <private/qqmlobjectmodel_p.h>
#include <private/qqmltablemodel_p.h>
+#include <private/qqmltablemodelcolumn_p.h>
+#include <private/qqmlinstantiator_p.h>
+#include <private/qquickpackage_p.h>
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.
+ qmlRegisterType<QQmlInstantiator>("QtQml", 2, 1, "Instantiator"); // Only available in >= 2.1
+ qmlRegisterType<QQmlInstanceModel>();
+}
+
+void QQmlModelsModule::registerQuickTypes()
+{
+ // Don't add anything here. These are only for backwards compatibility.
+
+ const char uri[] = "QtQuick";
+
+ qmlRegisterType<QQmlInstantiator>(uri, 2, 1, "Instantiator");
+ qmlRegisterType<QQmlInstanceModel>();
+#if QT_CONFIG(qml_list_model)
+ qmlRegisterType<QQmlListElement>(uri, 2, 0, "ListElement");
+ qmlRegisterCustomType<QQmlListModel>(uri, 2, 0, "ListModel", new QQmlListModelParser);
+#endif
+ qmlRegisterType<QQuickPackage>(uri, 2, 0, "Package");
+#if QT_CONFIG(qml_delegate_model)
+ qmlRegisterType<QQmlDelegateModel>(uri, 2, 0, "VisualDataModel");
+ qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 0, "VisualDataGroup");
+#endif
+ qmlRegisterType<QQmlObjectModel>(uri, 2, 0, "VisualItemModel");
+}
+
+#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+
void QQmlModelsModule::defineModule()
{
const char uri[] = "QtQml.Models";
@@ -67,16 +101,23 @@ void QQmlModelsModule::defineModule()
qmlRegisterType<QQmlObjectModel,3>(uri, 2, 3, "ObjectModel");
qmlRegisterType<QItemSelectionModel>(uri, 2, 2, "ItemSelectionModel");
+
+ qmlRegisterType<QQuickPackage>(uri, 2, 14, "Package");
+ qmlRegisterType<QQmlInstantiator>(uri, 2, 14, "Instantiator");
+ qmlRegisterType<QQmlInstanceModel>();
}
void QQmlModelsModule::defineLabsModule()
{
const char uri[] = "Qt.labs.qmlmodels";
+#if QT_CONFIG(qml_delegate_model)
qmlRegisterUncreatableType<QQmlAbstractDelegateComponent>(uri, 1, 0, "AbstractDelegateComponent", QQmlAbstractDelegateComponent::tr("Cannot create instance of abstract class AbstractDelegateComponent."));
qmlRegisterType<QQmlDelegateChooser>(uri, 1, 0, "DelegateChooser");
qmlRegisterType<QQmlDelegateChoice>(uri, 1, 0, "DelegateChoice");
+#endif
qmlRegisterType<QQmlTableModel>(uri, 1, 0, "TableModel");
+ qmlRegisterType<QQmlTableModelColumn>(uri, 1, 0, "TableModelColumn");
}
QT_END_NAMESPACE
diff --git a/src/qml/types/qqmlmodelsmodule_p.h b/src/qml/types/qqmlmodelsmodule_p.h
index 939ecc1500..2bb04f1e11 100644
--- a/src/qml/types/qqmlmodelsmodule_p.h
+++ b/src/qml/types/qqmlmodelsmodule_p.h
@@ -58,6 +58,11 @@ QT_BEGIN_NAMESPACE
class Q_QML_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/qml/types/qqmlobjectmodel.cpp
index 2f4d427430..b6330b4295 100644
--- a/src/qml/types/qqmlobjectmodel.cpp
+++ b/src/qml/types/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/qml/types/qqmlobjectmodel_p.h
index 4ac4f1c65b..1284ba1780 100644
--- a/src/qml/types/qqmlobjectmodel_p.h
+++ b/src/qml/types/qqmlobjectmodel_p.h
@@ -79,7 +79,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;
@@ -119,7 +120,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_p.h b/src/qml/types/qqmltableinstancemodel_p.h
index 3dd5c4e4ce..39ec66d136 100644
--- a/src/qml/types/qqmltableinstancemodel_p.h
+++ b/src/qml/types/qqmltableinstancemodel_p.h
@@ -122,7 +122,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/qml/types/qqmltablemodel.cpp b/src/qml/types/qqmltablemodel.cpp
index 6068155f5a..4a96e7a46b 100644
--- a/src/qml/types/qqmltablemodel.cpp
+++ b/src/qml/types/qqmltablemodel.cpp
@@ -47,27 +47,26 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcTableModel, "qt.qml.tablemodel")
-static const QString lengthPropertyName = QStringLiteral("length");
-static const QString displayRoleName = QStringLiteral("display");
-
/*!
\qmltype TableModel
\instantiates QQmlTableModel
\inqmlmodule Qt.labs.qmlmodels
\brief Encapsulates a simple table model.
- \since 5.12
-
- The TableModel type stores JavaScript objects as data for a table model
- that can be used with \l TableView.
+ \since 5.14
- The following snippet shows the simplest use case for TableModel:
+ 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 data is set with either the \l rows property or by
- calling \l appendRow(). Once the first row has been added to the table, the
- columns and roles are established and will be fixed for the lifetime of the
- model.
+ 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
@@ -87,14 +86,65 @@ static const QString displayRoleName = QStringLiteral("display");
data that is set, it will be automatically converted via
\l {QVariant::canConvert()}{QVariant}.
- For convenience, TableModel provides the \c display role if it is not
- explicitly specified in any column. When a column only has one role
- declared, that role will be used used as the display role. However, when
- there is more than one role in a column, which role will be used is
- undefined. This is because JavaScript does not guarantee that properties
- within an object can be accessed according to the order in which they were
- declared. This is why \c checkable may be used as the display role for the
- first column even though \c checked is declared before it, for example.
+ \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
@@ -112,13 +162,12 @@ static const QString displayRoleName = QStringLiteral("display");
\l [QtQuickControls2]{TextField}, and so that delegate is declared
last as a fallback.
- \sa QAbstractTableModel, TableView
+ \sa TableModelColumn, TableView, QAbstractTableModel
*/
QQmlTableModel::QQmlTableModel(QObject *parent)
: QAbstractTableModel(parent)
{
- mRoleNames = QAbstractTableModel::roleNames();
}
QQmlTableModel::~QQmlTableModel()
@@ -153,31 +202,34 @@ void QQmlTableModel::setRows(const QVariant &rows)
return;
}
- QVariant firstRowAsVariant;
- QVariantList firstRow;
- if (!rowsAsVariantList.isEmpty()) {
- // There are rows to validate. If they're not valid,
- // we'll return early without changing anything.
- firstRowAsVariant = rowsAsVariantList.first();
- firstRow = firstRowAsVariant.toList();
+ if (!componentCompleted) {
+ // Store the rows until we can call doSetRows() after component completion.
+ mRows = rowsAsVariantList;
+ return;
+ }
- if (firstRowAsVariant.type() != QVariant::List) {
- qmlWarning(this) << "setRows(): each row in \"rows\" must be an array of objects";
- return;
- }
+ doSetRows(rowsAsVariantList);
+}
- if (mColumnCount > 0) {
- qCDebug(lcTableModel) << "validating" << rowsAsVariantList.size()
- << "rows against existing metadata";
-
- // This is not the first time the rows have been set; validate the new columns.
- for (int i = 0; i < rowsAsVariantList.size(); ++i) {
- // validateNewRow() expects a QVariant wrapping a QJSValue, so to
- // simplify the code, just create one here.
- const QVariant row = QVariant::fromValue(rowsAsJSValue.property(i));
- if (!validateNewRow("setRows()", row, i))
- return;
- }
+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;
}
}
@@ -191,59 +243,9 @@ void QQmlTableModel::setRows(const QVariant &rows)
mRows = rowsAsVariantList;
mRowCount = mRows.size();
- const bool isFirstTimeSet = mColumnCount == 0;
- if (isFirstTimeSet && mRowCount > 0) {
- // This is the first time the rows have been set, so establish
- // the column count and gather column metadata.
- mColumnCount = firstRow.size();
- qCDebug(lcTableModel) << "gathering metadata for" << mColumnCount << "columns from first row:";
-
- // Go through each property of each cell in the first row
- // and make a role name from it.
- int userRoleKey = Qt::UserRole;
- for (int columnIndex = 0; columnIndex < mColumnCount; ++columnIndex) {
- // We need it as a QVariantMap because we need to get
- // the name of the property, which we can't do with QJSValue's API.
- const QVariantMap column = firstRow.at(columnIndex).toMap();
- const QStringList columnPropertyNames = column.keys();
- ColumnProperties properties;
- int propertyInfoIndex = 0;
-
- qCDebug(lcTableModel).nospace() << "- column " << columnIndex << ":";
-
- for (const QString &roleName : columnPropertyNames) {
- // QML/JS supports utf8.
- const QByteArray roleNameUtf8 = roleName.toUtf8();
- if (!mRoleNames.values().contains(roleNameUtf8)) {
- // We don't already have this role name, so it's a user role.
- mRoleNames[userRoleKey] = roleName.toUtf8().constData();
- qCDebug(lcTableModel) << " - added new user role" << roleName << "with key" << userRoleKey;
- ++userRoleKey;
- } else {
- qCDebug(lcTableModel) << " - found existing role" << roleName;
- }
-
- if (properties.explicitDisplayRoleIndex == -1 && roleName == displayRoleName) {
- // The user explicitly declared a "display" role,
- // so now we don't need to make it the first role in the column for them.
- properties.explicitDisplayRoleIndex = propertyInfoIndex;
- }
-
- // Keep track of the type of property so we can use it to validate new rows later on.
- const QVariant roleValue = column.value(roleName);
- const auto propertyInfo = ColumnPropertyInfo(roleName, roleValue.type(),
- QString::fromLatin1(roleValue.typeName()));
- properties.infoForProperties.append(propertyInfo);
-
- qCDebug(lcTableModel) << " - column property" << propertyInfo.name
- << "has type" << propertyInfo.typeName;
-
- ++propertyInfoIndex;
- }
-
- mColumnProperties.append(properties);
- }
- }
+ // Gather metadata the first time rows is set.
+ if (firstTimeValidRowsHaveBeenSet && !mRows.isEmpty())
+ fetchColumnMetadata();
endResetModel();
@@ -255,6 +257,94 @@ void QQmlTableModel::setRows(const QVariant &rows)
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)
@@ -262,13 +352,13 @@ void QQmlTableModel::setRows(const QVariant &rows)
values (cells) in \a row.
\code
- model.appendRow([
- { checkable: true, checked: false },
- { amount: 1 },
- { fruitType: "Pear" },
- { fruitName: "Williams" },
- { fruitPrice: 1.50 },
- ])
+ model.appendRow({
+ checkable: true,
+ amount: 1,
+ fruitType: "Pear",
+ fruitName: "Williams",
+ fruitPrice: 1.50,
+ })
\endcode
\sa insertRow(), setRow(), removeRow()
@@ -306,7 +396,7 @@ void QQmlTableModel::clear()
\code
Component.onCompleted: {
// These two lines are equivalent.
- console.log(model.getRow(0).fruitName);
+ console.log(model.getRow(0).display);
console.log(model.rows[0].fruitName);
}
\endcode
@@ -331,13 +421,13 @@ QVariant QQmlTableModel::getRow(int rowIndex)
values (cells) in \a row.
\code
- model.insertRow(2, [
- { checkable: true, checked: false },
- { amount: 1 },
- { fruitType: "Pear" },
- { fruitName: "Williams" },
- { fruitPrice: 1.50 },
- ])
+ 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
@@ -363,13 +453,32 @@ void QQmlTableModel::doInsert(int rowIndex, const QVariant &row)
mRows.insert(rowIndex, rowAsVariant);
++mRowCount;
- qCDebug(lcTableModel).nospace() << "inserted the following row to the model at index"
- << rowIndex << ":\n" << rowAsVariant.toList();
+ 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)
@@ -496,13 +605,13 @@ void QQmlTableModel::removeRow(int rowIndex, int rows)
All columns/cells must be present in \c row, and in the correct order.
\code
- model.setRow(0, [
- { checkable: true, checked: false },
- { amount: 1 },
- { fruitType: "Pear" },
- { fruitName: "Williams" },
- { fruitPrice: 1.50 },
- ])
+ 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
@@ -529,36 +638,40 @@ void QQmlTableModel::setRow(int rowIndex, const QVariant &row)
}
}
-/*!
- \qmlproperty var TableModel::roleDataProvider
-
- This property can hold a function that will map roles to values.
-
- When assigned, it will be called each time data() is called, to enable
- extracting arbitrary values, converting the data in arbitrary ways, or even
- doing calculations. It takes 3 arguments: \c index (\l QModelIndex),
- \c role (string), and \c cellData (object), which is the complete data that
- is stored in the given cell. (If the cell contains a JS object with
- multiple named values, the entire object will be given in \c cellData.)
- The function that you define must return the value to be used; for example
- a typical delegate will display the value returned for the \c display role,
- so you can check whether that is the role and return data in a form that is
- suitable for the delegate to show:
-
- \snippet qml/tablemodel/roleDataProvider.qml 0
-*/
-QJSValue QQmlTableModel::roleDataProvider() const
+QQmlListProperty<QQmlTableModelColumn> QQmlTableModel::columns()
{
- return mRoleDataProvider;
+ return QQmlListProperty<QQmlTableModelColumn>(this, nullptr,
+ &QQmlTableModel::columns_append,
+ &QQmlTableModel::columns_count,
+ &QQmlTableModel::columns_at,
+ &QQmlTableModel::columns_clear);
}
-void QQmlTableModel::setRoleDataProvider(QJSValue roleDataProvider)
+void QQmlTableModel::columns_append(QQmlListProperty<QQmlTableModelColumn> *property,
+ QQmlTableModelColumn *value)
{
- if (roleDataProvider.strictlyEquals(mRoleDataProvider))
- return;
+ QQmlTableModel *model = static_cast<QQmlTableModel*>(property->object);
+ QQmlTableModelColumn *column = qobject_cast<QQmlTableModelColumn*>(value);
+ if (column)
+ model->mColumns.append(column);
+}
- mRoleDataProvider = roleDataProvider;
- emit roleDataProviderChanged();
+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();
}
/*!
@@ -574,14 +687,19 @@ void QQmlTableModel::setRoleDataProvider(QJSValue roleDataProvider)
TableModel {
id: model
+
+ TableModelColumn { display: "fruitType" }
+ TableModelColumn { display: "fruitPrice" }
+
rows: [
- [{ fruitType: "Apple" }, { fruitPrice: 1.50 }],
- [{ fruitType: "Orange" }, { fruitPrice: 2.50 }]
+ { 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)).fruitType +
- " costs " + model.data(model.index(r, 1)).fruitPrice.toFixed(2))
+ console.log("An " + model.data(model.index(r, 0)).display +
+ " costs " + model.data(model.index(r, 1)).display.toFixed(2))
}
}
}
@@ -658,27 +776,31 @@ QVariant QQmlTableModel::data(const QModelIndex &index, int role) const
if (column < 0 || column >= columnCount())
return QVariant();
- if (!mRoleNames.contains(role))
+ 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 QVariantList rowData = mRows.at(row).toList();
-
- if (mRoleDataProvider.isCallable()) {
- auto engine = qmlEngine(this);
- const auto args = QJSValueList() <<
- engine->toScriptValue(index) <<
- QString::fromUtf8(mRoleNames.value(role)) <<
- engine->toScriptValue(rowData.at(column));
- return const_cast<QQmlTableModel*>(this)->mRoleDataProvider.call(args).toVariant();
+ 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;
}
- // TODO: should we also allow this code to be executed if roleDataProvider doesn't
- // handle the role/column, so that it only has to handle the case where there is
- // more than one role in a column?
- const QVariantMap columnData = rowData.at(column).toMap();
- const QString propertyName = columnPropertyNameFromRole(column, role);
- const QVariant value = columnData.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();
}
/*!
@@ -691,9 +813,9 @@ QVariant QQmlTableModel::data(const QModelIndex &index, int role) const
*/
bool QQmlTableModel::setData(const QModelIndex &index, const QString &role, const QVariant &value)
{
- const int iRole = mRoleNames.key(role.toUtf8(), -1);
- if (iRole >= 0)
- return setData(index, value, iRole);
+ const int intRole = mRoleNames.key(role.toUtf8(), -1);
+ if (intRole >= 0)
+ return setData(index, value, intRole);
return false;
}
@@ -707,56 +829,92 @@ bool QQmlTableModel::setData(const QModelIndex &index, const QVariant &value, in
if (column < 0 || column >= columnCount())
return false;
- if (!mRoleNames.contains(role))
- return false;
-
- const QVariantList rowData = mRows.at(row).toList();
- const QString propertyName = columnPropertyNameFromRole(column, role);
+ const QString roleName = QString::fromUtf8(mRoleNames.value(role));
qCDebug(lcTableModel).nospace() << "setData() called with index "
- << index << ", value " << value << " and role " << propertyName;
+ << index << ", value " << value << " and role " << roleName;
// Verify that the role exists for this column.
- const ColumnPropertyInfo propertyInfo = findColumnPropertyInfo(column, propertyName);
- if (!propertyInfo.isValid()) {
- QString message;
- QDebug stream(&message);
- stream.nospace() << "setData(): no role named " << propertyName
- << " at column index " << column << ". The available roles for that column are:\n";
-
- const QVector<ColumnPropertyInfo> availableProperties = mColumnProperties.at(column).infoForProperties;
- for (auto propertyInfo : availableProperties)
- stream << " - " << propertyInfo.name << " (" << qPrintable(propertyInfo.typeName) << ")";
-
- qmlWarning(this) << message;
+ 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() != propertyInfo.type) {
- if (!value.canConvert(int(propertyInfo.type))) {
+ 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 " << propertyName
- << " cannot be converted to " << propertyInfo.typeName;
+ << " set at row " << row << " column " << column << " with role " << roleName
+ << " cannot be converted to " << roleData.typeName;
return false;
}
- if (!effectiveValue.convert(int(propertyInfo.type))) {
+ if (!effectiveValue.convert(int(roleData.type))) {
qmlWarning(this).nospace() << "setData(): failed converting value " << value
- << " set at row " << row << " column " << column << " with role " << propertyName
- << " to " << propertyInfo.typeName;
+ << " set at row " << row << " column " << column << " with role " << roleName
+ << " to " << roleData.typeName;
return false;
}
}
- QVariantMap modifiedColumn = rowData.at(column).toMap();
- modifiedColumn[propertyName] = value;
+ 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:
- QVariantList modifiedRow = rowData;
- modifiedRow[column] = modifiedColumn;
- mRows[row] = modifiedRow;
+ - 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);
@@ -770,35 +928,36 @@ QHash<int, QByteArray> QQmlTableModel::roleNames() const
return mRoleNames;
}
-QQmlTableModel::ColumnPropertyInfo::ColumnPropertyInfo()
+QQmlTableModel::ColumnRoleMetadata::ColumnRoleMetadata()
{
}
-QQmlTableModel::ColumnPropertyInfo::ColumnPropertyInfo(
- const QString &name, QVariant::Type type, const QString &typeName) :
+QQmlTableModel::ColumnRoleMetadata::ColumnRoleMetadata(
+ bool isStringRole, const QString &name, QVariant::Type type, const QString &typeName) :
+ isStringRole(isStringRole),
name(name),
type(type),
typeName(typeName)
{
}
-bool QQmlTableModel::ColumnPropertyInfo::isValid() const
+bool QQmlTableModel::ColumnRoleMetadata::isValid() const
{
return !name.isEmpty();
}
bool QQmlTableModel::validateRowType(const char *functionName, const QVariant &row) const
{
- if (row.userType() != qMetaTypeId<QJSValue>()) {
- qmlWarning(this) << functionName << ": expected \"row\" argument to be an array,"
- << " but got " << row.typeName() << " instead";
+ if (!row.canConvert<QJSValue>()) {
+ qmlWarning(this) << functionName << ": expected \"row\" argument to be a QJSValue,"
+ << " but got " << row.typeName() << " instead:\n" << row;
return false;
}
- const QVariant rowAsVariant = row.value<QJSValue>().toVariant();
- if (rowAsVariant.type() != QVariant::List) {
- qmlWarning(this) << functionName << ": expected \"row\" argument to be an array,"
- << " but got " << row.typeName() << " instead";
+ 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;
}
@@ -806,12 +965,21 @@ bool QQmlTableModel::validateRowType(const char *functionName, const QVariant &r
}
bool QQmlTableModel::validateNewRow(const char *functionName, const QVariant &row,
- int rowIndex, NewRowOperationFlag appendFlag) const
+ int rowIndex, NewRowOperationFlag operation) const
{
- if (!validateRowType(functionName, row))
+ 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 (appendFlag == OtherOperation) {
+ if (operation == OtherOperation) {
// Inserting/setting.
if (rowIndex < 0) {
qmlWarning(this) << functionName << ": \"rowIndex\" cannot be negative";
@@ -825,29 +993,48 @@ bool QQmlTableModel::validateNewRow(const char *functionName, const QVariant &ro
}
}
- const QVariant rowAsVariant = row.value<QJSValue>().toVariant();
- const QVariantList rowAsList = rowAsVariant.toList();
+ 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 int columnCount = rowAsList.size();
- if (columnCount != mColumnCount) {
+ const QVariantMap rowAsMap = rowAsVariant.toMap();
+ const int columnCount = rowAsMap.size();
+ if (columnCount < mColumnCount) {
qmlWarning(this) << functionName << ": expected " << mColumnCount
- << " columns, but got " << columnCount;
+ << " columns, but only got " << columnCount;
return false;
}
- // Verify that the row's columns and their roles match the name and type of existing data.
- // This iterates across the columns in the row. For example:
- // [
- // { checkable: true, checked: false }, // columnIndex == 0
- // { amount: 1 }, // columnIndex == 1
- // { fruitType: "Orange" }, // etc.
- // { fruitName: "Navel" },
- // { fruitPrice: 2.50 }
- // ],
- for (int columnIndex = 0; columnIndex < mColumnCount; ++columnIndex) {
- const QVariantMap column = rowAsList.at(columnIndex).toMap();
- if (!validateColumnPropertyTypes(functionName, column, columnIndex))
- 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;
@@ -869,82 +1056,4 @@ bool QQmlTableModel::validateRowIndex(const char *functionName, const char *argu
return true;
}
-bool QQmlTableModel::validateColumnPropertyTypes(const char *functionName,
- const QVariantMap &column, int columnIndex) const
-{
- // Actual
- const QVariantList columnProperties = column.values();
- const QStringList propertyNames = column.keys();
- // Expected
- const QVector<ColumnPropertyInfo> properties = mColumnProperties.at(columnIndex).infoForProperties;
-
- // This iterates across the properties in the column. For example:
- // 0 1 2
- // { foo: "A", bar: 1, baz: true },
- for (int propertyIndex = 0; propertyIndex < properties.size(); ++propertyIndex) {
- const QString propertyName = propertyNames.at(propertyIndex);
- const QVariant propertyValue = columnProperties.at(propertyIndex);
- const ColumnPropertyInfo expectedPropertyFormat = properties.at(propertyIndex);
-
- if (!validateColumnPropertyType(functionName, propertyName,
- propertyValue, expectedPropertyFormat, columnIndex)) {
- return false;
- }
- }
-
- return true;
-}
-
-bool QQmlTableModel::validateColumnPropertyType(const char *functionName, const QString &propertyName,
- const QVariant &propertyValue, const ColumnPropertyInfo &expectedPropertyFormat, int columnIndex) const
-{
- if (propertyName != expectedPropertyFormat.name) {
- qmlWarning(this) << functionName
- << ": expected property named " << expectedPropertyFormat.name
- << " at column index " << columnIndex
- << ", but got " << propertyName << " instead";
- return false;
- }
-
- if (propertyValue.type() != expectedPropertyFormat.type) {
- qmlWarning(this) << functionName
- << ": expected property with type " << expectedPropertyFormat.typeName
- << " at column index " << columnIndex
- << ", but got " << propertyValue.typeName() << " instead";
- return false;
- }
-
- return true;
-}
-
-QQmlTableModel::ColumnPropertyInfo QQmlTableModel::findColumnPropertyInfo(
- int columnIndex, const QString &columnPropertyName) const
-{
- // TODO: check if a hash with its string-based lookup is faster,
- // keeping in mind that we may be doing index-based lookups too.
- const QVector<ColumnPropertyInfo> properties = mColumnProperties.at(columnIndex).infoForProperties;
- for (int i = 0; i < properties.size(); ++i) {
- const ColumnPropertyInfo &info = properties.at(i);
- if (info.name == columnPropertyName)
- return info;
- }
-
- return ColumnPropertyInfo();
-}
-
-QString QQmlTableModel::columnPropertyNameFromRole(int columnIndex, int role) const
-{
- QString propertyName;
- if (role == Qt::DisplayRole && mColumnProperties.at(columnIndex).explicitDisplayRoleIndex == -1) {
- // The user is getting or setting data for the display role,
- // but didn't specify any role with the name "display" in this column.
- // So, we give them the implicit display role, aka the first property we find.
- propertyName = mColumnProperties.at(columnIndex).infoForProperties.first().name;
- } else {
- // QML/JS supports utf8.
- propertyName = QString::fromUtf8(mRoleNames.value(role));
- }
- return propertyName;
-}
-
QT_END_NAMESPACE
diff --git a/src/qml/types/qqmltablemodel_p.h b/src/qml/types/qqmltablemodel_p.h
index 33b2189fcd..a1bb97e7d4 100644
--- a/src/qml/types/qqmltablemodel_p.h
+++ b/src/qml/types/qqmltablemodel_p.h
@@ -55,17 +55,21 @@
#include <QtCore/QAbstractTableModel>
#include <QtQml/qqml.h>
#include <QtQml/private/qtqmlglobal_p.h>
+#include <QtQml/private/qqmltablemodelcolumn_p.h>
#include <QtQml/QJSValue>
+#include <QtQml/QQmlListProperty>
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlTableModel : public QAbstractTableModel
+class Q_QML_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(QJSValue roleDataProvider READ roleDataProvider WRITE setRoleDataProvider NOTIFY roleDataProviderChanged)
+ Q_PROPERTY(QQmlListProperty<QQmlTableModelColumn> columns READ columns CONSTANT FINAL)
+ Q_INTERFACES(QQmlParserStatus)
+ Q_CLASSINFO("DefaultProperty", "columns")
public:
QQmlTableModel(QObject *parent = nullptr);
@@ -82,8 +86,12 @@ public:
Q_INVOKABLE void removeRow(int rowIndex, int rows = 1);
Q_INVOKABLE void setRow(int rowIndex, const QVariant &row);
- QJSValue roleDataProvider() const;
- void setRoleDataProvider(QJSValue roleDataProvider);
+ 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;
@@ -98,57 +106,61 @@ Q_SIGNALS:
void columnCountChanged();
void rowCountChanged();
void rowsChanged();
- void roleDataProviderChanged();
private:
- class ColumnPropertyInfo
+ class ColumnRoleMetadata
{
public:
- ColumnPropertyInfo();
- ColumnPropertyInfo(const QString &name, QVariant::Type type, const QString &typeName);
+ 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 ColumnProperties
+ struct ColumnMetadata
{
- QVector<ColumnPropertyInfo> infoForProperties;
- // If there was a display role found in this column, it'll be stored here.
- // The index is into infoForProperties.
- int explicitDisplayRoleIndex = -1;
+ // 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 appendFlag = OtherOperation) const;
+ int rowIndex, NewRowOperationFlag operation = OtherOperation) const;
bool validateRowIndex(const char *functionName, const char *argumentName, int rowIndex) const;
- bool validateColumnPropertyTypes(const char *functionName, const QVariantMap &column, int columnIndex) const;
- bool validateColumnPropertyType(const char *functionName, const QString &propertyName,
- const QVariant &propertyValue, const ColumnPropertyInfo &expectedPropertyFormat, int columnIndex) const;
-
- ColumnPropertyInfo findColumnPropertyInfo(int columnIndex, const QString &columnPropertyNameFromRole) const;
- QString columnPropertyNameFromRole(int columnIndex, int role) 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<ColumnProperties> mColumnProperties;
+ QVector<ColumnMetadata> mColumnMetadata;
// key = property index (0 to number of properties across all columns)
// value = role name
QHash<int, QByteArray> mRoleNames;
- QJSValue mRoleDataProvider;
};
QT_END_NAMESPACE
diff --git a/src/qml/types/qqmltablemodelcolumn.cpp b/src/qml/types/qqmltablemodelcolumn.cpp
new file mode 100644
index 0000000000..93da0642de
--- /dev/null
+++ b/src/qml/types/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/qml/types/qqmltablemodelcolumn_p.h b/src/qml/types/qqmltablemodelcolumn_p.h
new file mode 100644
index 0000000000..41c02482c0
--- /dev/null
+++ b/src/qml/types/qqmltablemodelcolumn_p.h
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** 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 <QtQml/private/qtqmlglobal_p.h>
+#include <QtQml/qjsvalue.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QML_AUTOTEST_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/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp
index edb112276c..c081c9e7fc 100644
--- a/src/qml/types/qquickworkerscript.cpp
+++ b/src/qml/types/qquickworkerscript.cpp
@@ -39,10 +39,6 @@
#include "qtqmlglobal_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>
@@ -651,12 +647,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/qml/types/qquickworkerscript_p.h
index 1a8d2ab076..87cf2e9754 100644
--- a/src/qml/types/qquickworkerscript_p.h
+++ b/src/qml/types/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/qml/types/types.pri b/src/qml/types/types.pri
index 1765beb09e..5a56208dc4 100644
--- a/src/qml/types/types.pri
+++ b/src/qml/types/types.pri
@@ -7,7 +7,8 @@ SOURCES += \
$$PWD/qquickpackage.cpp \
$$PWD/qqmlinstantiator.cpp \
$$PWD/qqmltableinstancemodel.cpp \
- $$PWD/qqmltablemodel.cpp
+ $$PWD/qqmltablemodel.cpp \
+ $$PWD/qqmltablemodelcolumn.cpp
HEADERS += \
$$PWD/qqmlbind_p.h \
@@ -19,7 +20,8 @@ HEADERS += \
$$PWD/qqmlinstantiator_p.h \
$$PWD/qqmlinstantiator_p_p.h \
$$PWD/qqmltableinstancemodel_p.h \
- $$PWD/qqmltablemodel_p.h
+ $$PWD/qqmltablemodel_p.h \
+ $$PWD/qqmltablemodelcolumn_p.h
qtConfig(qml-worker-script) {
SOURCES += \
diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp
index d9cb6506b8..f991ae0a69 100644
--- a/src/qml/util/qqmladaptormodel.cpp
+++ b/src/qml/util/qqmladaptormodel.cpp
@@ -42,7 +42,6 @@
#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>
@@ -525,7 +524,7 @@ public:
metaObject.reset(builder.toMetaObject());
*static_cast<QMetaObject *>(this) = *metaObject;
- propertyCache = new QQmlPropertyCache(metaObject.data(), model.modelItemRevision);
+ propertyCache.adopt(new QQmlPropertyCache(metaObject.data(), model.modelItemRevision));
}
};
@@ -659,8 +658,8 @@ public:
{
VDMListDelegateDataType *dataType = const_cast<VDMListDelegateDataType *>(this);
if (!propertyCache) {
- dataType->propertyCache = new QQmlPropertyCache(
- &QQmlDMListAccessorData::staticMetaObject, model.modelItemRevision);
+ dataType->propertyCache.adopt(new QQmlPropertyCache(
+ &QQmlDMListAccessorData::staticMetaObject, model.modelItemRevision));
}
return new QQmlDMListAccessorData(
diff --git a/src/qmldebug/qqmlenginedebugclient.cpp b/src/qmldebug/qqmlenginedebugclient.cpp
index ec45ec33bc..50f8deb087 100644
--- a/src/qmldebug/qqmlenginedebugclient.cpp
+++ b/src/qmldebug/qqmlenginedebugclient.cpp
@@ -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:
diff --git a/src/qmltest/doc/src/qtquicktest-index.qdoc b/src/qmltest/doc/src/qtquicktest-index.qdoc
index 4c0124689b..a7a840f2b3 100644
--- a/src/qmltest/doc/src/qtquicktest-index.qdoc
+++ b/src/qmltest/doc/src/qtquicktest-index.qdoc
@@ -149,11 +149,13 @@
\header
\li Name
\li Purpose
+ \li Since
\row
\li \c {void applicationAvailable()}
\li Called right after the QApplication object was instantiated.
- Use this function to setup everything that is not related
- to QML directly.
+ Use this function to perform setup that does not require a
+ \l QQmlEngine instance.
+ \li Qt 5.12
\row
\li \c {void qmlEngineAvailable(QQmlEngine *)}
\li Called when the QML engine is available.
@@ -161,16 +163,23 @@
\l {QQmlEngine::addPluginPath}{plugin paths},
and \l {QQmlFileSelector::setExtraSelectors}{extra file selectors}
will have been set on the engine by this point.
+
+ This function is called once for each QML test file,
+ so any arguments are unique to that test. For example, this
+ means that each QML test file will have its own QML engine.
+
+ This function can be used to \l {Choosing the Correct Integration
+ Method Between C++ and QML}{register QML types} and
+ \l {QQmlEngine::addImportPath()}{add import paths},
+ amongst other things.
+ \li Qt 5.11
\row
\li \c {void cleanupTestCase()}
\li Called right after the test execution has finished.
Use this function to clean up before everything will start to be destructed.
+ \li Qt 5.12
\endtable
- Each function will be called once for each \c {tst_*.qml} file, so any
- arguments are unique to that test. For example, this means that each QML
- test file will have its own QML engine.
-
The following example demonstrates how the macro can be used to set context
properties on the QML engine:
diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp
index 9a73726797..9cddf61543 100644
--- a/src/qmltest/quicktest.cpp
+++ b/src/qmltest/quicktest.cpp
@@ -575,6 +575,13 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch
qmlFileSelector->setExtraSelectors(fileSelectors);
}
+ // Do this down here so that import paths, plugin paths, file selectors, etc. are available
+ // in case the user needs access to them. Do it _before_ the TestCaseCollector parses the
+ // QML files though, because it attempts to import modules, which might not be available
+ // if qmlRegisterType()/QQmlEngine::addImportPath() are called in qmlEngineAvailable().
+ if (setup)
+ maybeInvokeSetupMethod(setup, "qmlEngineAvailable(QQmlEngine*)", Q_ARG(QQmlEngine*, &engine));
+
TestCaseCollector testCaseCollector(fi, &engine);
if (!testCaseCollector.errors().isEmpty()) {
for (const QQmlError &error : testCaseCollector.errors())
@@ -606,11 +613,6 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch
view.rootContext()->setContextProperty
(QLatin1String("qtest"), QTestRootObject::instance()); // Deprecated. Use QTestRootObject from Qt.test.qtestroot instead
- // Do this down here so that import paths, plugin paths,
- // file selectors, etc. are available in case the user needs access to them.
- if (setup)
- maybeInvokeSetupMethod(setup, "qmlEngineAvailable(QQmlEngine*)", Q_ARG(QQmlEngine*, view.engine()));
-
view.setObjectName(fi.baseName());
view.setTitle(view.objectName());
QTestRootObject::instance()->init();
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/quick/doc/qtquick.qdocconf b/src/quick/doc/qtquick.qdocconf
index b1268097fc..6620650d4a 100644
--- a/src/quick/doc/qtquick.qdocconf
+++ b/src/quick/doc/qtquick.qdocconf
@@ -43,7 +43,6 @@ headerdirs += ..\
../../quickwidgets
sourcedirs += .. \
- ../../imports/testlib \
../../quickwidgets
exampledirs += ../../../examples/quick \
diff --git a/src/quick/doc/src/concepts/statesanimations/states.qdoc b/src/quick/doc/src/concepts/statesanimations/states.qdoc
index b695713091..5592ccd25d 100644
--- a/src/quick/doc/src/concepts/statesanimations/states.qdoc
+++ b/src/quick/doc/src/concepts/statesanimations/states.qdoc
@@ -120,7 +120,7 @@ interpolation behaviors are definable. The
{Animation and Transitions} article has more information about creating state
animations.
-The \l {animation/states}{States and Transitions example}
+The \l {Qt Quick Examples - Animation}{Animation} example
demonstrates how to declare a basic set of states and apply animated
transitions between them.
diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
index 4c94259139..9383c78a42 100644
--- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
+++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
@@ -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 documentation of the Qt Toolkit.
@@ -269,7 +269,7 @@ animations, process events, etc.
\endlist
The threaded renderer is currently used by default on Windows with
-opengl32.dll, Linux with non-Mesa based drivers, \macos, mobile
+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.
@@ -277,13 +277,19 @@ 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 and Linux with
+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.
+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.
+
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.
diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri
index 226cca22cb..fa2f25c793 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 \
diff --git a/src/quick/handlers/qquickdragaxis.cpp b/src/quick/handlers/qquickdragaxis.cpp
index 5efe19b2fe..88470c8a7d 100644
--- a/src/quick/handlers/qquickdragaxis.cpp
+++ b/src/quick/handlers/qquickdragaxis.cpp
@@ -39,6 +39,8 @@
#include "qquickdragaxis_p.h"
#include <limits>
+QT_BEGIN_NAMESPACE
+
QQuickDragAxis::QQuickDragAxis()
: m_minimum(-std::numeric_limits<qreal>::max())
, m_maximum(std::numeric_limits<qreal>::max())
@@ -73,3 +75,4 @@ void QQuickDragAxis::setEnabled(bool enabled)
emit enabledChanged();
}
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickdragaxis_p.h b/src/quick/handlers/qquickdragaxis_p.h
index 2c2e0a426d..a4649d5eb9 100644
--- a/src/quick/handlers/qquickdragaxis_p.h
+++ b/src/quick/handlers/qquickdragaxis_p.h
@@ -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.
@@ -51,10 +51,11 @@
// We mean it.
//
-#include <QtCore/qobject.h>
-#include <QtCore/qglobal.h>
+#include <private/qtquickglobal_p.h>
-class Q_AUTOTEST_EXPORT QQuickDragAxis : public QObject
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QQuickDragAxis : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal minimum READ minimum WRITE setMinimum NOTIFY minimumChanged)
@@ -84,4 +85,6 @@ private:
bool m_enabled;
};
+QT_END_NAMESPACE
+
#endif // QQUICKDRAGAXIS_P_H
diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp
index 48f0599284..22fe3df4d0 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;
@@ -112,14 +112,13 @@ QPointF QQuickDragHandler::targetCentroidPosition()
void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point)
{
QQuickMultiPointHandler::onGrabChanged(grabber, transition, point);
- if (grabber == this && transition == QQuickEventPoint::GrabExclusive) {
+ 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())
+ if (target() != parentItem())
m_pressTargetPos = QPointF(target()->width(), target()->height()) / 2;
} else if (m_pressTargetPos.isNull()) {
- if (target())
- m_pressTargetPos = targetCentroidPosition();
+ m_pressTargetPos = targetCentroidPosition();
}
}
}
@@ -154,7 +153,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())
@@ -170,9 +169,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 387a81eb43..748026488a 100644
--- a/src/quick/handlers/qquickdraghandler_p.h
+++ b/src/quick/handlers/qquickdraghandler_p.h
@@ -56,7 +56,7 @@
QT_BEGIN_NAMESPACE
-class Q_AUTOTEST_EXPORT QQuickDragHandler : public QQuickMultiPointHandler
+class Q_QUICK_PRIVATE_EXPORT QQuickDragHandler : public QQuickMultiPointHandler
{
Q_OBJECT
Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT)
diff --git a/src/quick/handlers/qquickhoverhandler_p.h b/src/quick/handlers/qquickhoverhandler_p.h
index 1ee2aeb7e6..d8e5fc00a1 100644
--- a/src/quick/handlers/qquickhoverhandler_p.h
+++ b/src/quick/handlers/qquickhoverhandler_p.h
@@ -58,7 +58,7 @@
QT_BEGIN_NAMESPACE
-class Q_AUTOTEST_EXPORT QQuickHoverHandler : public QQuickSinglePointHandler
+class Q_QUICK_PRIVATE_EXPORT QQuickHoverHandler : public QQuickSinglePointHandler
{
Q_OBJECT
Q_PROPERTY(bool hovered READ isHovered NOTIFY hoveredChanged)
diff --git a/src/quick/handlers/qquickmultipointhandler.cpp b/src/quick/handlers/qquickmultipointhandler.cpp
index baa68e5e53..5c10ecce75 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;
@@ -83,14 +83,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)) {
@@ -100,57 +100,60 @@ bool QQuickMultiPointHandler::wantsPointerEvent(QQuickPointerEvent *event)
const bool 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)
@@ -190,14 +193,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();
}
@@ -216,22 +226,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;
@@ -243,30 +292,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
}
@@ -331,17 +383,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 94142013cc..eeb4b13b83 100644
--- a/src/quick/handlers/qquickmultipointhandler_p.h
+++ b/src/quick/handlers/qquickmultipointhandler_p.h
@@ -58,7 +58,9 @@
QT_BEGIN_NAMESPACE
-class Q_AUTOTEST_EXPORT QQuickMultiPointHandler : public QQuickPointerDeviceHandler
+class QQuickMultiPointHandlerPrivate;
+
+class Q_QUICK_PRIVATE_EXPORT QQuickMultiPointHandler : public QQuickPointerDeviceHandler
{
Q_OBJECT
Q_PROPERTY(int minimumPointCount READ minimumPointCount WRITE setMinimumPointCount NOTIFY minimumPointCountChanged)
@@ -68,13 +70,13 @@ class Q_AUTOTEST_EXPORT QQuickMultiPointHandler : public QQuickPointerDeviceHand
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);
@@ -106,11 +110,7 @@ protected:
bool grabPoints(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 9ae2116d39..4025cd7fbf 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 1afc028758..cea794f0c8 100644
--- a/src/quick/handlers/qquickpinchhandler_p.h
+++ b/src/quick/handlers/qquickpinchhandler_p.h
@@ -59,7 +59,7 @@
QT_BEGIN_NAMESPACE
-class Q_AUTOTEST_EXPORT QQuickPinchHandler : public QQuickMultiPointHandler
+class Q_QUICK_PRIVATE_EXPORT QQuickPinchHandler : public QQuickMultiPointHandler
{
Q_OBJECT
Q_PROPERTY(qreal minimumScale READ minimumScale WRITE setMinimumScale NOTIFY minimumScaleChanged)
@@ -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_p.h b/src/quick/handlers/qquickpointerdevicehandler_p.h
index 82b24369d3..bb15142824 100644
--- a/src/quick/handlers/qquickpointerdevicehandler_p.h
+++ b/src/quick/handlers/qquickpointerdevicehandler_p.h
@@ -56,7 +56,7 @@ QT_BEGIN_NAMESPACE
class QQuickPointerDeviceHandlerPrivate;
-class Q_AUTOTEST_EXPORT QQuickPointerDeviceHandler : public QQuickPointerHandler
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerDeviceHandler : public QQuickPointerHandler
{
Q_OBJECT
Q_PROPERTY(QQuickPointerDevice::DeviceTypes acceptedDevices READ acceptedDevices WRITE setAcceptedDevices NOTIFY acceptedDevicesChanged)
diff --git a/src/quick/handlers/qquickpointerdevicehandler_p_p.h b/src/quick/handlers/qquickpointerdevicehandler_p_p.h
index 6a950590f3..03272d9f69 100644
--- a/src/quick/handlers/qquickpointerdevicehandler_p_p.h
+++ b/src/quick/handlers/qquickpointerdevicehandler_p_p.h
@@ -56,7 +56,7 @@
QT_BEGIN_NAMESPACE
-class Q_AUTOTEST_EXPORT QQuickPointerDeviceHandlerPrivate : public QQuickPointerHandlerPrivate
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerDeviceHandlerPrivate : public QQuickPointerHandlerPrivate
{
Q_DECLARE_PUBLIC(QQuickPointerDeviceHandler)
diff --git a/src/quick/handlers/qquickpointhandler_p.h b/src/quick/handlers/qquickpointhandler_p.h
index 380ce1f90f..c197cb4f20 100644
--- a/src/quick/handlers/qquickpointhandler_p.h
+++ b/src/quick/handlers/qquickpointhandler_p.h
@@ -55,7 +55,7 @@
QT_BEGIN_NAMESPACE
-class Q_AUTOTEST_EXPORT QQuickPointHandler : public QQuickSinglePointHandler
+class Q_QUICK_PRIVATE_EXPORT QQuickPointHandler : public QQuickSinglePointHandler
{
Q_OBJECT
Q_PROPERTY(QVector2D translation READ translation NOTIFY translationChanged)
diff --git a/src/quick/handlers/qquicktaphandler_p.h b/src/quick/handlers/qquicktaphandler_p.h
index 6ec5d55227..56e08590b2 100644
--- a/src/quick/handlers/qquicktaphandler_p.h
+++ b/src/quick/handlers/qquicktaphandler_p.h
@@ -58,7 +58,7 @@
QT_BEGIN_NAMESPACE
-class Q_AUTOTEST_EXPORT QQuickTapHandler : public QQuickSinglePointHandler
+class Q_QUICK_PRIVATE_EXPORT QQuickTapHandler : public QQuickSinglePointHandler
{
Q_OBJECT
Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged)
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 14443a2f2f..188e74cd89 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();
}
/*!
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 546f3011ec..58bc12a221 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>
@@ -4552,6 +4551,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/qquickclipnode.cpp b/src/quick/items/qquickclipnode.cpp
index 747e844172..d90b1f1540 100644
--- a/src/quick/items/qquickclipnode.cpp
+++ b/src/quick/items/qquickclipnode.cpp
@@ -43,6 +43,8 @@
#include <QtGui/qvector2d.h>
#include <QtCore/qmath.h>
+QT_BEGIN_NAMESPACE
+
QQuickDefaultClipNode::QQuickDefaultClipNode(const QRectF &rect)
: m_rect(rect)
, m_radius(0)
@@ -117,3 +119,4 @@ void QQuickDefaultClipNode::updateGeometry()
markDirty(DirtyGeometry);
}
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickclipnode_p.h b/src/quick/items/qquickclipnode_p.h
index ed9e1ae4d8..00914deae8 100644
--- a/src/quick/items/qquickclipnode_p.h
+++ b/src/quick/items/qquickclipnode_p.h
@@ -54,6 +54,8 @@
#include <private/qtquickglobal_p.h>
#include <QtQuick/qsgnode.h>
+QT_BEGIN_NAMESPACE
+
class Q_QUICK_PRIVATE_EXPORT QQuickDefaultClipNode : public QSGClipNode
{
public:
@@ -78,4 +80,6 @@ private:
QSGGeometry m_geometry;
};
+QT_END_NAMESPACE
+
#endif // QQUICKCLIPNODE_P_H
diff --git a/src/quick/items/qquickdrag.cpp b/src/quick/items/qquickdrag.cpp
index f60f3c1ccf..74a00435e7 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>
diff --git a/src/quick/items/qquickdrag_p.h b/src/quick/items/qquickdrag_p.h
index 6bfbae74c9..81f3fb5e5c 100644
--- a/src/quick/items/qquickdrag_p.h
+++ b/src/quick/items/qquickdrag_p.h
@@ -53,7 +53,7 @@
#include <QtQuick/qquickitem.h>
-#include <private/qv8engine_p.h>
+#include <private/qintrusivelist_p.h>
#include <private/qqmlguard_p.h>
#include <QtCore/qmimedata.h>
diff --git a/src/quick/items/qquickgenericshadereffect.cpp b/src/quick/items/qquickgenericshadereffect.cpp
index 248c2b6ec3..3e7eda28eb 100644
--- a/src/quick/items/qquickgenericshadereffect.cpp
+++ b/src/quick/items/qquickgenericshadereffect.cpp
@@ -123,8 +123,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)
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index ec6bf5a1b8..e114404524 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -5122,6 +5122,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)
@@ -8359,7 +8393,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();
}
@@ -8656,7 +8690,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_p.h b/src/quick/items/qquickitem_p.h
index 771228914b..847fd90230 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -591,6 +591,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/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp
index 3aa00340b2..bc1f787b81 100644
--- a/src/quick/items/qquickopenglshadereffect.cpp
+++ b/src/quick/items/qquickopenglshadereffect.cpp
@@ -710,8 +710,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/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp
index be8532bf64..e4480b335a 100644
--- a/src/quick/items/qquickpathview.cpp
+++ b/src/quick/items/qquickpathview.cpp
@@ -2419,10 +2419,6 @@ void QQuickPathViewPrivate::snapToIndex(int index, MovementReason reason)
return;
qreal targetOffset = std::fmod(qreal(modelCount - index), qreal(modelCount));
-
- if (offset == targetOffset)
- return;
-
moveReason = reason;
offsetAdj = 0.0;
tl.reset(moveOffset);
diff --git a/src/quick/items/qquickrectangle_p_p.h b/src/quick/items/qquickrectangle_p_p.h
index c7c5293f9b..f40e5adada 100644
--- a/src/quick/items/qquickrectangle_p_p.h
+++ b/src/quick/items/qquickrectangle_p_p.h
@@ -64,7 +64,7 @@ class QQuickRectanglePrivate : public QQuickItemPrivate
public:
QQuickRectanglePrivate() :
- color(Qt::white), gradient(0), pen(0), radius(0)
+ color(Qt::white), gradient(QJSValue::UndefinedValue), pen(0), radius(0)
{
}
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
index ac3397d2a9..1dca5bea4a 100644
--- a/src/quick/items/qquicktableview.cpp
+++ b/src/quick/items/qquicktableview.cpp
@@ -53,11 +53,10 @@
/*!
\qmltype TableView
- \instantiates QQuickTableView
\inqmlmodule QtQuick
\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.
@@ -72,8 +71,8 @@
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
@@ -90,7 +89,7 @@
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
@@ -124,8 +123,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.
@@ -139,9 +138,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.
@@ -150,10 +149,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.
@@ -167,11 +167,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:
@@ -181,6 +181,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.
@@ -190,10 +191,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.
*/
@@ -203,7 +205,7 @@
This property holds the spacing between the rows.
- The default value is 0.
+ The default value is \c 0.
*/
/*!
@@ -211,20 +213,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}
*/
@@ -233,13 +235,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}
*/
@@ -249,9 +251,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}
@@ -265,10 +267,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
@@ -290,37 +291,39 @@
/*!
\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 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.
- 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 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.
- 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
*/
/*!
@@ -329,7 +332,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.
@@ -366,9 +369,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.
@@ -706,9 +709,8 @@ void QQuickTableViewPrivate::syncLoadedTableRectFromLoadedTable()
void QQuickTableViewPrivate::forceLayout()
{
- 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
@@ -747,8 +749,7 @@ void QQuickTableViewPrivate::forceLayout()
break;
}
- if (rebuildOptions)
- scheduleRebuildTable(rebuildOptions);
+ scheduleRebuildTable(rebuildOptions);
if (polishing) {
qWarning() << "TableView::forceLayout(): Cannot do an immediate re-layout during an ongoing layout!";
@@ -1192,7 +1193,6 @@ void QQuickTableViewPrivate::relayoutTable()
void QQuickTableViewPrivate::relayoutTableItems()
{
qCDebug(lcTableViewDelegateLifecycle);
- columnRowPositionsInvalid = false;
qreal nextColumnX = loadedTableOuterRect.x();
qreal nextRowY = loadedTableOuterRect.y();
@@ -1398,6 +1398,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) {
@@ -1408,12 +1424,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) {
@@ -1453,6 +1468,7 @@ void QQuickTableViewPrivate::processRebuildTable()
}
Q_TABLEVIEW_ASSERT(rebuildState == RebuildState::Done, int(rebuildState));
+ qCDebug(lcTableViewDelegateLifecycle()) << "rebuild complete:" << q;
}
bool QQuickTableViewPrivate::moveToNextRebuildState()
@@ -1462,7 +1478,13 @@ bool QQuickTableViewPrivate::moveToNextRebuildState()
// that the current state is not yet done.
return false;
}
- rebuildState = RebuildState(int(rebuildState) + 1);
+
+ if (rebuildState == RebuildState::Begin
+ && rebuildOptions.testFlag(RebuildOption::LayoutOnly))
+ rebuildState = RebuildState::LayoutTable;
+ else
+ rebuildState = RebuildState(int(rebuildState) + 1);
+
qCDebug(lcTableViewDelegateLifecycle()) << int(rebuildState);
return true;
}
@@ -1524,26 +1546,45 @@ void QQuickTableViewPrivate::beginRebuildTable()
loadedRows.clear();
loadedTableOuterRect = QRect();
loadedTableInnerRect = QRect();
- columnRowPositionsInvalid = false;
clearEdgeSizeCache();
+ if (!model) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "no model found, leaving table empty";
+ return;
+ }
+
+ if (model->count() == 0) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "empty model found, leaving table empty";
+ return;
+ }
+
+ if (tableModel && !tableModel->delegate()) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "no delegate found, leaving table empty";
+ return;
+ }
+
if (topLeft.x() == kEdgeIndexAtEnd || topLeft.y() == kEdgeIndexAtEnd) {
- // No visible columns or rows, so nothing to load
+ qCDebug(lcTableViewDelegateLifecycle()) << "no visible rows or columns, leaving table empty";
return;
}
- loadInitialTopLeftItem(topLeft, topLeftPos);
+ // Load top-left item. After loaded, loadItemsInsideRect() will take
+ // care of filling out the rest of the table.
+ loadRequest.begin(topLeft, topLeftPos, QQmlIncubator::AsynchronousIfNested);
+ processLoadRequest();
loadAndUnloadVisibleEdges();
}
void QQuickTableViewPrivate::layoutAfterLoadingInitialTable()
{
- if (rowHeightProvider.isUndefined() || columnWidthProvider.isUndefined()) {
+ if (rebuildOptions.testFlag(RebuildOption::LayoutOnly)
+ || rowHeightProvider.isUndefined() || columnWidthProvider.isUndefined()) {
// Since we don't have both size providers, we need to calculate the
// size of each row and column based on the size of the delegate items.
// This couldn't be done while we were loading the initial rows and
// columns, since during the process, we didn't have all the items
- // available yet for the calculation. So we do it now.
+ // available yet for the calculation. So we do it now. The exception
+ // is if we specifically only requested a relayout.
relayoutTable();
}
@@ -1552,19 +1593,6 @@ void QQuickTableViewPrivate::layoutAfterLoadingInitialTable()
updateContentHeight();
}
-void QQuickTableViewPrivate::loadInitialTopLeftItem(const QPoint &cell, const QPointF &pos)
-{
- Q_TABLEVIEW_ASSERT(loadedItems.isEmpty(), "");
-
- if (tableModel && !tableModel->delegate())
- 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);
- processLoadRequest();
-}
-
void QQuickTableViewPrivate::unloadEdge(Qt::Edge edge)
{
qCDebug(lcTableViewDelegateLifecycle) << edge;
@@ -1701,16 +1729,10 @@ void QQuickTableViewPrivate::scheduleRebuildTable(RebuildOptions options) {
return;
}
- rebuildScheduled = true;
scheduledRebuildOptions |= options;
q_func()->polish();
}
-void QQuickTableViewPrivate::invalidateColumnRowPositions() {
- columnRowPositionsInvalid = true;
- q_func()->polish();
-}
-
void QQuickTableViewPrivate::updatePolish()
{
// Whenever something changes, e.g viewport moves, spacing is set to a
@@ -1743,19 +1765,12 @@ void QQuickTableViewPrivate::updatePolish()
if (loadedItems.isEmpty())
return;
- if (columnRowPositionsInvalid) {
- relayoutTable();
- updateAverageEdgeSize();
- updateContentWidth();
- updateContentHeight();
- }
-
loadAndUnloadVisibleEdges();
}
void QQuickTableViewPrivate::fixup(QQuickFlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent)
{
- if (rebuildScheduled || rebuildState != RebuildState::Done)
+ if (scheduledRebuildOptions || rebuildState != RebuildState::Done)
return;
QQuickFlickablePrivate::fixup(data, minExtent, maxExtent);
@@ -1847,18 +1862,22 @@ void QQuickTableViewPrivate::syncWithPendingChanges()
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);
}
}
@@ -2072,7 +2091,7 @@ void QQuickTableView::setRowSpacing(qreal spacing)
return;
d->cellSpacing.setHeight(spacing);
- d->invalidateColumnRowPositions();
+ d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::LayoutOnly);
emit rowSpacingChanged();
}
@@ -2090,7 +2109,7 @@ void QQuickTableView::setColumnSpacing(qreal spacing)
return;
d->cellSpacing.setWidth(spacing);
- d->invalidateColumnRowPositions();
+ d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::LayoutOnly);
emit columnSpacingChanged();
}
@@ -2242,7 +2261,7 @@ void QQuickTableView::viewportMoved(Qt::Orientations orientation)
d->scheduleRebuildTable(options);
}
- if (d->rebuildScheduled) {
+ if (d->scheduledRebuildOptions) {
// 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
diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h
index f2fef0d774..6bbfd4e2d6 100644
--- a/src/quick/items/qquicktableview_p_p.h
+++ b/src/quick/items/qquicktableview_p_p.h
@@ -188,10 +188,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)
@@ -246,10 +247,8 @@ public:
QQmlTableInstanceModel::ReusableFlag reusableFlag = QQmlTableInstanceModel::Reusable;
bool blockItemCreatedCallback = false;
- bool columnRowPositionsInvalid = false;
bool layoutWarningIssued = false;
bool polishing = false;
- bool rebuildScheduled = true;
QJSValue rowHeightProvider;
QJSValue columnWidthProvider;
@@ -342,7 +341,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();
@@ -357,7 +355,6 @@ public:
void layoutAfterLoadingInitialTable();
void scheduleRebuildTable(QQuickTableViewPrivate::RebuildOptions options);
- void invalidateColumnRowPositions();
int resolveImportVersion();
void createWrapperModel();
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index 73b151168e..6d343a91ce 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -754,6 +754,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
bool once = true;
int elideStart = 0;
int elideEnd = 0;
+ bool noBreakLastLine = multilineElide && (wrapMode == QQuickText::Wrap || wrapMode == QQuickText::WordWrap);
int eos = multilengthEos;
@@ -786,11 +787,15 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
QRectF unelidedRect;
QTextLine line = layout.createLine();
for (visibleCount = 1; ; ++visibleCount) {
+ if (noBreakLastLine && visibleCount == maxLineCount)
+ layout.engine()->option.setWrapMode(QTextOption::WrapAnywhere);
if (customLayout) {
setupCustomLineGeometry(line, naturalHeight);
} else {
setLineGeometry(line, lineWidth, naturalHeight);
}
+ if (noBreakLastLine && visibleCount == maxLineCount)
+ layout.engine()->option.setWrapMode(QTextOption::WrapMode(wrapMode));
unelidedRect = br.united(line.naturalTextRect());
@@ -1247,7 +1252,7 @@ void QQuickTextPrivate::ensureDoc()
if (!extra.isAllocated() || !extra->doc) {
Q_Q(QQuickText);
extra.value().doc = new QQuickTextDocumentWithImageResources(q);
- extra->doc->setPageSize(QSizeF(q->width(), -1));
+ extra->doc->setPageSize(QSizeF(0, 0));
extra->doc->setDocumentMargin(0);
extra->doc->setBaseUrl(q->baseUrl());
qmlobject_connect(extra->doc, QQuickTextDocumentWithImageResources, SIGNAL(imagesLoaded()),
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index d448d74b99..9d0f4a6893 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -2025,8 +2025,8 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
qCDebug(DBG_TOUCH) << event;
Q_Q(QQuickWindow);
- if (q->mouseGrabberItem())
- q->mouseGrabberItem()->ungrabMouse();
+ if (QQuickItem *grabber = q->mouseGrabberItem())
+ sendUngrabEvent(grabber, true);
cancelTouchMouseSynthesis();
// A TouchCancel event will typically not contain any points.
@@ -4892,10 +4892,18 @@ void QQuickWindow::scheduleRenderJob(QRunnable *job, RenderStage stage)
} else if (stage == AfterSwapStage) {
d->afterSwapJobs << job;
} else if (stage == NoStage) {
- if (isExposed())
+ if (d->renderControl && openglContext()
+#if QT_CONFIG(opengl)
+ && openglContext()->thread() == QThread::currentThread()
+#endif
+ ) {
+ job->run();
+ delete job;
+ } else if (isExposed()) {
d->windowManager->postJob(this, job);
- else
+ } else {
delete job;
+ }
}
d->renderJobMutex.unlock();
}
diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h
index 79e8a11aa8..53fe0a4c4b 100644
--- a/src/quick/items/qquickwindow.h
+++ b/src/quick/items/qquickwindow.h
@@ -241,6 +241,7 @@ private:
friend class QQuickWidget;
friend class QQuickRenderControl;
friend class QQuickAnimatorController;
+ friend class QQuickWidgetPrivate;
Q_DISABLE_COPY(QQuickWindow)
};
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/scenegraph/coreapi/qsgnode.h b/src/quick/scenegraph/coreapi/qsgnode.h
index 854e284c9e..cb677de030 100644
--- a/src/quick/scenegraph/coreapi/qsgnode.h
+++ b/src/quick/scenegraph/coreapi/qsgnode.h
@@ -96,7 +96,7 @@ public:
// Uppermost 8 bits are reserved for internal use.
IsVisitableNode = 0x01000000
#ifdef Q_CLANG_QDOC
- InternalReserved = 0x01000000
+ , InternalReserved = 0x01000000
#endif
};
Q_DECLARE_FLAGS(Flags, Flag)
diff --git a/src/quick/scenegraph/qsgdefaultcontext.cpp b/src/quick/scenegraph/qsgdefaultcontext.cpp
index af0589e5d3..70b74b8877 100644
--- a/src/quick/scenegraph/qsgdefaultcontext.cpp
+++ b/src/quick/scenegraph/qsgdefaultcontext.cpp
@@ -62,6 +62,8 @@
#include <private/qqmlglobal_p.h>
+#include <algorithm>
+
QT_BEGIN_NAMESPACE
namespace QSGMultisampleAntialiasing {
@@ -158,11 +160,9 @@ 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));
- QSet<QByteArray> exts = openglRenderContext->openglContext()->extensions();
- QByteArray all;
- for (const QByteArray &e : qAsConst(exts))
- all += ' ' + e;
- qCDebug(QSG_LOG_INFO, "GL_EXTENSIONS: %s", all.constData());
+ QByteArrayList exts = openglRenderContext->openglContext()->extensions().toList();
+ 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, "Debug context: %s",
format.testOption(QSurfaceFormat::DebugContext) ? "true" : "false");
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/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index a098c94670..f1a0f0c863 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -76,6 +76,18 @@
QT_BEGIN_NAMESPACE
+// override setVisble to prevent accidental offscreen window being created
+// by base class.
+class QQuickOffcreenWindowPrivate: public QQuickWindowPrivate {
+public:
+ void setVisible(bool visible) override {
+ Q_Q(QWindow);
+ // this stays always invisible
+ visibility = visible ? QWindow::Windowed : QWindow::Hidden;
+ q->visibilityChanged(visibility); // workaround for QTBUG-49054
+ }
+};
+
class QQuickWidgetRenderControl : public QQuickRenderControl
{
public:
@@ -94,7 +106,7 @@ void QQuickWidgetPrivate::init(QQmlEngine* e)
Q_Q(QQuickWidget);
renderControl = new QQuickWidgetRenderControl(q);
- offscreenWindow = new QQuickWindow(renderControl);
+ offscreenWindow = new QQuickWindow(*new QQuickOffcreenWindowPrivate(),renderControl);
offscreenWindow->setTitle(QString::fromLatin1("Offscreen"));
// Do not call create() on offscreenWindow.
@@ -1321,12 +1333,9 @@ void QQuickWidget::showEvent(QShowEvent *)
triggerUpdate();
}
}
- QWindowPrivate *offscreenPrivate = QWindowPrivate::get(d->offscreenWindow);
- if (!offscreenPrivate->visible) {
- offscreenPrivate->visible = true;
- emit d->offscreenWindow->visibleChanged(true);
- offscreenPrivate->updateVisibility();
- }
+
+ // note offscreenWindow is "QQuickOffScreenWindow" instance
+ d->offscreenWindow->setVisible(true);
if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
service->setParentWindow(d->offscreenWindow, window()->windowHandle());
}
@@ -1337,12 +1346,8 @@ void QQuickWidget::hideEvent(QHideEvent *)
Q_D(QQuickWidget);
if (!d->offscreenWindow->isPersistentSceneGraph())
d->invalidateRenderControl();
- QWindowPrivate *offscreenPrivate = QWindowPrivate::get(d->offscreenWindow);
- if (offscreenPrivate->visible) {
- offscreenPrivate->visible = false;
- emit d->offscreenWindow->visibleChanged(false);
- offscreenPrivate->updateVisibility();
- }
+ // note offscreenWindow is "QQuickOffScreenWindow" instance
+ d->offscreenWindow->setVisible(false);
if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
service->setParentWindow(d->offscreenWindow, d->offscreenWindow);
}
diff --git a/tests/auto/particles/shared/particlestestsshared.h b/tests/auto/particles/shared/particlestestsshared.h
index 5ef1d2cabb..e1fc6f4ccd 100644
--- a/tests/auto/particles/shared/particlestestsshared.h
+++ b/tests/auto/particles/shared/particlestestsshared.h
@@ -31,6 +31,8 @@
#include <QtQuick/QQuickView>
#include <QtTest>
#include <QAbstractAnimation>
+#include <QScopedPointer>
+
const qreal EPSILON = 0.0001;
bool extremelyFuzzyCompare(qreal a, qreal b, qreal e)//For cases which can have larger variances
@@ -55,17 +57,18 @@ bool myFuzzyGEQ(qreal a, qreal b)
QQuickView* createView(const QUrl &filename, int additionalWait=0)
{
- QQuickView *view = new QQuickView(0);
+ QScopedPointer<QQuickView> view(new QQuickView(nullptr));
view->setSource(filename);
if (view->status() != QQuickView::Ready)
- return 0;
+ return nullptr;
view->show();
- QTest::qWaitForWindowExposed(view);
+ if (!QTest::qWaitForWindowExposed(view.data()))
+ return nullptr;
if (additionalWait)
QTest::qWait(additionalWait);
- return view;
+ return view.take();
}
void ensureAnimTime(int requiredTime, QAbstractAnimation* anim)//With consistentTiming, who knows how long an animation really takes...
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/qqmlpreview/tst_qqmlpreview.cpp b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp
index 4c4c514832..c7f8ec1118 100644
--- a/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp
+++ b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp
@@ -319,12 +319,12 @@ void tst_QQmlPreview::zoom()
QTRY_VERIFY(m_files.contains(testFile(file)));
float baseZoomFactor = -1;
QTRY_VERIFY_WITH_TIMEOUT((baseZoomFactor = parseZoomFactor(m_process->output())) > 0, 30000);
- m_client->triggerZoom(2.0f);
- verifyZoomFactor(m_process, baseZoomFactor * 2.0f);
- m_client->triggerZoom(1.5f);
- verifyZoomFactor(m_process, baseZoomFactor * 1.5f);
- m_client->triggerZoom(0.5f);
- verifyZoomFactor(m_process, baseZoomFactor * 0.5f);
+
+ for (auto testZoomFactor : {2.0f, 1.5f, 0.5f}) {
+ m_client->triggerZoom(testZoomFactor);
+ verifyZoomFactor(m_process, baseZoomFactor * testZoomFactor);
+ }
+
m_client->triggerZoom(-1.0f);
verifyZoomFactor(m_process, baseZoomFactor);
m_process->stop();
@@ -338,7 +338,7 @@ void tst_QQmlPreview::fps()
QVERIFY(m_client);
m_client->triggerLoad(testFileUrl(file));
if (QGuiApplication::platformName() != "offscreen") {
- QTRY_VERIFY(m_frameStats.numSyncs > 10);
+ QTRY_VERIFY_WITH_TIMEOUT(m_frameStats.numSyncs > 10, 10000);
QVERIFY(m_frameStats.minSync <= m_frameStats.maxSync);
QVERIFY(m_frameStats.totalSync / m_frameStats.numSyncs >= m_frameStats.minSync - 1);
QVERIFY(m_frameStats.totalSync / m_frameStats.numSyncs <= m_frameStats.maxSync);
diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
index b75fb6b895..d9f64f3899 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/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index 01b9465f58..9c3316e39f 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -242,6 +242,9 @@ private slots:
void importModuleWithLexicallyScopedVars();
void importExportErrors();
+ void equality();
+ void aggressiveGc();
+
public:
Q_INVOKABLE QJSValue throwingCppMethod1();
Q_INVOKABLE void throwingCppMethod2();
@@ -1579,13 +1582,13 @@ 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());
QCOMPARE(val.toVariant(), var);
@@ -4590,6 +4593,34 @@ void tst_QJSEngine::stringReplace()
val = engine.evaluate("'x'.replace(/()()()()()()()()()(x)/, '$10')");
QVERIFY(val.isString());
QCOMPARE(val.toString(), QString("x"));
+
+ val = engine.evaluate("'123'.replace(/\\.0*$|(\\.\\d*[1-9])(0+)$/, '$1')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("123"));
+
+ val = engine.evaluate("'123.00'.replace(/\\.0*$|(\\.\\d*[1-9])(0+)$/, '$1')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("123"));
+
+ val = engine.evaluate("'123.0'.replace(/\\.0*$|(\\.\\d*[1-9])(0+)$/, '$1')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("123"));
+
+ val = engine.evaluate("'123.'.replace(/\\.0*$|(\\.\\d*[1-9])(0+)$/, '$1')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("123"));
+
+ val = engine.evaluate("'123.50'.replace(/\\.0*$|(\\.\\d*[1-9])(0+)$/, '$1')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("123.5"));
+
+ val = engine.evaluate("'123.05'.replace(/\\.0*$|(\\.\\d*[1-9])(0+)$/, '$1')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("123.05"));
+
+ val = engine.evaluate("'0.050'.replace(/\\.0*$|(\\.\\d*[1-9])(0+)$/, '$1')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("0.05"));
}
void tst_QJSEngine::protoChanges_QTBUG68369()
@@ -4788,6 +4819,26 @@ void tst_QJSEngine::importExportErrors()
}
}
+void tst_QJSEngine::equality()
+{
+ QJSEngine engine;
+ QJSValue ok = engine.evaluate("(0 < 0) ? 'ko' : 'ok'");
+ QVERIFY(ok.isString());
+ QCOMPARE(ok.toString(), QString("ok"));
+}
+
+void tst_QJSEngine::aggressiveGc()
+{
+ const QByteArray origAggressiveGc = qgetenv("QV4_MM_AGGRESSIVE_GC");
+ qputenv("QV4_MM_AGGRESSIVE_GC", "true");
+ {
+ QJSEngine engine; // ctor crashes if core allocation methods don't properly scope things.
+ QJSValue obj = engine.newObject();
+ QVERIFY(obj.isObject());
+ }
+ qputenv("QV4_MM_AGGRESSIVE_GC", origAggressiveGc);
+}
+
QTEST_MAIN(tst_QJSEngine)
#include "tst_qjsengine.moc"
diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
index 2b80970559..a57cd3113c 100644
--- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
+++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
@@ -1121,7 +1121,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);
}
@@ -2327,7 +2327,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);
}
}
diff --git a/tests/auto/qml/qmlcachegen/Enums.qml b/tests/auto/qml/qmlcachegen/data/Enums.qml
index 830babb73e..830babb73e 100644
--- a/tests/auto/qml/qmlcachegen/Enums.qml
+++ b/tests/auto/qml/qmlcachegen/data/Enums.qml
diff --git a/tests/auto/qml/qmlcachegen/Retain.qml b/tests/auto/qml/qmlcachegen/data/Retain.qml
index 0e69012662..0e69012662 100644
--- a/tests/auto/qml/qmlcachegen/Retain.qml
+++ b/tests/auto/qml/qmlcachegen/data/Retain.qml
diff --git a/tests/auto/qml/qmlcachegen/data/componentInItem.qml b/tests/auto/qml/qmlcachegen/data/componentInItem.qml
new file mode 100644
index 0000000000..820b9fddcd
--- /dev/null
+++ b/tests/auto/qml/qmlcachegen/data/componentInItem.qml
@@ -0,0 +1,15 @@
+import QtQuick 2.12
+
+Item {
+ Component {
+ Rectangle {
+ id: xxx
+ Text {
+ text: qsTr("&Undo")
+ }
+ Text {
+ text: qsTr("&Redo")
+ }
+ }
+ }
+}
diff --git a/tests/auto/qml/qmlcachegen/jsimport.qml b/tests/auto/qml/qmlcachegen/data/jsimport.qml
index 9c40878e60..9c40878e60 100644
--- a/tests/auto/qml/qmlcachegen/jsimport.qml
+++ b/tests/auto/qml/qmlcachegen/data/jsimport.qml
diff --git a/tests/auto/qml/qmlcachegen/jsmoduleimport.qml b/tests/auto/qml/qmlcachegen/data/jsmoduleimport.qml
index c1fad7fee2..c1fad7fee2 100644
--- a/tests/auto/qml/qmlcachegen/jsmoduleimport.qml
+++ b/tests/auto/qml/qmlcachegen/data/jsmoduleimport.qml
diff --git a/tests/auto/qml/qmlcachegen/library.js b/tests/auto/qml/qmlcachegen/data/library.js
index 51fb41dc23..51fb41dc23 100644
--- a/tests/auto/qml/qmlcachegen/library.js
+++ b/tests/auto/qml/qmlcachegen/data/library.js
diff --git a/tests/auto/qml/qmlcachegen/script.js b/tests/auto/qml/qmlcachegen/data/script.js
index fa55f9069e..fa55f9069e 100644
--- a/tests/auto/qml/qmlcachegen/script.js
+++ b/tests/auto/qml/qmlcachegen/data/script.js
diff --git a/tests/auto/qml/qmlcachegen/script.mjs b/tests/auto/qml/qmlcachegen/data/script.mjs
index 459c336125..459c336125 100644
--- a/tests/auto/qml/qmlcachegen/script.mjs
+++ b/tests/auto/qml/qmlcachegen/data/script.mjs
diff --git a/tests/auto/qml/qmlcachegen/trickypaths.qml b/tests/auto/qml/qmlcachegen/data/trickypaths.qml
index 0836808dc2..0836808dc2 100644
--- a/tests/auto/qml/qmlcachegen/trickypaths.qml
+++ b/tests/auto/qml/qmlcachegen/data/trickypaths.qml
diff --git a/tests/auto/qml/qmlcachegen/umlaut.qml b/tests/auto/qml/qmlcachegen/data/umlaut.qml
index 0836808dc2..0836808dc2 100644
--- a/tests/auto/qml/qmlcachegen/umlaut.qml
+++ b/tests/auto/qml/qmlcachegen/data/umlaut.qml
diff --git a/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-core-yc.qml b/tests/auto/qml/qmlcachegen/data/versionStyleSuffix-1.2-core-yc.qml
index 0836808dc2..0836808dc2 100644
--- a/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-core-yc.qml
+++ b/tests/auto/qml/qmlcachegen/data/versionStyleSuffix-1.2-core-yc.qml
diff --git a/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-more.qml b/tests/auto/qml/qmlcachegen/data/versionStyleSuffix-1.2-more.qml
index 0836808dc2..0836808dc2 100644
--- a/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-more.qml
+++ b/tests/auto/qml/qmlcachegen/data/versionStyleSuffix-1.2-more.qml
diff --git a/tests/auto/qml/qmlcachegen/versionchecks.qml b/tests/auto/qml/qmlcachegen/data/versionchecks.qml
index 77d67e7da4..77d67e7da4 100644
--- a/tests/auto/qml/qmlcachegen/versionchecks.qml
+++ b/tests/auto/qml/qmlcachegen/data/versionchecks.qml
diff --git a/tests/auto/qml/qmlcachegen/worker.js b/tests/auto/qml/qmlcachegen/data/worker.js
index dd2d0b843d..dd2d0b843d 100644
--- a/tests/auto/qml/qmlcachegen/worker.js
+++ b/tests/auto/qml/qmlcachegen/data/worker.js
diff --git a/tests/auto/qml/qmlcachegen/worker.qml b/tests/auto/qml/qmlcachegen/data/worker.qml
index 1f1c9d1ac7..1f1c9d1ac7 100644
--- a/tests/auto/qml/qmlcachegen/worker.qml
+++ b/tests/auto/qml/qmlcachegen/data/worker.qml
diff --git a/tests/auto/qml/qmlcachegen/qmlcachegen.pro b/tests/auto/qml/qmlcachegen/qmlcachegen.pro
index c7820ac1cd..7bd4414302 100644
--- a/tests/auto/qml/qmlcachegen/qmlcachegen.pro
+++ b/tests/auto/qml/qmlcachegen/qmlcachegen.pro
@@ -2,26 +2,34 @@ CONFIG += testcase qtquickcompiler
TARGET = tst_qmlcachegen
macos:CONFIG -= app_bundle
+include (../../shared/util.pri)
+TESTDATA = data/*
+
SOURCES += tst_qmlcachegen.cpp
-workerscripts_test.files = worker.js worker.qml
+RESOURCES += \
+ data/versionchecks.qml \
+ data/jsimport.qml \
+ data/script.js \
+ data/library.js \
+ data/Enums.qml \
+ data/componentInItem.qml \
+ data/jsmoduleimport.qml \
+ data/script.mjs
+
+workerscripts_test.files = \
+ data/worker.js \
+ data/worker.qml
workerscripts_test.prefix = /workerscripts
-RESOURCES += workerscripts_test
-
-RESOURCES += versionchecks.qml
-
-RESOURCES += trickypaths.qrc
-RESOURCES += jsimport.qml script.js library.js
-
-RESOURCES += Enums.qml
+RESOURCES += \
+ workerscripts_test \
+ trickypaths.qrc \
+ retain.qrc
# QTBUG-46375
!win32: RESOURCES += trickypaths_umlaut.qrc
-RESOURCES += jsmoduleimport.qml script.mjs
-
-RESOURCES += retain.qrc
QTQUICK_COMPILER_RETAINED_RESOURCES += retain.qrc
QT += core-private qml-private testlib
diff --git a/tests/auto/qml/qmlcachegen/retain.qrc b/tests/auto/qml/qmlcachegen/retain.qrc
index af042b25d8..e5eed9b12f 100644
--- a/tests/auto/qml/qmlcachegen/retain.qrc
+++ b/tests/auto/qml/qmlcachegen/retain.qrc
@@ -1,5 +1,5 @@
<RCC>
<qresource prefix="/">
- <file>Retain.qml</file>
+ <file alias="Retain.qml">data/Retain.qml</file>
</qresource>
</RCC>
diff --git a/tests/auto/qml/qmlcachegen/trickypaths.qrc b/tests/auto/qml/qmlcachegen/trickypaths.qrc
index 57977ccf6d..b0c3bcf209 100644
--- a/tests/auto/qml/qmlcachegen/trickypaths.qrc
+++ b/tests/auto/qml/qmlcachegen/trickypaths.qrc
@@ -1,7 +1,7 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/directory with spaces">
-<file alias="file name with spaces.qml">trickypaths.qml</file>
-<file>versionStyleSuffix-1.2-core-yc.qml</file>
-<file>versionStyleSuffix-1.2-more.qml</file>
+<file alias="file name with spaces.qml">data/trickypaths.qml</file>
+<file alias="versionStyleSuffix-1.2-core-yc.qml">data/versionStyleSuffix-1.2-core-yc.qml</file>
+<file alias="versionStyleSuffix-1.2-more.qml">data/versionStyleSuffix-1.2-more.qml</file>
</qresource>
</RCC>
diff --git a/tests/auto/qml/qmlcachegen/trickypaths_umlaut.qrc b/tests/auto/qml/qmlcachegen/trickypaths_umlaut.qrc
index 9ca889d692..17aa30473f 100644
--- a/tests/auto/qml/qmlcachegen/trickypaths_umlaut.qrc
+++ b/tests/auto/qml/qmlcachegen/trickypaths_umlaut.qrc
@@ -1,5 +1,5 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/">
-<file alias="Bäh.qml">umlaut.qml</file>
+<file alias="Bäh.qml">data/umlaut.qml</file>
</qresource>
</RCC>
diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
index 8cfa4cb6af..5462e6c8ae 100644
--- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
+++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
@@ -38,7 +38,9 @@
#include <private/qqmlcomponent_p.h>
#include <qtranslator.h>
-class tst_qmlcachegen: public QObject
+#include "../../shared/util.h"
+
+class tst_qmlcachegen: public QQmlDataTest
{
Q_OBJECT
@@ -66,6 +68,9 @@ private slots:
void enums();
void sourceFileIndices();
+
+ void reproducibleCache_data();
+ void reproducibleCache();
};
// A wrapper around QQmlComponent to ensure the temporary reference counts
@@ -353,12 +358,13 @@ static QQmlPrivate::CachedQmlUnit *temporaryModifiedCachedUnit = nullptr;
void tst_qmlcachegen::versionChecksForAheadOfTimeUnits()
{
- QVERIFY(QFile::exists(":/versionchecks.qml"));
- QCOMPARE(QFileInfo(":/versionchecks.qml").size(), 0);
+ QVERIFY(QFile::exists(":/data/versionchecks.qml"));
+ QCOMPARE(QFileInfo(":/data/versionchecks.qml").size(), 0);
Q_ASSERT(!temporaryModifiedCachedUnit);
QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError;
- const QV4::CompiledData::Unit *originalUnit = QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/versionchecks.qml"), &error);
+ const QV4::CompiledData::Unit *originalUnit = QQmlMetaType::findCachedCompilationUnit(
+ QUrl("qrc:/data/versionchecks.qml"), &error);
QVERIFY(originalUnit);
QV4::CompiledData::Unit *tweakedUnit = (QV4::CompiledData::Unit *)malloc(originalUnit->unitSize);
memcpy(reinterpret_cast<void *>(tweakedUnit), reinterpret_cast<const void *>(originalUnit), originalUnit->unitSize);
@@ -366,7 +372,7 @@ void tst_qmlcachegen::versionChecksForAheadOfTimeUnits()
temporaryModifiedCachedUnit = new QQmlPrivate::CachedQmlUnit{tweakedUnit, nullptr, nullptr};
auto testHandler = [](const QUrl &url) -> const QQmlPrivate::CachedQmlUnit * {
- if (url == QUrl("qrc:/versionchecks.qml"))
+ if (url == QUrl("qrc:/data/versionchecks.qml"))
return temporaryModifiedCachedUnit;
return nullptr;
};
@@ -374,15 +380,18 @@ void tst_qmlcachegen::versionChecksForAheadOfTimeUnits()
{
QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError;
- QVERIFY(!QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/versionchecks.qml"), &error));
+ QVERIFY(!QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/data/versionchecks.qml"), &error));
QCOMPARE(error, QQmlMetaType::CachedUnitLookupError::VersionMismatch);
}
{
QQmlEngine engine;
- QQmlComponent component(&engine, QUrl("qrc:/versionchecks.qml"));
+ QQmlComponent component(&engine, QUrl("qrc:/data/versionchecks.qml"));
QCOMPARE(component.status(), QQmlComponent::Error);
- QCOMPARE(component.errorString(), QString("qrc:/versionchecks.qml:-1 File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile\n"));
+ QCOMPARE(component.errorString(),
+ QString("qrc:/data/versionchecks.qml:-1 File was compiled ahead of time with an "
+ "incompatible version of Qt and the original file cannot be found. Please "
+ "recompile\n"));
}
Q_ASSERT(temporaryModifiedCachedUnit);
@@ -402,12 +411,12 @@ void tst_qmlcachegen::retainedResources()
void tst_qmlcachegen::workerScripts()
{
- QVERIFY(QFile::exists(":/workerscripts/worker.js"));
- QVERIFY(QFile::exists(":/workerscripts/worker.qml"));
- QCOMPARE(QFileInfo(":/workerscripts/worker.js").size(), 0);
+ QVERIFY(QFile::exists(":/workerscripts/data/worker.js"));
+ QVERIFY(QFile::exists(":/workerscripts/data/worker.qml"));
+ QCOMPARE(QFileInfo(":/workerscripts/data/worker.js").size(), 0);
QQmlEngine engine;
- CleanlyLoadingComponent component(&engine, QUrl("qrc:///workerscripts/worker.qml"));
+ CleanlyLoadingComponent component(&engine, QUrl("qrc:///workerscripts/data/worker.qml"));
QScopedPointer<QObject> obj(component.create());
QVERIFY(!obj.isNull());
QTRY_VERIFY(obj->property("success").toBool());
@@ -504,7 +513,7 @@ void tst_qmlcachegen::trickyPaths()
void tst_qmlcachegen::qrcScriptImport()
{
QQmlEngine engine;
- CleanlyLoadingComponent component(&engine, QUrl("qrc:///jsimport.qml"));
+ CleanlyLoadingComponent component(&engine, QUrl("qrc:///data/jsimport.qml"));
QScopedPointer<QObject> obj(component.create());
QVERIFY(!obj.isNull());
QTRY_COMPARE(obj->property("value").toInt(), 42);
@@ -567,14 +576,14 @@ void tst_qmlcachegen::fsScriptImport()
void tst_qmlcachegen::moduleScriptImport()
{
QQmlEngine engine;
- CleanlyLoadingComponent component(&engine, QUrl("qrc:///jsmoduleimport.qml"));
+ CleanlyLoadingComponent component(&engine, QUrl("qrc:///data/jsmoduleimport.qml"));
QVERIFY2(!component.isError(), qPrintable(component.errorString()));
QScopedPointer<QObject> obj(component.create());
QVERIFY(!obj.isNull());
QTRY_VERIFY(obj->property("ok").toBool());
- QVERIFY(QFile::exists(":/script.mjs"));
- QCOMPARE(QFileInfo(":/script.mjs").size(), 0);
+ QVERIFY(QFile::exists(":/data/script.mjs"));
+ QCOMPARE(QFileInfo(":/data/script.mjs").size(), 0);
{
auto componentPrivate = QQmlComponentPrivate::get(&component);
@@ -587,7 +596,8 @@ void tst_qmlcachegen::moduleScriptImport()
QVERIFY(unitData->flags & QV4::CompiledData::Unit::IsESModule);
QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError;
- const QV4::CompiledData::Unit *unitFromResources = QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/script.mjs"), &error);
+ const QV4::CompiledData::Unit *unitFromResources = QQmlMetaType::findCachedCompilationUnit(
+ QUrl("qrc:/data/script.mjs"), &error);
QVERIFY(unitFromResources);
QCOMPARE(unitFromResources, compilationUnit->unitData());
@@ -597,7 +607,7 @@ void tst_qmlcachegen::moduleScriptImport()
void tst_qmlcachegen::enums()
{
QQmlEngine engine;
- CleanlyLoadingComponent component(&engine, QUrl("qrc:///Enums.qml"));
+ CleanlyLoadingComponent component(&engine, QUrl("qrc:///data/Enums.qml"));
QScopedPointer<QObject> obj(component.create());
QVERIFY(!obj.isNull());
QTRY_COMPARE(obj->property("value").toInt(), 200);
@@ -605,16 +615,50 @@ void tst_qmlcachegen::enums()
void tst_qmlcachegen::sourceFileIndices()
{
- QVERIFY(QFile::exists(":/versionchecks.qml"));
- QCOMPARE(QFileInfo(":/versionchecks.qml").size(), 0);
+ QVERIFY(QFile::exists(":/data/versionchecks.qml"));
+ QCOMPARE(QFileInfo(":/data/versionchecks.qml").size(), 0);
QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError;
- const QV4::CompiledData::Unit *unitFromResources = QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/versionchecks.qml"), &error);
+ const QV4::CompiledData::Unit *unitFromResources = QQmlMetaType::findCachedCompilationUnit(
+ QUrl("qrc:/data/versionchecks.qml"), &error);
QVERIFY(unitFromResources);
QVERIFY(unitFromResources->flags & QV4::CompiledData::Unit::PendingTypeCompilation);
QCOMPARE(uint(unitFromResources->sourceFileIndex), uint(0));
}
+void tst_qmlcachegen::reproducibleCache_data()
+{
+ QTest::addColumn<QString>("filePath");
+
+ QDir dir(dataDirectory());
+ for (const QString &entry : dir.entryList(QDir::Files)) {
+ QVERIFY(entry.endsWith(".qml") || entry.endsWith(".js") || entry.endsWith(".mjs"));
+ QTest::newRow(entry.toUtf8().constData()) << dir.filePath(entry);
+ }
+}
+
+void tst_qmlcachegen::reproducibleCache()
+{
+ QFETCH(QString, filePath);
+
+ QFile file(filePath);
+ QVERIFY(file.exists());
+
+ auto generate = [](const QString &path) {
+ if (!generateCache(path))
+ return QByteArray();
+ QFile generated(path + 'c');
+ [&](){ QVERIFY(generated.open(QIODevice::ReadOnly)); }();
+ const QByteArray result = generated.readAll();
+ generated.remove();
+ return result;
+ };
+
+ const QByteArray contents1 = generate(file.fileName());
+ const QByteArray contents2 = generate(file.fileName());
+ QCOMPARE(contents1, contents2);
+}
+
QTEST_GUILESS_MAIN(tst_qmlcachegen)
#include "tst_qmlcachegen.moc"
diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
index 70a5a73e0f..e86b7bf5fe 100644
--- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
+++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
@@ -30,7 +30,6 @@
#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>
diff --git a/tests/auto/qml/qmlmin/tst_qmlmin.cpp b/tests/auto/qml/qmlmin/tst_qmlmin.cpp
index c393149f59..d1e74aecef 100644
--- a/tests/auto/qml/qmlmin/tst_qmlmin.cpp
+++ b/tests/auto/qml/qmlmin/tst_qmlmin.cpp
@@ -128,6 +128,7 @@ void tst_qmlmin::initTestCase()
invalidFiles << "tests/auto/qml/parserstress/tests/ecma_3/FunExpr/fe-001.js";
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";
}
QStringList tst_qmlmin::findFiles(const QDir &d)
diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
index 7c7c7d3bd0..98ae86d248 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>
diff --git a/tests/auto/qml/qqmlecmascript/data/signalHandlers.qml b/tests/auto/qml/qqmlecmascript/data/signalHandlers.qml
index 14326bb9e6..a924519f0f 100644
--- a/tests/auto/qml/qqmlecmascript/data/signalHandlers.qml
+++ b/tests/auto/qml/qqmlecmascript/data/signalHandlers.qml
@@ -8,6 +8,8 @@ QtObject {
signal testSignal
onTestSignal: count++
+ readonly property string scopeObjectAsString: this.toString()
+
property int funcCount: 0
function testFunction() {
funcCount++;
diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h
index 32120ee5b7..730dc7cab8 100644
--- a/tests/auto/qml/qqmlecmascript/testtypes.h
+++ b/tests/auto/qml/qqmlecmascript/testtypes.h
@@ -50,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
@@ -797,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; }
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index b4349f79ca..98f9bfe3ef 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -364,6 +364,9 @@ private slots:
void arrayAndException();
void numberToStringWithRadix();
void tailCallWithArguments();
+ void deleteSparseInIteration();
+ void saveAccumulatorBeforeToInt32();
+ void intMinDividedByMinusOne();
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
@@ -1607,7 +1610,7 @@ void tst_qqmlecmascript::aliasPropertyReset()
// test that a manual write (of undefined) to a non-resettable property fails properly
QUrl url = testFileUrl("aliasreset/aliasPropertyReset.error.1.qml");
- QString warning1 = url.toString() + QLatin1String(": Error: Cannot assign [undefined] to int");
+ QString warning1 = url.toString() + QLatin1String(":15: Error: Cannot assign [undefined] to int");
QQmlComponent e1(&engine, url);
object = e1.create();
QVERIFY(object != nullptr);
@@ -2830,35 +2833,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()));
@@ -6492,7 +6495,8 @@ void tst_qqmlecmascript::signalHandlers()
QMetaObject::invokeMethod(o.data(), "testSignalHandlerCall");
QCOMPARE(o->property("count").toInt(), 1);
- QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
+ QString scopeObjectAsString = o->property("scopeObjectAsString").toString();
+ QCOMPARE(o->property("errorString").toString(), QString("TypeError: Property 'onTestSignal' of object %1 is not a function").arg(scopeObjectAsString));
QCOMPARE(o->property("funcCount").toInt(), 0);
QMetaObject::invokeMethod(o.data(), "testSignalConnection");
@@ -8187,12 +8191,11 @@ void tst_qqmlecmascript::stackLimits()
void tst_qqmlecmascript::idsAsLValues()
{
QQmlEngine engine;
- QString err = QString(QLatin1String("%1:5 left-hand side of assignment operator is not an lvalue\n")).arg(testFileUrl("idAsLValue.qml").toString());
+ 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, "QQmlComponent: Component is not ready");
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(err));
MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
QVERIFY(!object);
- QCOMPARE(component.errorString(), err);
}
void tst_qqmlecmascript::qtbug_34792()
@@ -8933,6 +8936,54 @@ void tst_qqmlecmascript::tailCallWithArguments()
QCOMPARE(value.toInt(), 1);
}
+void tst_qqmlecmascript::deleteSparseInIteration()
+{
+ QJSEngine engine;
+ const QJSValue value = engine.evaluate(
+ "(function() {\n"
+ " var obj = { 1: null, 2: null, 4096: null };\n"
+ " var iterated = [];\n"
+ " for (var t in obj) {\n"
+ " if (t == 2)\n"
+ " delete obj[t];\n"
+ " iterated.push(t);\n"
+ " }\n"
+ " return iterated;"
+ "})()");
+ QVERIFY(value.isArray());
+ QCOMPARE(value.property("length").toInt(), 3);
+ QCOMPARE(value.property("0").toInt(), 1);
+ QCOMPARE(value.property("1").toInt(), 2);
+ QCOMPARE(value.property("2").toInt(), 4096);
+}
+
+void tst_qqmlecmascript::saveAccumulatorBeforeToInt32()
+{
+ QJSEngine engine;
+
+ // Infinite recursion produces a range error, but should not crash.
+ // Also, any GC runs in between should not trash the temporary results of "a+a".
+ const QJSValue value = engine.evaluate("function a(){a(a&a+a)}a()");
+ QVERIFY(value.isError());
+ QCOMPARE(value.toString(), QLatin1String("RangeError: Maximum call stack size exceeded."));
+}
+
+void tst_qqmlecmascript::intMinDividedByMinusOne()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.setData(QByteArray("import QtQml 2.2\n"
+ "QtObject {\n"
+ " property int intMin: -2147483648\n"
+ " property int minusOne: -1\n"
+ " property double doesNotFitInInt: intMin / minusOne\n"
+ "}"), QUrl());
+ QVERIFY(component.isReady());
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QCOMPARE(object->property("doesNotFitInInt").toUInt(), 2147483648u);
+}
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"
diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
index f58ae38264..b9cb6b70d2 100644
--- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
+++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
@@ -80,6 +80,7 @@ private slots:
void qrcUrls();
void cppSignalAndEval();
void singletonInstance();
+ void aggressiveGc();
public slots:
QObject *createAQObjectForOwnershipTest ()
@@ -1043,6 +1044,18 @@ void tst_qqmlengine::singletonInstance()
}
}
+void tst_qqmlengine::aggressiveGc()
+{
+ const QByteArray origAggressiveGc = qgetenv("QV4_MM_AGGRESSIVE_GC");
+ qputenv("QV4_MM_AGGRESSIVE_GC", "true");
+ {
+ QQmlEngine engine; // freezing should not run into infinite recursion
+ QJSValue obj = engine.newObject();
+ QVERIFY(obj.isObject());
+ }
+ qputenv("QV4_MM_AGGRESSIVE_GC", origAggressiveGc);
+}
+
QTEST_MAIN(tst_qqmlengine)
#include "tst_qqmlengine.moc"
diff --git a/tests/auto/qml/qqmlimport/data/interceptQmldir.qml b/tests/auto/qml/qqmlimport/data/interceptQmldir.qml
new file mode 100644
index 0000000000..bf485b2282
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/interceptQmldir.qml
@@ -0,0 +1,7 @@
+import QtQml 2.2
+
+import "$(INTERCEPT)" as Intercepted
+
+QtObject {
+ property QtObject view: Intercepted.View {}
+}
diff --git a/tests/auto/qml/qqmlimport/data/intercepted/View.qml b/tests/auto/qml/qqmlimport/data/intercepted/View.qml
new file mode 100644
index 0000000000..92aa3af95a
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/intercepted/View.qml
@@ -0,0 +1,5 @@
+import QtQml 2.2
+
+QtObject {
+ property int foo: 12
+}
diff --git a/tests/auto/qml/qqmlimport/data/intercepted/qmldir b/tests/auto/qml/qqmlimport/data/intercepted/qmldir
new file mode 100644
index 0000000000..ab31de73d8
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/intercepted/qmldir
@@ -0,0 +1,2 @@
+module SomeModule
+View 1.0 View.qml
diff --git a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
index 70aaa9678e..a3cb68fdcb 100644
--- a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
+++ b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
@@ -28,6 +28,7 @@
#include <QtTest/QtTest>
#include <QQmlApplicationEngine>
+#include <QQmlAbstractUrlInterceptor>
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickitem.h>
#include <private/qqmlimport_p.h>
@@ -43,6 +44,7 @@ private slots:
void uiFormatLoading();
void completeQmldirPaths_data();
void completeQmldirPaths();
+ void interceptQmldir();
void cleanup();
};
@@ -185,6 +187,32 @@ void tst_QQmlImport::completeQmldirPaths()
QCOMPARE(QQmlImports::completeQmldirPaths(uri, basePaths, majorVersion, minorVersion), expectedPaths);
}
+class QmldirUrlInterceptor : public QQmlAbstractUrlInterceptor {
+public:
+ QUrl intercept(const QUrl &url, DataType type) override
+ {
+ if (type != UrlString && !url.isEmpty() && url.isValid()) {
+ QString str = url.toString(QUrl::None);
+ return str.replace(QStringLiteral("$(INTERCEPT)"), QStringLiteral("intercepted"));
+ }
+ return url;
+ }
+};
+
+void tst_QQmlImport::interceptQmldir()
+{
+ QQmlEngine engine;
+ QmldirUrlInterceptor interceptor;
+ engine.setUrlInterceptor(&interceptor);
+
+ QQmlComponent component(&engine);
+ component.loadUrl(testFileUrl("interceptQmldir.qml"));
+ QVERIFY(component.isReady());
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+}
+
+
QTEST_MAIN(tst_QQmlImport)
#include "tst_qqmlimport.moc"
diff --git a/tests/auto/qml/qqmllanguage/data/EdgeObject.qml b/tests/auto/qml/qqmllanguage/data/EdgeObject.qml
new file mode 100644
index 0000000000..b25dc29c49
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/EdgeObject.qml
@@ -0,0 +1,20 @@
+import QtQuick 2.0
+
+Item {
+ property int edgeWidth: bg.width
+
+ Rectangle {
+ id: bg
+ }
+
+ states: [
+ State {
+ when: 1 === 1
+ PropertyChanges {
+ target: bg
+ anchors.left: parent.left
+ anchors.right: parent.right
+ }
+ }
+ ]
+}
diff --git a/tests/auto/qml/qqmllanguage/data/anchorsToParentInPropertyChagnes.qml b/tests/auto/qml/qqmllanguage/data/anchorsToParentInPropertyChagnes.qml
new file mode 100644
index 0000000000..8b1682da92
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/anchorsToParentInPropertyChagnes.qml
@@ -0,0 +1,10 @@
+import QtQuick 2.9
+
+Item {
+ width: 200
+ property int edgeWidth: edge.edgeWidth
+ EdgeObject {
+ id: edge
+ anchors.fill: parent
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/cyclicAlias.errors.txt b/tests/auto/qml/qqmllanguage/data/cyclicAlias.errors.txt
new file mode 100644
index 0000000000..46951ef69f
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/cyclicAlias.errors.txt
@@ -0,0 +1 @@
+4:5:Cyclic alias
diff --git a/tests/auto/qml/qqmllanguage/data/cyclicAlias.qml b/tests/auto/qml/qqmllanguage/data/cyclicAlias.qml
new file mode 100644
index 0000000000..23129e210d
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/cyclicAlias.qml
@@ -0,0 +1,5 @@
+import QtQml 2.2
+QtObject {
+ id: o
+ property alias t: o.t
+}
diff --git a/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt b/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt
new file mode 100644
index 0000000000..e399799fe9
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/fuzzed.1.errors.txt
@@ -0,0 +1,2 @@
+2:8:Cannot assign to non-existent property "_G"
+
diff --git a/tests/auto/qml/qqmllanguage/data/fuzzed.1.qml b/tests/auto/qml/qqmllanguage/data/fuzzed.1.qml
new file mode 100644
index 0000000000..f80b9c3a2d
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/fuzzed.1.qml
@@ -0,0 +1,2 @@
+import QtQuick 2.4
+Item { _G.G.G:G }
diff --git a/tests/auto/qml/qqmllanguage/data/fuzzed.2.errors.txt b/tests/auto/qml/qqmllanguage/data/fuzzed.2.errors.txt
new file mode 100644
index 0000000000..92ce4c649f
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/fuzzed.2.errors.txt
@@ -0,0 +1,2 @@
+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
new file mode 100644
index 0000000000..e726f6783c
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/fuzzed.2.qml
Binary files differ
diff --git a/tests/auto/qml/qqmllanguage/data/objectValueTypeProperty.errors.txt b/tests/auto/qml/qqmllanguage/data/objectValueTypeProperty.errors.txt
index cefd62f9d4..945dacf8ab 100644
--- a/tests/auto/qml/qqmllanguage/data/objectValueTypeProperty.errors.txt
+++ b/tests/auto/qml/qqmllanguage/data/objectValueTypeProperty.errors.txt
@@ -1 +1 @@
-4:18:Unexpected object assignment for property "x"
+4:18:Can not assign value of type "int" to property "x", expecting an object
diff --git a/tests/auto/qml/qqmllanguage/data/thisInQmlScope.qml b/tests/auto/qml/qqmllanguage/data/thisInQmlScope.qml
index e3c99e70e1..9ff758c33f 100644
--- a/tests/auto/qml/qqmllanguage/data/thisInQmlScope.qml
+++ b/tests/auto/qml/qqmllanguage/data/thisInQmlScope.qml
@@ -6,5 +6,18 @@ QtObject {
y = this.x;
}
property var f: g
+
Component.onCompleted: f()
+
+ property int a: 42
+ property int b: 0
+ function g_subobj(){
+ b = this.a;
+ }
+ property var f_subobj: g_subobj
+
+ property QtObject subObject: QtObject {
+ property int a: 100
+ Component.onCompleted: f_subobj()
+ }
}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 32bab2954d..1b4ff71beb 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -299,6 +299,7 @@ private slots:
void retrieveQmlTypeId();
void polymorphicFunctionLookup();
+ void anchorsToParentInPropertyChanges();
private:
QQmlEngine engine;
@@ -360,7 +361,7 @@ private:
} \
file.close(); \
} else { \
- QCOMPARE(expected, actual); \
+ QCOMPARE(actual, expected); \
} \
}
@@ -613,6 +614,10 @@ void tst_qqmllanguage::errors_data()
QTest::newRow("badCompositeRegistration.2") << "badCompositeRegistration.2.qml" << "badCompositeRegistration.2.errors.txt" << false;
QTest::newRow("assignComponentToWrongType") << "assignComponentToWrongType.qml" << "assignComponentToWrongType.errors.txt" << false;
+ QTest::newRow("cyclicAlias") << "cyclicAlias.qml" << "cyclicAlias.errors.txt" << false;
+
+ QTest::newRow("fuzzed.1") << "fuzzed.1.qml" << "fuzzed.1.errors.txt" << false;
+ QTest::newRow("fuzzed.2") << "fuzzed.2.qml" << "fuzzed.2.errors.txt" << false;
}
@@ -623,6 +628,7 @@ void tst_qqmllanguage::errors()
QFETCH(bool, create);
QQmlComponent component(&engine, testFileUrl(file));
+ QTRY_VERIFY(!component.isLoading());
QScopedPointer<QObject> object;
@@ -1450,8 +1456,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"));
@@ -1459,7 +1465,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));
}
}
@@ -1728,7 +1734,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);
@@ -5022,6 +5028,8 @@ void tst_qqmllanguage::thisInQmlScope()
QVERIFY(!o.isNull());
QCOMPARE(o->property("x"), QVariant(42));
QCOMPARE(o->property("y"), QVariant(42));
+ QCOMPARE(o->property("a"), QVariant(42));
+ QCOMPARE(o->property("b"), QVariant(42));
}
void tst_qqmllanguage::valueTypeGroupPropertiesInBehavior()
@@ -5069,6 +5077,16 @@ void tst_qqmllanguage::polymorphicFunctionLookup()
QVERIFY(o->property("ok").toBool());
}
+void tst_qqmllanguage::anchorsToParentInPropertyChanges()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("anchorsToParentInPropertyChagnes.qml"));
+ VERIFY_ERRORS(0);
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
+ QTRY_COMPARE(o->property("edgeWidth").toInt(), 200);
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"
diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
index 771f3e5c4e..2022a0d892 100644
--- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
+++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
@@ -760,11 +760,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))
diff --git a/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp b/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp
index 21b0508e4d..236a13a6f8 100644
--- a/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp
+++ b/tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp
@@ -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/qqmltablemodel/data/builtInRoles.qml b/tests/auto/qml/qqmlmetatype/data/Components/App.qml
index d9882e4dea..3792ca665e 100644
--- a/tests/auto/qml/qqmltablemodel/data/builtInRoles.qml
+++ b/tests/auto/qml/qqmlmetatype/data/Components/App.qml
@@ -26,22 +26,32 @@
**
****************************************************************************/
-import Qt.labs.qmlmodels 1.0
+import QtQml 2.0
-import "TestUtils.js" as TestUtils
+import Components 1.0
-TableModel {
- id: testModel
- objectName: "testModel"
- roleDataProvider: TestUtils.testModelRoleDataProvider
- rows: [
- [
- { name: "John", someOtherRole1: "foo" }, // column 0
- { age: 22, someOtherRole2: "foo" } // column 1
- ],
- [
- { name: "Oliver", someOtherRole1: "foo" }, // column 0
- { age: 33, someOtherRole2: "foo" } // column 1
- ]
- ]
+QtObject {
+ id: mainRect
+
+ property int appState: App.AppState.Blue
+ property string color: "blue"
+
+ enum AppState {
+ Red,
+ Green,
+ Blue
+ }
+
+ onAppStateChanged: {
+ if (appState === App.AppState.Green)
+ mainRect.color = "green"
+ else if (appState === App.AppState.Red)
+ mainRect.color = "red"
+ }
+
+ property Timer timer: Timer {
+ onTriggered: appState = App.AppState.Green
+ running: true
+ interval: 100
+ }
}
diff --git a/tests/auto/qml/qqmlmetatype/data/Components/qmldir b/tests/auto/qml/qqmlmetatype/data/Components/qmldir
new file mode 100644
index 0000000000..3f6db4ed2d
--- /dev/null
+++ b/tests/auto/qml/qqmlmetatype/data/Components/qmldir
@@ -0,0 +1,3 @@
+module Components
+
+App 1.0 App.qml
diff --git a/tests/auto/qml/qqmlmetatype/data/enumsInRecursiveImport.qml b/tests/auto/qml/qqmlmetatype/data/enumsInRecursiveImport.qml
new file mode 100644
index 0000000000..eef6abc6e5
--- /dev/null
+++ b/tests/auto/qml/qqmlmetatype/data/enumsInRecursiveImport.qml
@@ -0,0 +1,11 @@
+import QtQml 2.0
+
+import Components 1.0
+
+QtObject {
+ property App app: App {
+ appState: 0
+ }
+
+ property string color: app.color
+}
diff --git a/tests/auto/qml/qqmlmetatype/qqmlmetatype.pro b/tests/auto/qml/qqmlmetatype/qqmlmetatype.pro
index 345bc59615..109de7d212 100644
--- a/tests/auto/qml/qqmlmetatype/qqmlmetatype.pro
+++ b/tests/auto/qml/qqmlmetatype/qqmlmetatype.pro
@@ -10,4 +10,11 @@ qmlfiles.files = data/CompositeType.qml
qmlfiles.prefix = /tstqqmlmetatype
RESOURCES += qmlfiles
+qmldirresource.files = \
+ data/Components/App.qml \
+ data/Components/qmldir \
+ data/enumsInRecursiveImport.qml
+qmldirresource.prefix = /
+RESOURCES += qmldirresource
+
QT += core-private gui-private qml-private testlib
diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
index ac75eeab26..1878cccd39 100644
--- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
+++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
@@ -66,6 +66,9 @@ private slots:
void normalizeUrls();
void unregisterAttachedProperties();
void revisionedGroupedProperties();
+
+ void enumsInRecursiveImport_data();
+ void enumsInRecursiveImport();
};
class TestType : public QObject
@@ -628,6 +631,35 @@ void tst_qqmlmetatype::revisionedGroupedProperties()
}
}
+void tst_qqmlmetatype::enumsInRecursiveImport_data()
+{
+ QTest::addColumn<QString>("importPath");
+ QTest::addColumn<QUrl>("componentUrl");
+
+ QTest::addRow("data directory") << dataDirectory()
+ << testFileUrl("enumsInRecursiveImport.qml");
+
+ // The qrc case behaves differently because we failed to detect the recursion in type loading
+ // due to varying numbers of slashes after the "qrc:" in the URLs.
+ QTest::addRow("resources") << QStringLiteral("qrc:/data")
+ << QUrl("qrc:/data/enumsInRecursiveImport.qml");
+}
+
+void tst_qqmlmetatype::enumsInRecursiveImport()
+{
+ QFETCH(QString, importPath);
+ QFETCH(QUrl, componentUrl);
+
+ qmlClearTypeRegistrations();
+ QQmlEngine engine;
+ engine.addImportPath(importPath);
+ QQmlComponent c(&engine, componentUrl);
+ QVERIFY(c.isReady());
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY(!obj.isNull());
+ QTRY_COMPARE(obj->property("color").toString(), QString("green"));
+}
+
QTEST_MAIN(tst_qqmlmetatype)
#include "tst_qqmlmetatype.moc"
diff --git a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
index 6e831eacc1..a5332c8860 100644
--- a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
+++ b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
@@ -349,7 +349,7 @@ void tst_qqmlnotifier::deleteFromHandler()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("objectRenamer.qml"));
QPointer<QObject> mess = component.create();
- QObject::connect(mess, &QObject::objectNameChanged, [&]() { delete mess; });
+ QObject::connect(mess.data(), &QObject::objectNameChanged, [&]() { delete mess; });
QTRY_VERIFY(mess.isNull()); // BANG!
} else {
QProcess process;
diff --git a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp
index c2c73935c0..fead8c4ebc 100644
--- a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp
+++ b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp
@@ -105,6 +105,11 @@ public:
{
nodeStack.removeLast();
}
+
+ void throwRecursionDepthError() final
+ {
+ QFAIL("Maximum statement or expression depth exceeded");
+ }
};
}
@@ -229,8 +234,8 @@ void tst_qqmlparser::stringLiteral()
auto *literal = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(expression);
QVERIFY(literal);
QCOMPARE(literal->value, "hello string");
- QCOMPARE(literal->firstSourceLocation().begin(), 0);
- QCOMPARE(literal->lastSourceLocation().end(), code.size());
+ QCOMPARE(literal->firstSourceLocation().begin(), 0u);
+ QCOMPARE(literal->lastSourceLocation().end(), quint32(code.size()));
}
void tst_qqmlparser::noSubstitutionTemplateLiteral()
@@ -250,8 +255,8 @@ void tst_qqmlparser::noSubstitutionTemplateLiteral()
QVERIFY(literal);
QCOMPARE(literal->value, "hello template");
- QCOMPARE(literal->firstSourceLocation().begin(), 0);
- QCOMPARE(literal->lastSourceLocation().end(), code.size());
+ QCOMPARE(literal->firstSourceLocation().begin(), 0u);
+ QCOMPARE(literal->lastSourceLocation().end(), quint32(code.size()));
}
void tst_qqmlparser::templateLiteral()
@@ -270,7 +275,7 @@ void tst_qqmlparser::templateLiteral()
auto *templateLiteral = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expression);
QVERIFY(templateLiteral);
- QCOMPARE(templateLiteral->firstSourceLocation().begin(), 0);
+ QCOMPARE(templateLiteral->firstSourceLocation().begin(), 0u);
auto *e = templateLiteral->expression;
QVERIFY(e);
}
diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
index 27e06c6f67..04c61ec11b 100644
--- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
+++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
@@ -1624,7 +1624,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));
}
@@ -1641,13 +1641,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);*/
}
diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
index 02b5302a45..9a1e4667dd 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"
diff --git a/tests/auto/qml/qqmltablemodel/data/TestModel.qml b/tests/auto/qml/qqmltablemodel/data/TestModel.qml
index 7aeb5d03f4..00e1fa65a7 100644
--- a/tests/auto/qml/qqmltablemodel/data/TestModel.qml
+++ b/tests/auto/qml/qqmltablemodel/data/TestModel.qml
@@ -33,15 +33,18 @@ import "TestUtils.js" as TestUtils
TableModel {
id: testModel
objectName: "testModel"
- roleDataProvider: TestUtils.testModelRoleDataProvider
+
+ TableModelColumn { display: "name" }
+ TableModelColumn { display: "age" }
+
rows: [
- [
- { name: "John" },
- { age: 22 }
- ],
- [
- { name: "Oliver" },
- { age: 33 }
- ]
+ {
+ name: "John",
+ age: 22
+ },
+ {
+ name: "Oliver",
+ age: 33
+ }
]
}
diff --git a/tests/auto/qml/qqmltablemodel/data/common.qml b/tests/auto/qml/qqmltablemodel/data/common.qml
index aec796bd4f..2f8b0c072b 100644
--- a/tests/auto/qml/qqmltablemodel/data/common.qml
+++ b/tests/auto/qml/qqmltablemodel/data/common.qml
@@ -26,7 +26,7 @@
**
****************************************************************************/
-import QtQuick 2.12
+import QtQuick 2.13
import Qt.labs.qmlmodels 1.0
Item {
@@ -38,80 +38,101 @@ Item {
property alias tableView: tableView
function appendRow(personName, personAge) {
- testModel.appendRow([
- { name: personName },
- { age: personAge }
- ])
+ testModel.appendRow({
+ name: personName,
+ age: personAge
+ })
+ }
+
+ function appendRowExtraData() {
+ testModel.appendRow({
+ name: "Foo",
+ age: 99,
+ nonExistentRole: 123
+ })
}
function appendRowInvalid1() {
- testModel.appendRow([
- { name: "Foo" },
- { age: 99 },
- { nonExistentRole: 123 }
- ])
+ testModel.appendRow(123)
}
function appendRowInvalid2() {
- testModel.appendRow(123)
+ testModel.appendRow({
+ name: "Foo",
+ age: []
+ })
}
function appendRowInvalid3() {
testModel.appendRow([
- { name: "Foo" },
- { age: [] }
+ { name: "Bar" },
+ { age: "111" }
])
}
function insertRow(personName, personAge, rowIndex) {
- testModel.insertRow(rowIndex, [
- { name: personName },
- { age: personAge }]
- )
+ testModel.insertRow(rowIndex, {
+ name: personName,
+ age: personAge
+ })
+ }
+
+ function insertRowExtraData() {
+ testModel.insertRow(0, {
+ name: "Foo",
+ age: 99,
+ nonExistentRole: 123
+ })
}
function insertRowInvalid1() {
- testModel.insertRow(0, [
- { name: "Foo" },
- { age: 99 },
- { nonExistentRole: 123 }
- ])
+ testModel.insertRow(0, 123)
}
function insertRowInvalid2() {
- testModel.insertRow(0, 123)
+ testModel.insertRow(0, {
+ name: "Foo",
+ age: []
+ })
}
function insertRowInvalid3() {
testModel.insertRow(0, [
- { name: "Foo" },
- { age: [] }
+ { name: "Bar" },
+ { age: "111" }
])
}
function setRow(rowIndex, personName, personAge) {
- testModel.setRow(rowIndex, [
- { name: personName },
- { age: personAge }]
- )
+ testModel.setRow(rowIndex, {
+ name: personName,
+ age: personAge
+ })
+ }
+
+ function setRowExtraData() {
+ testModel.setRow(0, {
+ name: "Foo",
+ age: 99,
+ nonExistentRole: 123
+ })
}
function setRowInvalid1() {
- testModel.setRow(0, [
- { name: "Foo" },
- { age: 99 },
- { nonExistentRole: 123 }
- ])
+ testModel.setRow(0, 123)
}
function setRowInvalid2() {
- testModel.setRow(0, 123)
+ testModel.setRow(0, {
+ name: "Foo",
+ age: []
+ })
}
function setRowInvalid3() {
testModel.setRow(0, [
- { name: "Foo" },
- { age: [] }
+ { name: "Bar" },
+ { age: "111" }
])
}
diff --git a/tests/auto/qml/qqmltablemodel/data/roleDataProvider.qml b/tests/auto/qml/qqmltablemodel/data/complex.qml
index 2706ea54fd..dbf53bac7e 100644
--- a/tests/auto/qml/qqmltablemodel/data/roleDataProvider.qml
+++ b/tests/auto/qml/qqmltablemodel/data/complex.qml
@@ -26,40 +26,43 @@
**
****************************************************************************/
-import QtQuick 2.12
+import QtQuick 2.13
import Qt.labs.qmlmodels 1.0
-Item {
- id: root
- width: 200
- height: 200
+TableView {
+ width: 100
+ height: 100
+ delegate: Item {
+ implicitWidth: 50
+ implicitHeight: 50
- property alias testModel: testModel
-
- TableModel {
+ Text {
+ text: model.display
+ anchors.centerIn: parent
+ }
+ }
+ model: TableModel {
id: testModel
objectName: "testModel"
- rows: [
- [ { name: "Rex" }, { age: 3 } ],
- [ { name: "Buster" }, { age: 5 } ]
- ]
- roleDataProvider: function(index, role, cellData) {
- if (role === "display") {
- // Age will now be in dog years
- if (cellData.hasOwnProperty("age"))
- return (cellData.age * 7);
- else if (index.column === 0)
- return (cellData.name);
- }
- return cellData;
+
+ TableModelColumn {
+ display: function(modelIndex) { return testModel.rows[modelIndex.row][0].name }
+ setDisplay: function(modelIndex, cellData) { testModel.rows[modelIndex.row][0].name = cellData }
}
- }
- TableView {
- anchors.fill: parent
- model: testModel
- delegate: Text {
- id: textItem
- text: model.display
+ 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
index d61c50ba2c..d3f726bfa1 100644
--- a/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml
+++ b/tests/auto/qml/qqmltablemodel/data/dataAndSetData.qml
@@ -31,36 +31,25 @@ import Qt.labs.qmlmodels 1.0
TableView {
width: 200; height: 200
- model: TableModel {
+ model: TestModel {
id: testModel
- objectName: "testModel"
- rows: [
- [
- { name: "John", someOtherRole1: "foo" }, // column 0
- { age: 22, someOtherRole2: "foo" } // column 1
- ],
- [
- { name: "Oliver", someOtherRole1: "foo" }, // column 0
- { age: 33, someOtherRole2: "foo" } // column 1
- ]
- ]
// 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), "name") === dude)
+ if (testModel.data(testModel.index(r, 0), "display") === dude)
row = r;
var index = testModel.index(row, 1)
- testModel.setData(index, "age", testModel.data(index, "age") + 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), "name"))
+ 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
index 6e66b99145..f5afbd1d27 100644
--- a/tests/auto/qml/qqmltablemodel/data/empty.qml
+++ b/tests/auto/qml/qqmltablemodel/data/empty.qml
@@ -29,8 +29,6 @@
import QtQuick 2.12
import Qt.labs.qmlmodels 1.0
-import "TestUtils.js" as TestUtils
-
Item {
id: root
width: 200
@@ -41,21 +39,37 @@ Item {
function setRows() {
testModel.rows = [
- [
- { name: "John" },
- { age: 22 }
- ],
- [
- { name: "Oliver" },
- { age: 33 }
- ]
+ {
+ 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"
- roleDataProvider: TestUtils.testModelRoleDataProvider
+
+ TableModelColumn { display: "name" }
+ TableModelColumn { display: "age" }
}
TableView {
id: tableView
diff --git a/tests/auto/qml/qqmltablemodel/data/explicitDisplayRole.qml b/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml
index 510a62e74b..86bcb08fa2 100644
--- a/tests/auto/qml/qqmltablemodel/data/explicitDisplayRole.qml
+++ b/tests/auto/qml/qqmltablemodel/data/omitTableModelColumnIndex.qml
@@ -28,14 +28,20 @@
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", display: "foo" },
- { age: 22, display: "bar" }
- ]
+ {
+ 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
index 5f849c3350..ebfe4ed930 100644
--- a/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml
+++ b/tests/auto/qml/qqmltablemodel/data/setDataThroughDelegate.qml
@@ -41,11 +41,11 @@ Item {
signal shouldModifyInvalidType()
function modify() {
- shouldModify();
+ shouldModify()
}
function modifyInvalidRole() {
- shouldModifyInvalidRole();
+ shouldModifyInvalidRole()
}
function modifyInvalidType() {
@@ -54,37 +54,24 @@ Item {
TableView {
anchors.fill: parent
- model: TableModel {
+ model: TestModel {
id: testModel
- objectName: "testModel"
- rows: [
- [
- { name: "John" },
- { age: 22 }
- ],
- [
- { name: "Oliver" },
- { age: 33 }
- ]
- ]
}
delegate: Text {
id: textItem
- // TODO: this is currently random when no roleDataProvider handles it
- // we should allow roleDataProvider to be used to handle specific roles only
text: model.display
Connections {
target: root
enabled: column === 1
- onShouldModify: model.age = 18
+ onShouldModify: model.display = 18
}
Connections {
target: root
enabled: column === 0
- // Invalid: should be "name".
+ // Invalid: should be "display".
onShouldModifyInvalidRole: model.age = 100
}
@@ -92,7 +79,7 @@ Item {
target: root
enabled: column === 1
// Invalid: should be string.
- onShouldModifyInvalidType: model.age = "Whoops"
+ onShouldModifyInvalidType: model.display = "Whoops"
}
}
}
diff --git a/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml b/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml
index 6aaf79f2d4..01ec40270c 100644
--- a/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml
+++ b/tests/auto/qml/qqmltablemodel/data/setRowsMultipleTimes.qml
@@ -39,35 +39,35 @@ Item {
function setRowsValid() {
testModel.rows = [
- [
- { name: "Max" },
- { age: 20 }
- ],
- [
- { name: "Imum" },
- { age: 41 }
- ],
- [
- { name: "Power" },
- { age: 89 }
- ]
+ {
+ 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 }
- ]
+ {
+ nope: "Nope",
+ age: 20
+ },
+ {
+ nope: "Nah",
+ age: 41
+ },
+ {
+ nope: "No",
+ age: 89
+ }
]
}
diff --git a/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp
index 059ce082d9..113a27494d 100644
--- a/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp
+++ b/tests/auto/qml/qqmltablemodel/tst_qqmltablemodel.cpp
@@ -47,6 +47,7 @@ public:
private slots:
void appendRemoveRow();
+ void appendRowToEmptyModel();
void clear();
void getRow();
void insertRow();
@@ -55,15 +56,11 @@ private slots:
void setDataThroughDelegate();
void setRowsImperatively();
void setRowsMultipleTimes();
- void builtInRoles_data();
- void builtInRoles();
- void explicitDisplayRole();
- void roleDataProvider();
void dataAndEditing();
+ void omitTableModelColumnIndex();
+ void complexRow();
};
-static const int builtInRoleCount = 6;
-
void tst_QQmlTableModel::appendRemoveRow()
{
QQuickView view(testFileUrl("common.qml"));
@@ -81,22 +78,15 @@ void tst_QQmlTableModel::appendRemoveRow()
QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
QVERIFY(rowCountSpy.isValid());
- int heightSignalEmissions = 0;
+ int rowCountSignalEmissions = 0;
const QHash<int, QByteArray> roleNames = model->roleNames();
- QCOMPARE(roleNames.size(), 2 + builtInRoleCount);
- QVERIFY(roleNames.values().contains("name"));
- QVERIFY(roleNames.values().contains("age"));
+ QCOMPARE(roleNames.size(), 1);
QVERIFY(roleNames.values().contains("display"));
- QVERIFY(roleNames.values().contains("decoration"));
- QVERIFY(roleNames.values().contains("edit"));
- QVERIFY(roleNames.values().contains("toolTip"));
- QVERIFY(roleNames.values().contains("statusTip"));
- QVERIFY(roleNames.values().contains("whatsThis"));
- QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
+ 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"));
@@ -104,7 +94,7 @@ void tst_QQmlTableModel::appendRemoveRow()
QCOMPARE(model->rowCount(), 2);
QCOMPARE(model->columnCount(), 2);
QCOMPARE(columnCountSpy.count(), 0);
- QCOMPARE(rowCountSpy.count(), heightSignalEmissions);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
// Call remove() with an rowIndex that is too large.
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
@@ -113,7 +103,7 @@ void tst_QQmlTableModel::appendRemoveRow()
QCOMPARE(model->rowCount(), 2);
QCOMPARE(model->columnCount(), 2);
QCOMPARE(columnCountSpy.count(), 0);
- QCOMPARE(rowCountSpy.count(), heightSignalEmissions);
+ 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"));
@@ -121,7 +111,7 @@ void tst_QQmlTableModel::appendRemoveRow()
QCOMPARE(model->rowCount(), 2);
QCOMPARE(model->columnCount(), 2);
QCOMPARE(columnCountSpy.count(), 0);
- QCOMPARE(rowCountSpy.count(), heightSignalEmissions);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
// Call remove() with a valid rowIndex but excessive rows.
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
@@ -130,71 +120,125 @@ void tst_QQmlTableModel::appendRemoveRow()
QCOMPARE(model->rowCount(), 2);
QCOMPARE(model->columnCount(), 2);
QCOMPARE(columnCountSpy.count(), 0);
- QCOMPARE(rowCountSpy.count(), heightSignalEmissions);
+ 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("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
+ 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(), ++heightSignalEmissions);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
- // Call append() with a row that has a new (and hence unexpected) role.
- QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*appendRow\\(\\): expected 2 columns, but got 3"));
+ // 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(), 1);
+ QCOMPARE(model->rowCount(), 2);
QCOMPARE(model->columnCount(), 2);
- QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
+ 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(), heightSignalEmissions);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
- // Call append() with a row that is not an array.
+ // Call append() with a row with a role of the wrong type.
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
- ".*appendRow\\(\\): expected \"row\" argument to be an array, but got int instead"));
+ ".*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(), 1);
+ QCOMPARE(model->rowCount(), 2);
QCOMPARE(model->columnCount(), 2);
- QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
+ 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(), heightSignalEmissions);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
- // Call append() with a row with a role that is of the wrong type.
+ // Call append() with a row that is an array instead of a simple object.
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
- ".*appendRow\\(\\): expected property with type int at column index 1, but got QVariantList instead"));
+ ".*appendRow\\(\\): row manipulation functions do not support complex rows \\(row index: -1\\)"));
QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "appendRowInvalid3"));
// Nothing should change.
- QCOMPARE(model->rowCount(), 1);
+ QCOMPARE(model->rowCount(), 2);
QCOMPARE(model->columnCount(), 2);
- QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
+ 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(), heightSignalEmissions);
+ 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(), 2);
+ QCOMPARE(model->rowCount(), 3);
QCOMPARE(model->columnCount(), 2);
- QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 40);
+ 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(), ++heightSignalEmissions);
+ 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, 2)));
+ 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("name")), QVariant());
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")), QVariant());
+ 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(), ++heightSignalEmissions);
+ 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()
@@ -216,9 +260,8 @@ void tst_QQmlTableModel::clear()
QVERIFY(rowCountSpy.isValid());
const QHash<int, QByteArray> roleNames = model->roleNames();
- QVERIFY(roleNames.values().contains("name"));
- QVERIFY(roleNames.values().contains("age"));
- QCOMPARE(roleNames.size(), 2 + builtInRoleCount);
+ QVERIFY(roleNames.values().contains("display"));
+ QCOMPARE(roleNames.size(), 1);
QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>();
QVERIFY(tableView);
@@ -263,9 +306,9 @@ void tst_QQmlTableModel::getRow()
// Call get() with a valid row index.
QVERIFY(QMetaObject::invokeMethod(model, "getRow", Q_RETURN_ARG(QVariant, returnValue), Q_ARG(int, 0)));
- const QVariantList rowAsVariantList = returnValue.toList();
- QCOMPARE(rowAsVariantList.at(0).toMap().value(QLatin1String("name")), QLatin1String("John"));
- QCOMPARE(rowAsVariantList.at(1).toMap().value(QLatin1String("age")), 22);
+ const QVariantMap rowAsVariantMap = returnValue.toMap();
+ QCOMPARE(rowAsVariantMap.value(QLatin1String("name")).toString(), QLatin1String("John"));
+ QCOMPARE(rowAsVariantMap.value(QLatin1String("age")).toInt(), 22);
}
void tst_QQmlTableModel::insertRow()
@@ -285,7 +328,7 @@ void tst_QQmlTableModel::insertRow()
QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
QVERIFY(rowCountSpy.isValid());
- int heightSignalEmissions = 0;
+ int rowCountSignalEmissions = 0;
QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>();
QVERIFY(tableView);
@@ -300,12 +343,12 @@ void tst_QQmlTableModel::insertRow()
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("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
+ 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(), heightSignalEmissions);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
QCOMPARE(tableView->rows(), 2);
QCOMPARE(tableView->columns(), 2);
@@ -316,92 +359,112 @@ void tst_QQmlTableModel::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("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
+ 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(), heightSignalEmissions);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
QCOMPARE(tableView->rows(), 2);
QCOMPARE(tableView->columns(), 2);
- // Try to insert a row that has a new (and hence unexpected) role.
- QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*insertRow\\(\\): expected 2 columns, but got 3"));
+ // 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->columnCount(), 2);
QCOMPARE(model->rowCount(), 2);
- QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
+ 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(), heightSignalEmissions);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
QCOMPARE(tableView->rows(), 2);
QCOMPARE(tableView->columns(), 2);
- // Try to insert a row that is not an array.
+ // Try to insert a row with a role of the wrong type.
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
- ".*insertRow\\(\\): expected \"row\" argument to be an array, but got int instead"));
+ ".*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("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
+ 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(), heightSignalEmissions);
+ 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.
+ // Try to insert a row that is an array instead of a simple object.
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
- ".*insertRow\\(\\): expected property with type int at column index 1, but got QVariantList instead"));
+ ".*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("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
+ 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(), heightSignalEmissions);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
QCOMPARE(tableView->rows(), 2);
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, 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("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
- QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max"));
- QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("age")).toInt(), 40);
+ 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(), ++heightSignalEmissions);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
QTRY_COMPARE(tableView->rows(), 3);
QCOMPARE(tableView->columns(), 2);
- // Insert a row in the middle of the table.
+ // Insert a row at the bottom of the table.
QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "insertRow",
- Q_ARG(QVariant, QLatin1String("Daisy")), Q_ARG(QVariant, 30), Q_ARG(QVariant, 1)));
+ 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("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Daisy"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 30);
- QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
- QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max"));
- QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("age")).toInt(), 40);
+ 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(), ++heightSignalEmissions);
+ 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()
@@ -421,7 +484,7 @@ void tst_QQmlTableModel::moveRow()
QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
QVERIFY(rowCountSpy.isValid());
- int heightSignalEmissions = 0;
+ int rowCountSignalEmissions = 0;
const QHash<int, QByteArray> roleNames = model->roleNames();
@@ -431,105 +494,105 @@ void tst_QQmlTableModel::moveRow()
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("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
- QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max"));
- QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("age")).toInt(), 40);
- QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Daisy"));
- QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("age")).toInt(), 30);
- QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Trev"));
- QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("age")).toInt(), 48);
- heightSignalEmissions = 3;
+ 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(), heightSignalEmissions);
+ 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("name")).toString(), QLatin1String("Trev"));
- QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("age")).toInt(), 48);
+ 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(), heightSignalEmissions);
+ 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("name")).toString(), QLatin1String("Trev"));
- QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("age")).toInt(), 48);
+ 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(), heightSignalEmissions);
+ 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("name")).toString(), QLatin1String("Trev"));
- QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("age")).toInt(), 48);
+ 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(), heightSignalEmissions);
+ 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("name")).toString(), QLatin1String("Trev"));
- QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("age")).toInt(), 48);
+ 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(), heightSignalEmissions);
+ 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("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 40);
- QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Daisy"));
- QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("age")).toInt(), 30);
- QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Trev"));
- QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("age")).toInt(), 48);
- QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
+ 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(), heightSignalEmissions);
+ 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("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
- QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max"));
- QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("age")).toInt(), 40);
- QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Daisy"));
- QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("age")).toInt(), 30);
- QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Trev"));
- QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("age")).toInt(), 48);
+ 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(), heightSignalEmissions);
+ 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("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
- QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Max"));
- QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("age")).toInt(), 40);
- QCOMPARE(model->data(model->index(3, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Daisy"));
- QCOMPARE(model->data(model->index(3, 1, QModelIndex()), roleNames.key("age")).toInt(), 30);
- QCOMPARE(model->data(model->index(4, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Trev"));
- QCOMPARE(model->data(model->index(4, 1, QModelIndex()), roleNames.key("age")).toInt(), 48);
- QCOMPARE(columnCountSpy.count(), 0);
- QCOMPARE(rowCountSpy.count(), heightSignalEmissions);
+ 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()
@@ -549,14 +612,14 @@ void tst_QQmlTableModel::setRow()
QSignalSpy rowCountSpy(model, SIGNAL(rowCountChanged()));
QVERIFY(rowCountSpy.isValid());
- int heightSignalEmissions = 0;
+ 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.
+ // Try to set with a negative index.
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
".*setRow\\(\\): \"rowIndex\" cannot be negative"));
QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRow",
@@ -564,72 +627,86 @@ void tst_QQmlTableModel::setRow()
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("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
+ 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(), heightSignalEmissions);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
QCOMPARE(tableView->rows(), 2);
QCOMPARE(tableView->columns(), 2);
- // Try to insert past the last allowed index.
+ // 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("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
+ 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(), heightSignalEmissions);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
QCOMPARE(tableView->rows(), 2);
QCOMPARE(tableView->columns(), 2);
- // Try to insert a row that has a new (and hence unexpected) role.
- QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*setRow\\(\\): expected 2 columns, but got 3"));
- QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowInvalid1"));
+ // 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("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
+ 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(), heightSignalEmissions);
+ 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 an array, but got int instead"));
- QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "setRowInvalid2"));
+ ".*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("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
+ 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(), heightSignalEmissions);
+ 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 property with type int at column index 1, but got QVariantList instead"));
+ ".*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("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
+ 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(), heightSignalEmissions);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
QCOMPARE(tableView->rows(), 2);
QCOMPARE(tableView->columns(), 2);
@@ -638,12 +715,12 @@ void tst_QQmlTableModel::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("name")).toString(), QLatin1String("Max"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 40);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
+ 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(), heightSignalEmissions);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
QCOMPARE(tableView->rows(), 2);
QCOMPARE(tableView->columns(), 2);
@@ -652,12 +729,12 @@ void tst_QQmlTableModel::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("name")).toString(), QLatin1String("Max"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 40);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Daisy"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 30);
+ 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(), heightSignalEmissions);
+ QCOMPARE(rowCountSpy.count(), rowCountSignalEmissions);
QCOMPARE(tableView->rows(), 2);
QCOMPARE(tableView->columns(), 2);
@@ -666,14 +743,14 @@ void tst_QQmlTableModel::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("name")).toString(), QLatin1String("Max"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 40);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Daisy"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 30);
- QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Wot"));
- QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("age")).toInt(), 99);
+ 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(), ++heightSignalEmissions);
+ QCOMPARE(rowCountSpy.count(), ++rowCountSignalEmissions);
QTRY_COMPARE(tableView->rows(), 3);
QCOMPARE(tableView->columns(), 2);
}
@@ -697,56 +774,50 @@ void tst_QQmlTableModel::setDataThroughDelegate()
QVERIFY(rowCountSpy.isValid());
const QHash<int, QByteArray> roleNames = model->roleNames();
- QCOMPARE(roleNames.size(), 2 + builtInRoleCount);
- QVERIFY(roleNames.values().contains("name"));
- QVERIFY(roleNames.values().contains("age"));
- QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
+ 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("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 18);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 18);
+ 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.
- const auto invalidRoleRegEx = QRegularExpression(".*setData\\(\\): no role named \"age\" at column index 0. " \
- "The available roles for that column are:[\r\n] - \"name\" \\(QString\\)");
- // There are two rows, so two delegates respond to the signal, which means we need to ignore two warnings.
- QTest::ignoreMessage(QtWarningMsg, invalidRoleRegEx);
- QTest::ignoreMessage(QtWarningMsg, invalidRoleRegEx);
QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "modifyInvalidRole"));
// Should be unchanged.
- QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 18);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 18);
+ 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 \"age\" to \"int\""));
+ "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 \"age\" to \"int\""));
+ "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("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 18);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 18);
+ 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 append to test widthChanged().
+// Start off with empty rows and then set them to test rowCountChanged().
void tst_QQmlTableModel::setRowsImperatively()
{
QQuickView view(testFileUrl("empty.qml"));
@@ -756,8 +827,8 @@ void tst_QQmlTableModel::setRowsImperatively()
QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
QVERIFY(model);
- QCOMPARE(model->columnCount(), 0);
QCOMPARE(model->rowCount(), 0);
+ QCOMPARE(model->columnCount(), 2);
QSignalSpy columnCountSpy(model, SIGNAL(columnCountChanged()));
QVERIFY(columnCountSpy.isValid());
@@ -768,17 +839,17 @@ void tst_QQmlTableModel::setRowsImperatively()
QQuickTableView *tableView = view.rootObject()->property("tableView").value<QQuickTableView*>();
QVERIFY(tableView);
QCOMPARE(tableView->rows(), 0);
- QCOMPARE(tableView->columns(), 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("name")).toString(), QLatin1String("John"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 22);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Oliver"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 33);
- QCOMPARE(columnCountSpy.count(), 1);
+ 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);
@@ -812,134 +883,96 @@ void tst_QQmlTableModel::setRowsMultipleTimes()
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("name")).toString(), QLatin1String("Max"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 20);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Imum"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 41);
- QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Power"));
- QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("age")).toInt(), 89);
+ 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.
- // TODO: add quotes to the warning message
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
- ".*setRows\\(\\): expected property named name at column index 0, but got nope instead"));
+ ".*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("name")).toString(), QLatin1String("Max"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("age")).toInt(), 20);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Imum"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("age")).toInt(), 41);
- QCOMPARE(model->data(model->index(2, 0, QModelIndex()), roleNames.key("name")).toString(), QLatin1String("Power"));
- QCOMPARE(model->data(model->index(2, 1, QModelIndex()), roleNames.key("age")).toInt(), 89);
+ 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::builtInRoles_data()
-{
- QTest::addColumn<int>("row");
- QTest::addColumn<int>("column");
- QTest::addColumn<QByteArray>("roleName");
- QTest::addColumn<QVariant>("expectedValue");
-
- const QByteArray displayRole = "display";
-
- QTest::addRow("display(0,0)") << 0 << 0 << displayRole << QVariant(QLatin1String("John"));
- QTest::addRow("display(0,1)") << 0 << 1 << displayRole << QVariant(QLatin1String("22"));
- QTest::addRow("display(1,0)") << 1 << 0 << displayRole << QVariant(QLatin1String("Oliver"));
- QTest::addRow("display(1,1)") << 1 << 1 << displayRole << QVariant(QLatin1String("33"));
-}
-
-void tst_QQmlTableModel::builtInRoles()
+void tst_QQmlTableModel::dataAndEditing()
{
- QFETCH(int, row);
- QFETCH(int, column);
- QFETCH(QByteArray, roleName);
- QFETCH(QVariant, expectedValue);
-
- QQmlEngine engine;
- QQmlComponent component(&engine, testFileUrl("builtInRoles.qml"));
- QCOMPARE(component.status(), QQmlComponent::Ready);
+ QQuickView view(testFileUrl("dataAndSetData.qml"));
+ QCOMPARE(view.status(), QQuickView::Ready);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowActive(&view));
- QScopedPointer<QQmlTableModel> model(qobject_cast<QQmlTableModel*>(component.create()));
+ QQmlTableModel *model = view.rootObject()->property("model").value<QQmlTableModel*>();
QVERIFY(model);
- QCOMPARE(model->rowCount(), 2);
- QCOMPARE(model->columnCount(), 2);
const QHash<int, QByteArray> roleNames = model->roleNames();
- QCOMPARE(roleNames.size(), 4 + builtInRoleCount);
QVERIFY(roleNames.values().contains("display"));
- QVERIFY(roleNames.values().contains("decoration"));
- QVERIFY(roleNames.values().contains("edit"));
- QVERIFY(roleNames.values().contains("toolTip"));
- QVERIFY(roleNames.values().contains("statusTip"));
- QVERIFY(roleNames.values().contains("whatsThis"));
- QVERIFY(roleNames.values().contains("name"));
- QVERIFY(roleNames.values().contains("age"));
- QVERIFY(roleNames.values().contains("someOtherRole1"));
- QVERIFY(roleNames.values().contains("someOtherRole2"));
- QCOMPARE(model->data(model->index(row, column, QModelIndex()), roleNames.key(roleName)), expectedValue);
+ 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::explicitDisplayRole()
+void tst_QQmlTableModel::omitTableModelColumnIndex()
{
QQmlEngine engine;
- QQmlComponent component(&engine, testFileUrl("explicitDisplayRole.qml"));
+ QQmlComponent component(&engine, testFileUrl("omitTableModelColumnIndex.qml"));
QCOMPARE(component.status(), QQmlComponent::Ready);
QScopedPointer<QQmlTableModel> model(qobject_cast<QQmlTableModel*>(component.create()));
QVERIFY(model);
- QCOMPARE(model->rowCount(), 1);
+ 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("foo"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("bar"));
-}
-
-void tst_QQmlTableModel::roleDataProvider()
-{
- QQuickView view(testFileUrl("roleDataProvider.qml"));
- QCOMPARE(view.status(), QQuickView::Ready);
- view.show();
- QVERIFY(QTest::qWaitForWindowActive(&view));
-
- QQmlTableModel *model = view.rootObject()->property("testModel").value<QQmlTableModel*>();
- QVERIFY(model);
const QHash<int, QByteArray> roleNames = model->roleNames();
- QVERIFY(roleNames.values().contains("display"));
- QCOMPARE(model->data(model->index(0, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Rex"));
- QCOMPARE(model->data(model->index(0, 1, QModelIndex()), roleNames.key("display")).toInt(), 3 * 7);
- QCOMPARE(model->data(model->index(1, 0, QModelIndex()), roleNames.key("display")).toString(), QLatin1String("Buster"));
- QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 5 * 7);
+ 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::dataAndEditing()
+void tst_QQmlTableModel::complexRow()
{
- QQuickView view(testFileUrl("dataAndSetData.qml"));
+ QQuickView view(testFileUrl("complex.qml"));
QCOMPARE(view.status(), QQuickView::Ready);
view.show();
QVERIFY(QTest::qWaitForWindowActive(&view));
- QQmlTableModel *model = view.rootObject()->property("model").value<QQmlTableModel*>();
+ 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();
- 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);
+ QCOMPARE(model->data(model->index(1, 1, QModelIndex()), roleNames.key("display")).toInt(), 33);
}
QTEST_MAIN(tst_QQmlTableModel)
diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
index 8a01524b5b..83a37df797 100644
--- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
+++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
@@ -33,6 +33,8 @@
#include <QJSValueIterator>
#include <private/qquickvaluetypes_p.h>
#include <private/qqmlglobal_p.h>
+#include <private/qv4engine_p.h>
+#include <private/qv4variantobject_p.h>
#include "../../shared/util.h"
#include "testtypes.h"
@@ -94,6 +96,7 @@ private slots:
void toStringConversion();
void enumerableProperties();
void enumProperties();
+ void scarceTypes();
private:
QQmlEngine engine;
@@ -1809,6 +1812,26 @@ void tst_qqmlvaluetypes::enumProperties()
QCOMPARE(enumValue.toInt(), int(g.enumProperty()));
}
+void tst_qqmlvaluetypes::scarceTypes()
+{
+ // These should not be treated as value types because we want the scarce resource
+ // mechanism to clear them when going out of scope. The scarce resource mechanism
+ // only works on QV4::VariantObject as that has an additional level of redirection.
+ QVERIFY(!QQmlValueTypeFactory::isValueType(qMetaTypeId<QImage>()));
+ QVERIFY(!QQmlValueTypeFactory::isValueType(qMetaTypeId<QPixmap>()));
+
+ QV4::ExecutionEngine engine;
+ QV4::Scope scope(&engine);
+
+ QImage img(20, 20, QImage::Format_ARGB32);
+ QV4::ScopedObject imgValue(scope, engine.fromVariant(QVariant::fromValue(img)));
+ QCOMPARE(QByteArray(imgValue->vtable()->className), QByteArray("VariantObject"));
+
+ QPixmap pixmap;
+ QV4::ScopedObject pixmapValue(scope, engine.fromVariant(QVariant::fromValue(img)));
+ QCOMPARE(QByteArray(pixmapValue->vtable()->className), QByteArray("VariantObject"));
+}
+
QTEST_MAIN(tst_qqmlvaluetypes)
diff --git a/tests/auto/qml/qquickfolderlistmodel/BLACKLIST b/tests/auto/qml/qquickfolderlistmodel/BLACKLIST
new file mode 100644
index 0000000000..642fdea741
--- /dev/null
+++ b/tests/auto/qml/qquickfolderlistmodel/BLACKLIST
@@ -0,0 +1,3 @@
+[nameFilters]
+msvc-2015
+msvc-2017
diff --git a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp
index dfaeca67f1..bea9978f0b 100644
--- a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp
+++ b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp
@@ -92,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();
}
@@ -141,15 +141,15 @@ 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());
- QTest::newRow("regexp") << qVariantFromValue(QRegExp("^\\d\\d?$", Qt::CaseInsensitive,
+ 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") << qVariantFromValue(QRegularExpression(
+ QTest::newRow("regularexpression") << QVariant::fromValue(QRegularExpression(
"^\\d\\d?$", QRegularExpression::CaseInsensitiveOption));
}
@@ -165,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();
@@ -193,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 4916cb4cc0..fd50ff5020 100644
--- a/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp
+++ b/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp
@@ -34,6 +34,8 @@
#include <QtQml/qqml.h>
#include <QtQml/qqmlapplicationengine.h>
+#include <private/qv4global_p.h>
+
#ifdef Q_OS_WIN
#include <windows.h>
#endif
@@ -46,6 +48,7 @@ private slots:
void initTestCase() override;
void perfMapFile();
void functionTable();
+ void jitEnabled();
};
void tst_QV4Assembler::initTestCase()
@@ -137,6 +140,26 @@ 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);
+#elif defined(Q_OS_WIN) && defined(Q_PROCESSOR_ARM)
+ /* JIT should be disabled Windows on ARM/ARM64 for now. */
+ QCOMPARE(JIT_ENABLED, 0);
+#else
+ /* JIT should be enabled on all other architectures/OSes tested in CI. */
+ QCOMPARE(JIT_ENABLED, 1);
+#endif
+}
+
QTEST_MAIN(tst_QV4Assembler)
#include "tst_qv4assembler.moc"
diff --git a/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp b/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp
index 095943cdc7..308fba9049 100644
--- a/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp
+++ b/tests/auto/qml/qv4identifiertable/tst_qv4identifiertable.cpp
@@ -67,8 +67,8 @@ void tst_qv4identifiertable::sweepFirstEntryInBucket()
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);
diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
index 578a47d5fa..1e34b79954 100644
--- a/tests/auto/qml/qv4mm/tst_qv4mm.cpp
+++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
@@ -33,6 +33,7 @@
#include <private/qv4mm_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qjsvalue_p.h>
#include "../../shared/util.h"
@@ -46,6 +47,7 @@ private slots:
void gcStats();
void multiWrappedQObjects();
void accessParentOnDestruction();
+ void clearICParent();
};
void tst_qv4mm::gcStats()
@@ -108,6 +110,44 @@ void tst_qv4mm::accessParentOnDestruction()
QCOMPARE(obj->property("destructions").toInt(), 100);
}
+void tst_qv4mm::clearICParent()
+{
+ QV4::ExecutionEngine engine;
+ QV4::Scope scope(engine.rootContext());
+ QV4::ScopedObject object(scope, engine.newObject());
+
+ // Keep identifiers in a separate array so that we don't have to allocate them in the loop that
+ // should test the GC on InternalClass allocations.
+ QV4::ScopedArrayObject identifiers(scope, engine.newArrayObject());
+ for (uint i = 0; i < 16 * 1024; ++i) {
+ QV4::Scope scope(&engine);
+ QV4::ScopedString s(scope);
+ s = engine.newIdentifier(QString::fromLatin1("key%1").arg(i));
+ identifiers->push_back(s);
+
+ QV4::ScopedValue v(scope);
+ v->setDouble(i);
+ object->insertMember(s, v);
+ }
+
+ // When allocating the InternalClass objects required for deleting properties, the GC should
+ // eventually run and remove all but the last two.
+ // If we ever manage to avoid allocating the InternalClasses in the first place we will need
+ // to change this test.
+ for (uint i = 0; i < 16 * 1024; ++i) {
+ QV4::Scope scope(&engine);
+ QV4::ScopedString s(scope, identifiers->getIndexed(i));
+ QV4::Scoped<QV4::InternalClass> ic(scope, object->internalClass());
+ QVERIFY(ic->d()->parent != nullptr);
+ object->deleteProperty(s->toPropertyKey());
+ QVERIFY(object->internalClass() != ic->d());
+ QCOMPARE(object->internalClass()->parent, ic->d());
+ if (ic->d()->parent == nullptr)
+ return;
+ }
+ QFAIL("Garbage collector was not triggered by large amount of InternalClasses");
+}
+
QTEST_MAIN(tst_qv4mm)
#include "tst_qv4mm.moc"
diff --git a/tests/auto/qmltest/animatedimage/BLACKLIST b/tests/auto/qmltest/animatedimage/BLACKLIST
new file mode 100644
index 0000000000..3a5ed393ea
--- /dev/null
+++ b/tests/auto/qmltest/animatedimage/BLACKLIST
@@ -0,0 +1,2 @@
+[AnimatedImage::test_crashRaceCondition_replyFinished]
+osx-10.13
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragMargin.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragMargin.qml
new file mode 100644
index 0000000000..e5ca681bd5
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/dragMargin.qml
@@ -0,0 +1,36 @@
+import QtQuick 2.12
+
+Rectangle {
+ color: "#333"
+ width: 480; height: 480
+
+ Rectangle {
+ color: "#112"
+ width: 100
+ height: 100
+ x: 50; y: 50
+
+ DragHandler {
+ id: dragHandler
+ margin: 20
+ }
+
+ Rectangle {
+ id: rect
+ anchors.fill: parent
+ anchors.margins: -dragHandler.margin
+ color: "transparent"
+ border.color: "cyan"
+ border.width: 2
+ radius: 10
+ antialiasing: true
+
+ Text {
+ color: "cyan"
+ text: "drag this margin area"
+ font.pixelSize: 10
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
index eb210c2112..cc8c567e5c 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
@@ -55,6 +55,7 @@ private slots:
void defaultPropertyValues();
void touchDrag();
void mouseDrag();
+ void dragFromMargin();
void touchDragMulti();
void touchDragMultiSliders_data();
void touchDragMultiSliders();
@@ -251,6 +252,38 @@ void tst_DragHandler::mouseDrag()
QCOMPARE(centroidChangedSpy.count(), 5);
}
+void tst_DragHandler::dragFromMargin() // QTBUG-74966
+{
+ const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+ QScopedPointer<QQuickView> windowPtr;
+ createView(windowPtr, "dragMargin.qml");
+ QQuickView * window = windowPtr.data();
+
+ QQuickItem *draggableItem = window->rootObject()->childItems().first();
+ QVERIFY(draggableItem);
+ QQuickDragHandler *dragHandler = draggableItem->findChild<QQuickDragHandler*>();
+ QVERIFY(dragHandler);
+
+ QPointF originalPos = draggableItem->position();
+ QPointF scenePressPos = originalPos - QPointF(10, 0);
+ QPoint p1 = scenePressPos.toPoint();
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1);
+ QVERIFY(!dragHandler->active());
+ QCOMPARE(dragHandler->centroid().scenePosition(), scenePressPos);
+ QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos);
+ p1 += QPoint(dragThreshold * 2, 0);
+ QTest::mouseMove(window, p1);
+ QTRY_VERIFY(dragHandler->active());
+ QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos);
+ QCOMPARE(dragHandler->centroid().sceneGrabPosition(), p1);
+ QCOMPARE(dragHandler->translation().x(), 0.0); // hmm that's odd
+ QCOMPARE(dragHandler->translation().y(), 0.0);
+ QCOMPARE(draggableItem->position(), originalPos + QPointF(dragThreshold * 2, 0));
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1);
+ QTRY_VERIFY(!dragHandler->active());
+ QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton);
+}
+
void tst_DragHandler::touchDragMulti()
{
const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
index f141a2546c..575139f851 100644
--- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
@@ -252,7 +252,7 @@ void tst_HoverHandler::movingItemWithHoverHandler()
QTRY_COMPARE(window->isVisible(), false);
QCursor::setPos(paddlePos);
window->show();
- QTest::qWaitForWindowExposed(window);
+ QVERIFY(QTest::qWaitForWindowExposed(window));
QTRY_COMPARE(paddleHH->isHovered(), true);
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/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
index 3b704d7fa4..448096720c 100644
--- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
+++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
@@ -6637,7 +6637,7 @@ void tst_QQuickGridView::contentHeightWithDelayRemove()
QCOMPARE(qRound(gridview->contentHeight()), qRound(initialContentHeight));
QTRY_COMPARE(qRound(gridview->contentHeight()), eventualContentHeight);
} else {
- QCOMPARE(qRound(gridview->contentHeight()), eventualContentHeight);
+ QTRY_COMPARE(qRound(gridview->contentHeight()), eventualContentHeight);
}
delete window;
diff --git a/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp b/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp
index 1a289a2087..2f90632841 100644
--- a/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp
+++ b/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp
@@ -51,9 +51,8 @@ public:
view.setSource(testFileUrl(fileName));
view.showNormal();
- QTest::qWaitForWindowExposed(&view);
-
- return view.grabWindow();
+ return QTest::qWaitForWindowExposed(&view)
+ ? view.grabWindow() : QImage();
}
private slots:
@@ -153,6 +152,7 @@ void tst_QQuickItemLayer::layerSmooth()
QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms");
QImage fb = runTest("Smooth.qml");
+ QVERIFY(!fb.size().isEmpty());
QCOMPARE(fb.pixel(0, 0), qRgb(0xff, 0, 0));
QCOMPARE(fb.pixel(fb.width() - 1, 0), qRgb(0, 0, 0xff));
@@ -177,6 +177,7 @@ void tst_QQuickItemLayer::layerEnabled()
QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms");
QImage fb = runTest("Enabled.qml");
+ QVERIFY(!fb.size().isEmpty());
// Verify the banding
QCOMPARE(fb.pixel(0, 0), fb.pixel(0, 1));
// Verify the gradient
@@ -212,6 +213,7 @@ void tst_QQuickItemLayer::layerEffect()
QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms");
QImage fb = runTest("Effect.qml");
+ QVERIFY(!fb.size().isEmpty());
QCOMPARE(fb.pixel(0, 0), qRgb(0xff, 0, 0));
QCOMPARE(fb.pixel(fb.width() - 1, 0), qRgb(0, 0xff, 0));
}
@@ -229,6 +231,7 @@ void tst_QQuickItemLayer::layerSourceRect()
QSKIP("Only OpenGL Renderer supports GLSL ShaderEffects");
QImage fb = runTest("SourceRect.qml");
+ QVERIFY(!fb.size().isEmpty());
// Check that the edges are converted to blue
QCOMPARE(fb.pixel(0, 0), qRgb(0, 0, 0xff));
@@ -253,6 +256,7 @@ void tst_QQuickItemLayer::layerIsTextureProvider()
QSKIP("Only OpenGL Renderer supports GLSL ShaderEffects");
QImage fb = runTest("TextureProvider.qml");
+ QVERIFY(!fb.size().isEmpty());
QCOMPARE(fb.pixel(0, 0), qRgb(0xff, 0, 0));
QCOMPARE(fb.pixel(fb.width() - 1, 0), qRgb(0, 0xff, 0));
}
@@ -448,6 +452,7 @@ void tst_QQuickItemLayer::changeSamplerName()
QSKIP("Only OpenGL Renderer supports GLSL ShaderEffects");
QImage fb = runTest("SamplerNameChange.qml");
+ QVERIFY(!fb.size().isEmpty());
QCOMPARE(fb.pixel(0, 0), qRgb(0, 0, 0xff));
}
@@ -459,6 +464,7 @@ void tst_QQuickItemLayer::itemEffect()
QSKIP("Only OpenGL Renderer supports GLSL ShaderEffects");
QImage fb = runTest("ItemEffect.qml");
+ QVERIFY(!fb.size().isEmpty());
QCOMPARE(fb.pixel(0, 0), qRgb(0xff, 0, 0));
QCOMPARE(fb.pixel(199, 0), qRgb(0xff, 0, 0));
QCOMPARE(fb.pixel(0, 199), qRgb(0, 0, 0xff));
@@ -472,6 +478,7 @@ void tst_QQuickItemLayer::rectangleEffect()
QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms");
QImage fb = runTest("RectangleEffect.qml");
+ QVERIFY(!fb.size().isEmpty());
QCOMPARE(fb.pixel(0, 0), qRgb(0, 0xff, 0));
QCOMPARE(fb.pixel(199, 0), qRgb(0, 0xff, 0));
QCOMPARE(fb.pixel(0, 199), qRgb(0, 0xff, 0));
diff --git a/tests/auto/quick/qquicklistview/BLACKLIST b/tests/auto/quick/qquicklistview/BLACKLIST
index e22d52294f..893f95dcea 100644
--- a/tests/auto/quick/qquicklistview/BLACKLIST
+++ b/tests/auto/quick/qquicklistview/BLACKLIST
@@ -1,6 +1,11 @@
[enforceRange_withoutHighlight]
osx
+opensuse-42.3
+opensuse-leap
#QTBUG-53863
[populateTransitions]
opensuse-42.1
#QTBUG-65964
+
+[contentHeightWithDelayRemove]
+osx-10.12
diff --git a/tests/auto/quick/qquicklistview/data/delegateWithMouseArea.qml b/tests/auto/quick/qquicklistview/data/delegateWithMouseArea.qml
new file mode 100644
index 0000000000..e0b8222bfb
--- /dev/null
+++ b/tests/auto/quick/qquicklistview/data/delegateWithMouseArea.qml
@@ -0,0 +1,29 @@
+import QtQuick 2.12
+
+ListView {
+ id: root
+ objectName: "view"
+ width: 600
+ height: 600
+ model: 3
+ snapMode: ListView.SnapOneItem
+ boundsBehavior: Flickable.StopAtBounds
+ highlightRangeMode: ListView.StrictlyEnforceRange
+ preferredHighlightBegin: 0
+ preferredHighlightEnd: 0
+ highlightMoveDuration: 100
+ delegate: Rectangle {
+ id: delegateRect
+ width: 500
+ height: 500
+ color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
+ Text {
+ text: index
+ font.pixelSize: 128
+ anchors.centerIn: parent
+ }
+ MouseArea {
+ anchors.fill: parent
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
index d96590bdae..2ea8a477a8 100644
--- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
+++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
@@ -38,6 +38,7 @@
#include <QtQml/qqmlincubator.h>
#include <QtQuick/private/qquickitemview_p_p.h>
#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>
@@ -275,6 +276,7 @@ private slots:
void addOnCompleted();
void setPositionOnLayout();
+ void touchCancel();
private:
template <class T> void items(const QUrl &source);
@@ -330,6 +332,7 @@ private:
QQuickView *m_view;
QString testForView;
+ QTouchDevice *touchDevice = QTest::createTouchDevice();
};
class TestObject : public QObject
@@ -8968,6 +8971,37 @@ void tst_QQuickListView::useDelegateChooserWithoutDefault()
window->show();
};
+void tst_QQuickListView::touchCancel() // QTBUG-74679
+{
+ QScopedPointer<QQuickView> window(createView());
+ window->setSource(testFileUrl("delegateWithMouseArea.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ QQuickListView *listview = qobject_cast<QQuickListView *>(window->rootObject());
+ QVERIFY(listview);
+ QQuickMouseArea *mouseArea = listview->currentItem()->findChild<QQuickMouseArea *>();
+ QVERIFY(mouseArea);
+
+ QPoint p1(300, 300);
+ QTest::touchEvent(window.data(), touchDevice).press(0, p1, window.data());
+ QQuickTouchUtils::flush(window.data());
+ QTRY_VERIFY(mouseArea->pressed());
+ // and because Flickable filtered it, QQuickFlickablePrivate::pressed
+ // should be true, but it's not easily tested here
+
+ QTouchEvent cancelEvent(QEvent::TouchCancel);
+ cancelEvent.setDevice(touchDevice);
+ QCoreApplication::sendEvent(window.data(), &cancelEvent);
+ // now QQuickWindowPrivate::sendUngrabEvent() will be called, Flickable will filter it,
+ // QQuickFlickablePrivate::pressed will be set to false, and that will allow setCurrentIndex() to make it move
+ QQuickTouchUtils::flush(window.data());
+
+ listview->setCurrentIndex(1);
+ // ensure that it actually moves (animates) to the second delegate
+ QTRY_COMPARE(listview->contentY(), 500.0);
+}
+
QTEST_MAIN(tst_QQuickListView)
#include "tst_qquicklistview.moc"
diff --git a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST
index cdb3e7733b..6af00ab76f 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST
+++ b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST
@@ -1,6 +1,12 @@
[nonOverlapping]
ubuntu-16.04
ubuntu-18.04
+opensuse-42.3
+opensuse-leap
+sles
[nested]
ubuntu-16.04
ubuntu-18.04
+opensuse-42.3
+opensuse-leap
+sles
diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
index d4ad282701..cd66fc4ede 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
+++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
@@ -1145,16 +1145,18 @@ void tst_QQuickMultiPointTouchArea::transformedTouchArea()
QQuickView *tst_QQuickMultiPointTouchArea::createAndShowView(const QString &file)
{
- QQuickView *window = new QQuickView(nullptr);
+ QScopedPointer<QQuickView> window(new QQuickView(nullptr));
window->setSource(testFileUrl(file));
+ if (window->status() != QQuickView::Ready)
+ return nullptr;
const QRect screenGeometry = window->screen()->availableGeometry();
const QSize size = window->size();
const QPoint offset = QPoint(size.width() / 2, size.height() / 2);
window->setFramePosition(screenGeometry.center() - offset);
window->show();
- QTest::qWaitForWindowExposed(window);
-
- return window;
+ if (!QTest::qWaitForWindowExposed(window.data()))
+ return nullptr;
+ return window.take();
}
void tst_QQuickMultiPointTouchArea::mouseInteraction_data()
diff --git a/tests/auto/quick/qquickpathview/data/pathview5.qml b/tests/auto/quick/qquickpathview/data/pathview5.qml
new file mode 100644
index 0000000000..479c5dc500
--- /dev/null
+++ b/tests/auto/quick/qquickpathview/data/pathview5.qml
@@ -0,0 +1,65 @@
+import QtQuick 2.0
+
+PathView {
+ property int countclick: 0
+ id: pathview
+ y: 0
+ width: 348
+ height: 480
+
+ interactive: false
+
+ cacheItemCount: 10
+ currentIndex: 2
+ pathItemCount: 4
+ highlightMoveDuration: 1000
+ highlightRangeMode : PathView.StrictlyEnforceRange
+ preferredHighlightBegin: 0.5
+ preferredHighlightEnd: 0.5
+ snapMode : PathView.SnapOneItem
+
+ path: Path {
+ id: leftPath
+ startX: pathview.width / 2 - 800
+ startY: pathview.height / 2 - 800
+
+ PathArc {
+ x: pathview.width / 2 - 800
+ y: pathview.height / 2 + 800
+ radiusX: 800
+ radiusY: 800
+ direction: PathArc.Clockwise
+ }
+ }
+
+ model: ListModel {
+ id: model
+ ListElement { objectName:"aqua"; name: "aqua" ;mycolor:"aqua"}
+ ListElement { objectName:"blue"; name: "blue" ;mycolor:"blue"}
+ ListElement { objectName:"blueviolet"; name: "blueviolet" ;mycolor:"blueviolet"}
+ ListElement { objectName:"brown"; name: "brown" ;mycolor:"brown"}
+ ListElement { objectName:"chartreuse"; name: "chartreuse" ;mycolor:"chartreuse"}
+ }
+
+ delegate: Item {
+ id: revolveritem
+ objectName: model.objectName
+
+ width: pathview.width
+ height: pathview.height
+
+ Rectangle
+ {
+ id:myRectangle
+ color: mycolor
+ width: pathview.width -20
+ height: pathview.height -20
+
+ Text {
+ anchors.centerIn: parent
+ text: "index:"+index
+ color: "white"
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
index bf38d2d926..1a5ce39318 100644
--- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
+++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
@@ -104,6 +104,7 @@ private slots:
void offset_data();
void offset();
void setCurrentIndex();
+ void setCurrentIndexWrap();
void resetModel();
void propertyChanges();
void pathChanges();
@@ -1138,6 +1139,28 @@ void tst_QQuickPathView::setCurrentIndex()
QCOMPARE(currentIndexSpy.count(), 1);
}
+void tst_QQuickPathView::setCurrentIndexWrap()
+{
+ QScopedPointer<QQuickView> window(createView());
+ window->setSource(testFileUrl("pathview5.qml"));
+ window->show();
+ qApp->processEvents();
+
+ QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject());
+ QVERIFY(pathview);
+
+ // set current index to last item
+ pathview->setCurrentIndex(4);
+ // set currentIndex to first item, then quickly set it back (QTBUG-74508)
+ QSignalSpy currentIndexSpy(pathview, SIGNAL(currentIndexChanged()));
+ QSignalSpy movementStartedSpy(pathview, SIGNAL(movementStarted()));
+ pathview->setCurrentIndex(0);
+ pathview->setCurrentIndex(4);
+ QCOMPARE(pathview->currentIndex(), 4);
+ QCOMPARE(currentIndexSpy.count(), 2);
+ QCOMPARE(movementStartedSpy.count(), 0);
+}
+
void tst_QQuickPathView::resetModel()
{
QScopedPointer<QQuickView> window(createView());
diff --git a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp
index f6ca999cf5..710caaa734 100644
--- a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp
+++ b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp
@@ -134,14 +134,12 @@ void tst_qquickrectangle::gradient_separate()
// Start off clean
QQuickItemPrivate *rectPriv = QQuickItemPrivate::get(rect);
- bool isDirty = rectPriv->dirtyAttributes & QQuickItemPrivate::Content;
- QVERIFY(!isDirty);
+ QTRY_COMPARE(rectPriv->dirtyAttributes & QQuickItemPrivate::Content, 0u);
QMetaObject::invokeMethod(rect, "changeGradient");
// Changing the gradient should have scheduled an update of the item.
- isDirty = rectPriv->dirtyAttributes & QQuickItemPrivate::Content;
- QVERIFY(isDirty);
+ QVERIFY((rectPriv->dirtyAttributes & QQuickItemPrivate::Content) != 0);
}
// When a gradient is changed, every Rectangle connected to it must update.
@@ -161,17 +159,15 @@ void tst_qquickrectangle::gradient_multiple()
// Start off clean
QQuickItemPrivate *firstRectPriv = QQuickItemPrivate::get(firstRect);
QQuickItemPrivate *secondRectPriv = QQuickItemPrivate::get(secondRect);
- bool firstIsDirty = firstRectPriv->dirtyAttributes & QQuickItemPrivate::Content;
+ QTRY_VERIFY(!(firstRectPriv->dirtyAttributes & QQuickItemPrivate::Content));
bool secondIsDirty = secondRectPriv->dirtyAttributes & QQuickItemPrivate::Content;
- QVERIFY(!firstIsDirty);
QVERIFY(!secondIsDirty);
QMetaObject::invokeMethod(view.rootObject(), "changeGradient");
// Changing the gradient should have scheduled an update of both items
- firstIsDirty = firstRectPriv->dirtyAttributes & QQuickItemPrivate::Content;
+ QTRY_VERIFY(firstRectPriv->dirtyAttributes & QQuickItemPrivate::Content);
secondIsDirty = secondRectPriv->dirtyAttributes & QQuickItemPrivate::Content;
- QVERIFY(firstIsDirty);
QVERIFY(secondIsDirty);
}
diff --git a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp
index e4b427f6ec..f19f5f9877 100644
--- a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp
+++ b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp
@@ -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/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
index 60d48bb59f..420a9fdb13 100644
--- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
+++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
@@ -601,8 +601,7 @@ 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;
// Check that the average cell size is now matching the
@@ -654,7 +653,7 @@ 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
@@ -689,7 +688,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
@@ -699,7 +697,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));
@@ -721,7 +718,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);
@@ -1942,7 +1938,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;
@@ -2025,7 +2021,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
diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
index a862604fc1..4cf7fa7119 100644
--- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
+++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
@@ -1645,11 +1645,11 @@ void tst_qquickwindow::focusReason()
window->setTitle(QTest::currentTestFunction());
QVERIFY(QTest::qWaitForWindowExposed(window));
- QQuickItem *firstItem = new QQuickItem;
+ QScopedPointer<QQuickItem> firstItem(new QQuickItem);
firstItem->setSize(QSizeF(100, 100));
firstItem->setParentItem(window->contentItem());
- QQuickItem *secondItem = new QQuickItem;
+ QScopedPointer<QQuickItem> secondItem(new QQuickItem);
secondItem->setSize(QSizeF(100, 100));
secondItem->setParentItem(window->contentItem());
@@ -1673,7 +1673,7 @@ void tst_qquickwindow::ignoreUnhandledMouseEvents()
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window));
- QQuickItem *item = new QQuickItem;
+ QScopedPointer<QQuickItem> item(new QQuickItem);
item->setSize(QSizeF(100, 100));
item->setParentItem(window->contentItem());
@@ -1883,8 +1883,8 @@ void tst_qquickwindow::hideThenDelete()
QFETCH(bool, persistentSG);
QFETCH(bool, persistentGL);
- QSignalSpy *openglDestroyed = nullptr;
- QSignalSpy *sgInvalidated = nullptr;
+ QScopedPointer<QSignalSpy> openglDestroyed;
+ QScopedPointer<QSignalSpy> sgInvalidated;
{
QQuickWindow window;
@@ -1903,10 +1903,10 @@ void tst_qquickwindow::hideThenDelete()
const bool isGL = window.rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL;
#if QT_CONFIG(opengl)
if (isGL)
- openglDestroyed = new QSignalSpy(window.openglContext(), SIGNAL(aboutToBeDestroyed()));
+ openglDestroyed.reset(new QSignalSpy(window.openglContext(), SIGNAL(aboutToBeDestroyed())));
#endif
- sgInvalidated = new QSignalSpy(&window, SIGNAL(sceneGraphInvalidated()));
+ sgInvalidated.reset(new QSignalSpy(&window, SIGNAL(sceneGraphInvalidated())));
window.hide();
@@ -1951,7 +1951,7 @@ void tst_qquickwindow::showHideAnimate()
QQmlEngine engine;
QQmlComponent component(&engine);
component.loadUrl(testFileUrl("showHideAnimate.qml"));
- QQuickItem* created = qobject_cast<QQuickItem *>(component.create());
+ QScopedPointer<QQuickItem> created(qobject_cast<QQuickItem *>(component.create()));
QVERIFY(created);
@@ -2293,7 +2293,7 @@ void tst_qquickwindow::contentItemSize()
QQmlEngine engine;
QQmlComponent component(&engine);
component.setData(QByteArray("import QtQuick 2.1\n Rectangle { anchors.fill: parent }"), QUrl());
- QQuickItem *rect = qobject_cast<QQuickItem *>(component.create());
+ QScopedPointer<QQuickItem> rect(qobject_cast<QQuickItem *>(component.create()));
QVERIFY(rect);
rect->setParentItem(window.contentItem());
QCOMPARE(QSizeF(rect->width(), rect->height()), size);
diff --git a/tests/auto/quick/touchmouse/BLACKLIST b/tests/auto/quick/touchmouse/BLACKLIST
index b2ba52eca9..0dfe28087a 100644
--- a/tests/auto/quick/touchmouse/BLACKLIST
+++ b/tests/auto/quick/touchmouse/BLACKLIST
@@ -1,2 +1,6 @@
[buttonOnDelayedPressFlickable]
windows gcc developer-build
+
+# QTBUG-74517
+[buttonOnFlickable]
+windows gcc developer-build
diff --git a/tests/auto/quicktest/quicktestmainwithsetup/data/tst_setup.qml b/tests/auto/quicktest/quicktestmainwithsetup/data/tst_setup.qml
index 0f5466998a..ea6b3e014b 100644
--- a/tests/auto/quicktest/quicktestmainwithsetup/data/tst_setup.qml
+++ b/tests/auto/quicktest/quicktestmainwithsetup/data/tst_setup.qml
@@ -29,9 +29,15 @@
import QtQuick 2.0
import QtTest 1.2
+import QmlRegisterTypeCppModule 1.0
+import ImportPathQmlModule 1.0
+
TestCase {
name: "setup"
+ QmlRegisterTypeCppType {}
+ ImportPathQmlType {}
+
function initTestCase()
{
verify(qmlEngineAvailableCalled)
diff --git a/tests/auto/quicktest/quicktestmainwithsetup/imports/ImportPathQmlModule/ImportPathQmlType.qml b/tests/auto/quicktest/quicktestmainwithsetup/imports/ImportPathQmlModule/ImportPathQmlType.qml
new file mode 100644
index 0000000000..617bdaaf67
--- /dev/null
+++ b/tests/auto/quicktest/quicktestmainwithsetup/imports/ImportPathQmlModule/ImportPathQmlType.qml
@@ -0,0 +1,3 @@
+import QtQuick 2.0
+
+Item {}
diff --git a/tests/auto/quicktest/quicktestmainwithsetup/imports/ImportPathQmlModule/qmldir b/tests/auto/quicktest/quicktestmainwithsetup/imports/ImportPathQmlModule/qmldir
new file mode 100644
index 0000000000..dea7c9a8a4
--- /dev/null
+++ b/tests/auto/quicktest/quicktestmainwithsetup/imports/ImportPathQmlModule/qmldir
@@ -0,0 +1,2 @@
+module ImportPathQmlModule
+ImportPathQmlType 1.0 ImportPathQmlType.qml
diff --git a/tests/auto/quicktest/quicktestmainwithsetup/tst_quicktestmainwithsetup.cpp b/tests/auto/quicktest/quicktestmainwithsetup/tst_quicktestmainwithsetup.cpp
index b0545d1a95..b5deeceac4 100644
--- a/tests/auto/quicktest/quicktestmainwithsetup/tst_quicktestmainwithsetup.cpp
+++ b/tests/auto/quicktest/quicktestmainwithsetup/tst_quicktestmainwithsetup.cpp
@@ -35,6 +35,14 @@
#include "../../shared/util.h"
+class QmlRegisterTypeCppType : public QObject
+{
+ Q_OBJECT
+
+public:
+ QmlRegisterTypeCppType() {}
+};
+
class CustomTestSetup : public QObject
{
Q_OBJECT
@@ -45,6 +53,12 @@ public:
public slots:
void qmlEngineAvailable(QQmlEngine *qmlEngine)
{
+ // Test that modules are successfully imported by the TestCaseCollector that
+ // parses the QML files (but doesn't run them). For that to happen, qmlEngineAvailable()
+ // must be called before TestCaseCollector does its thing.
+ qmlRegisterType<QmlRegisterTypeCppType>("QmlRegisterTypeCppModule", 1, 0, "QmlRegisterTypeCppType");
+ qmlEngine->addImportPath(QString::fromUtf8(QT_QMLTEST_DATADIR) + "/../imports");
+
qmlEngine->rootContext()->setContextProperty("qmlEngineAvailableCalled", true);
}
};
diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
index aaf37b32cd..fd5c3653ad 100644
--- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
+++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
@@ -161,7 +161,7 @@ void tst_qquickwidget::showHide()
window.show();
QVERIFY(QTest::qWaitForWindowExposed(&window));
- QVERIFY(childView->quickWindow()->isVisible());
+ QVERIFY(!childView->quickWindow()->isVisible()); // this window is always not visible see QTBUG-65761
QVERIFY(childView->quickWindow()->visibility() != QWindow::Hidden);
window.hide();
@@ -612,7 +612,7 @@ void tst_qquickwidget::synthMouseFromTouch()
childView->resize(300, 300);
window.show();
QVERIFY(QTest::qWaitForWindowActive(&window));
- QVERIFY(childView->quickWindow()->isVisible());
+ QVERIFY(!childView->quickWindow()->isVisible()); // this window is always not visible see QTBUG-65761
QVERIFY(item->isVisible());
QPoint p1 = QPoint(20, 20);
diff --git a/tests/manual/scenegraph_lancelot/data/text/text_wrap_elide_maxlines.qml b/tests/manual/scenegraph_lancelot/data/text/text_wrap_elide_maxlines.qml
new file mode 100644
index 0000000000..927f2b3148
--- /dev/null
+++ b/tests/manual/scenegraph_lancelot/data/text/text_wrap_elide_maxlines.qml
@@ -0,0 +1,133 @@
+import QtQuick 2.0
+
+//test wrapping and elision when maximumLineCount is set
+
+Item {
+ width: 320
+ height: 480
+ Rectangle {
+ id: text_area
+ color: "light yellow"
+ x: 50
+ y: 0
+ height: parent.height
+ width: 150
+ }
+ Text {
+ id: text_0000
+ wrapMode: Text.WrapAnywhere
+ text: "The quick brown fox jumps over the lazy dog."
+ x: text_area.x
+ y: text_area.y
+ width: text_area.width
+ maximumLineCount: 2
+ elide: Text.ElideRight
+ color: "red"
+ font.family: "Arial"
+ font.pixelSize: 22
+ }
+ Text {
+ id: text_0001
+ wrapMode: Text.Wrap
+ text: text_0000.text
+ anchors.top: text_0000.bottom
+ anchors.left: text_0000.left
+ width: text_0000.width
+ maximumLineCount: text_0000.maximumLineCount
+ elide: Text.ElideRight
+ color: "blue"
+ font.family: text_0000.font.family
+ font.pixelSize: text_0000.font.pixelSize
+ }
+ Text {
+ id: text_0002
+ wrapMode: Text.WordWrap
+ text: text_0000.text
+ anchors.top: text_0001.bottom
+ anchors.left: text_0000.left
+ width: text_0000.width
+ maximumLineCount: text_0000.maximumLineCount
+ elide: Text.ElideRight
+ color: "green"
+ font.family: text_0000.font.family
+ font.pixelSize: text_0000.font.pixelSize
+ }
+ Text {
+ id: text_0003
+ wrapMode: Text.WrapAnywhere
+ text: "ABCDEFGHIJKL 1234567890123"
+ anchors.top: text_0002.bottom
+ anchors.left: text_0000.left
+ width: 150
+ maximumLineCount: 2
+ elide: Text.ElideRight
+ color: "red"
+ font.family: text_0000.font.family
+ font.pixelSize: text_0000.font.pixelSize
+ }
+ Text {
+ id: text_0004
+ wrapMode: Text.Wrap
+ text: text_0003.text
+ anchors.top: text_0003.bottom
+ anchors.left: text_0000.left
+ width: text_0000.width
+ maximumLineCount: text_0000.maximumLineCount
+ elide: Text.ElideRight
+ color: "blue"
+ font.family: text_0000.font.family
+ font.pixelSize: text_0000.font.pixelSize
+ }
+ Text {
+ id: text_0005
+ wrapMode: Text.WordWrap
+ text: text_0003.text
+ anchors.top: text_0004.bottom
+ anchors.left: text_0000.left
+ width: text_0000.width
+ maximumLineCount: text_0000.maximumLineCount
+ elide: Text.ElideRight
+ color: "green"
+ font.family: text_0000.font.family
+ font.pixelSize: text_0000.font.pixelSize
+ }
+ Text {
+ id: text_0006
+ wrapMode: Text.WrapAnywhere
+ text: "The quick brown 1234567890123"
+ anchors.top: text_0005.bottom
+ anchors.left: text_0000.left
+ width: 150
+ maximumLineCount: 2
+ elide: Text.ElideRight
+ color: "red"
+ font.family: text_0000.font.family
+ font.pixelSize: text_0000.font.pixelSize
+ }
+ Text {
+ id: text_0007
+ wrapMode: Text.Wrap
+ text: text_0006.text
+ anchors.top: text_0006.bottom
+ anchors.left: text_0000.left
+ width: text_0000.width
+ maximumLineCount: text_0000.maximumLineCount
+ elide: Text.ElideRight
+ color: "blue"
+ font.family: text_0000.font.family
+ font.pixelSize: text_0000.font.pixelSize
+ }
+ Text {
+ id: text_0008
+ wrapMode: Text.WordWrap
+ text: text_0006.text
+ anchors.top: text_0007.bottom
+ anchors.left: text_0000.left
+ width: text_0000.width
+ maximumLineCount: text_0000.maximumLineCount
+ elide: Text.ElideRight
+ color: "green"
+ font.family: text_0000.font.family
+ font.pixelSize: text_0000.font.pixelSize
+ }
+}
diff --git a/tests/manual/tableview/tablemodel/form/RowForm.qml b/tests/manual/tableview/tablemodel/form/RowForm.qml
index 428682008a..bb03e685c0 100644
--- a/tests/manual/tableview/tablemodel/form/RowForm.qml
+++ b/tests/manual/tableview/tablemodel/form/RowForm.qml
@@ -45,13 +45,13 @@ ScrollView {
clip: true
function inputAsRow() {
- return [
- { checkable: checkableCheckBox.checked, checked: checkedCheckBox.checked },
- { amount: amountSpinBox.value },
- { fruitType: fruitTypeTextField.text },
- { fruitName: fruitNameTextField.text },
- { fruitPrice: parseFloat(fruitPriceTextField.text) },
- ]
+ return {
+ checked: checkedCheckBox.checked,
+ amount: amountSpinBox.value,
+ fruitType: fruitTypeTextField.text,
+ fruitName: fruitNameTextField.text,
+ fruitPrice: parseFloat(fruitPriceTextField.text)
+ }
}
default property alias content: gridLayout.children
@@ -60,23 +60,11 @@ ScrollView {
id: gridLayout
columns: 2
- RowLayout {
- Layout.columnSpan: 2
-
- Label {
- text: "checkable"
- }
- CheckBox {
- id: checkableCheckBox
- checked: true
- }
-
- Label {
- text: "checked"
- }
- CheckBox {
- id: checkedCheckBox
- }
+ Label {
+ text: "checked"
+ }
+ CheckBox {
+ id: checkedCheckBox
}
Label {
diff --git a/tests/manual/tableview/tablemodel/form/main.qml b/tests/manual/tableview/tablemodel/form/main.qml
index 21ecd8edbb..6c6874fb4b 100644
--- a/tests/manual/tableview/tablemodel/form/main.qml
+++ b/tests/manual/tableview/tablemodel/form/main.qml
@@ -64,31 +64,37 @@ ApplicationWindow {
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 cell/column,
- // and each property in that object is a role.
- { 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: true },
- { amount: 1 },
- { fruitType: "Banana" },
- { fruitName: "Cavendish" },
- { fruitPrice: 3.50 }
- ]
+ {
+ // 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
+ }
]
}
@@ -97,16 +103,16 @@ ApplicationWindow {
column: 0
delegate: CheckBox {
objectName: "tableViewCheckBoxDelegate"
- checked: model.checked
- onToggled: model.checked = checked
+ checked: model.display
+ onToggled: model.display = display
}
}
DelegateChoice {
column: 1
delegate: SpinBox {
objectName: "tableViewSpinBoxDelegate"
- value: model.amount
- onValueModified: model.amount = value
+ value: model.display
+ onValueModified: model.display = value
}
}
DelegateChoice {
diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp
index f7891e7d4b..df7468eaef 100644
--- a/tools/qmlcachegen/qmlcachegen.cpp
+++ b/tools/qmlcachegen/qmlcachegen.cpp
@@ -55,17 +55,8 @@ QSet<QString> illegalNames;
void setupIllegalNames()
{
- // #### this in incomplete
- illegalNames.insert(QStringLiteral("Math"));
- illegalNames.insert(QStringLiteral("Array"));
- illegalNames.insert(QStringLiteral("String"));
- illegalNames.insert(QStringLiteral("Function"));
- illegalNames.insert(QStringLiteral("Boolean"));
- illegalNames.insert(QStringLiteral("Number"));
- illegalNames.insert(QStringLiteral("Date"));
- illegalNames.insert(QStringLiteral("RegExp"));
- illegalNames.insert(QStringLiteral("Error"));
- illegalNames.insert(QStringLiteral("Object"));
+ for (const char **g = QV4::Compiler::Codegen::s_globalNames; *g != nullptr; ++g)
+ illegalNames.insert(QString::fromLatin1(*g));
}
struct Error
@@ -212,16 +203,13 @@ static bool compileQmlFile(const QString &inputFileName, SaveFunction saveFuncti
QmlIR::JSCodeGen v4CodeGen(irDocument.code,
&irDocument.jsGenerator, &irDocument.jsModule,
&irDocument.jsParserEngine, irDocument.program,
- /*import cache*/nullptr, &irDocument.jsGenerator.stringTable, illegalNames);
- v4CodeGen.setUseFastLookups(false); // Disable lookups in non-standalone (aka QML) mode
+ &irDocument.jsGenerator.stringTable, illegalNames);
for (QmlIR::Object *object: qAsConst(irDocument.objects)) {
if (object->functionsAndExpressions->count == 0)
continue;
QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
- for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next) {
- foe->disableAcceleratedLookups = true;
+ 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()) {
@@ -247,8 +235,6 @@ static bool compileQmlFile(const QString &inputFileName, SaveFunction saveFuncti
if (!saveFunction(irDocument.javaScriptCompilationUnit, &error->message))
return false;
-
- free(unit);
}
return true;
}
@@ -256,7 +242,6 @@ 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;
- QScopedPointer<QV4::CompiledData::Unit, QScopedPointerPodDeleter> unitDataToFree;
QString sourceCode;
{
@@ -319,9 +304,7 @@ static bool compileJSFile(const QString &inputFileName, const QString &inputFile
{
QmlIR::JSCodeGen v4CodeGen(irDocument.code, &irDocument.jsGenerator,
&irDocument.jsModule, &irDocument.jsParserEngine,
- irDocument.program, /*import cache*/nullptr,
- &irDocument.jsGenerator.stringTable, illegalNames);
- v4CodeGen.setUseFastLookups(false); // Disable lookups in non-standalone (aka QML) mode
+ irDocument.program, &irDocument.jsGenerator.stringTable, illegalNames);
v4CodeGen.generateFromProgram(inputFileName, inputFileUrl, sourceCode, program,
&irDocument.jsModule, QV4::Compiler::ContextType::ScriptImportedByQML);
QList<QQmlJS::DiagnosticMessage> jsErrors = v4CodeGen.errors();
@@ -339,7 +322,6 @@ static bool compileJSFile(const QString &inputFileName, const QString &inputFile
generator.generate(irDocument);
QV4::CompiledData::Unit *unitData = const_cast<QV4::CompiledData::Unit*>(irDocument.javaScriptCompilationUnit->data);
unitData->flags |= QV4::CompiledData::Unit::StaticData;
- unitDataToFree.reset(unitData);
unit = irDocument.javaScriptCompilationUnit;
}
}
diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp
index 60e0f1773f..616de9e80d 100644
--- a/tools/qmlimportscanner/main.cpp
+++ b/tools/qmlimportscanner/main.cpp
@@ -89,13 +89,13 @@ QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, con
{
QVariantList imports;
- // extract uri and version from the imports (which look like "import Foo.Bar 1.2.3")
+ // Extract uri and version from the imports (which look like "import Foo.Bar 1.2.3")
for (QQmlJS::AST::UiHeaderItemList *headerItemIt = headerItemList; headerItemIt; headerItemIt = headerItemIt->next) {
QVariantMap import;
QQmlJS::AST::UiImport *importNode = QQmlJS::AST::cast<QQmlJS::AST::UiImport *>(headerItemIt->headerItem);
if (!importNode)
continue;
- // handle directory imports
+ // Handle directory imports
if (!importNode->fileName.isEmpty()) {
QString name = importNode->fileName.toString();
import[nameLiteral()] = name;
@@ -137,7 +137,7 @@ QVariantMap pluginsForModulePath(const QString &modulePath) {
qmldirFile.open(QIODevice::ReadOnly | QIODevice::Text);
- // a qml import may contain several plugins
+ // A qml import may contain several plugins
QString plugins;
QString classnames;
QStringList dependencies;
@@ -206,7 +206,7 @@ QPair<QString, QString> resolveImportPath(const QString &uri, const QString &ver
}
}
- // remove the last version digit; stop if there are none left
+ // Remove the last version digit; stop if there are none left
if (ver.isEmpty())
break;
@@ -426,7 +426,7 @@ QVariantList findQmlImportsInDirectory(const QString &qmlDir)
if (std::find_if(blacklist.cbegin(), blacklist.cend(), pathStartsWith(path)) != blacklist.cend())
continue;
- // skip obvious build output directories
+ // Skip obvious build output directories
if (path.contains(QLatin1String("Debug-iphoneos")) || path.contains(QLatin1String("Release-iphoneos")) ||
path.contains(QLatin1String("Debug-iphonesimulator")) || path.contains(QLatin1String("Release-iphonesimulator"))
#ifdef Q_OS_WIN
@@ -455,30 +455,29 @@ QSet<QString> importModulePaths(const QVariantList &imports) {
return ret;
}
-// Find Qml Imports Recursively from a root set of qml files.
+// Find qml imports recursively from a root set of qml files.
// The directories in qmlDirs are searched recursively.
// The files in qmlFiles parsed directly.
QVariantList findQmlImportsRecursively(const QStringList &qmlDirs, const QStringList &scanFiles)
{
QVariantList ret;
- // scan all app root qml directories for imports
+ // Scan all app root qml directories for imports
for (const QString &qmlDir : qmlDirs) {
QVariantList imports = findQmlImportsInDirectory(qmlDir);
ret = mergeImports(ret, imports);
}
- // scan app qml files for imports
+ // Scan app qml files for imports
for (const QString &file : scanFiles) {
QVariantList imports = findQmlImportsInFile(file);
ret = mergeImports(ret, imports);
}
-
- // get the paths to theimports found in the app qml
+ // Get the paths to the imports found in the app qml
QSet<QString> toVisit = importModulePaths(ret);
- // recursivly scan for import dependencies.
+ // Recursively scan for import dependencies.
QSet<QString> visited;
while (!toVisit.isEmpty()) {
QString qmlDir = *toVisit.begin();
diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp
index 465bcb53d4..867267c821 100644
--- a/tools/qmlscene/main.cpp
+++ b/tools/qmlscene/main.cpp
@@ -42,6 +42,7 @@
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlfileselector.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickview.h>
@@ -361,6 +362,7 @@ static void usage()
#endif
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(" -P <path> ........................ Add <path> to the list of plugin paths");
puts(" -translation <translationfile> ... Set the language to run in");
@@ -457,6 +459,7 @@ int main(int argc, char ** argv)
Options options;
QStringList imports;
+ QStringList customSelectors;
QStringList pluginPaths;
// Parse arguments for application attributes to be applied before Q[Gui]Application creation.
@@ -522,12 +525,15 @@ int main(int argc, char ** argv)
options.resizeViewToRootItem = true;
else if (lowerArgument == QLatin1String("--multisample"))
options.multisample = true;
- else if (lowerArgument == QLatin1String("--core-profile"))
+ else if (lowerArgument == QLatin1String("--core-profile")
+ || qEnvironmentVariableIsSet("QMLSCENE_CORE_PROFILE"))
options.coreProfile = true;
else if (lowerArgument == QLatin1String("--verbose"))
options.verbose = true;
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));
else if (lowerArgument == QLatin1String("-p") && i + 1 < size)
pluginPaths.append(arguments.at(++i));
else if (lowerArgument == QLatin1String("--apptype"))
@@ -583,6 +589,8 @@ int main(int argc, char ** argv)
// TODO: as soon as the engine construction completes, the debug service is
// listening for connections. But actually we aren't ready to debug anything.
QQmlEngine engine;
+ QQmlFileSelector* selector = new QQmlFileSelector(&engine, &engine);
+ selector->setExtraSelectors(customSelectors);
QPointer<QQmlComponent> component = new QQmlComponent(&engine);
for (int i = 0; i < imports.size(); ++i)
engine.addImportPath(imports.at(i));
@@ -612,6 +620,19 @@ int main(int argc, char ** argv)
fprintf(stderr, "%s\n", qPrintable(component->errorString()));
return -1;
}
+
+ // Set default surface format before creating the window
+ QSurfaceFormat surfaceFormat;
+ if (options.multisample)
+ surfaceFormat.setSamples(16);
+ if (options.transparent)
+ surfaceFormat.setAlphaBufferSize(8);
+ if (options.coreProfile) {
+ surfaceFormat.setVersion(4, 1);
+ surfaceFormat.setProfile(QSurfaceFormat::CoreProfile);
+ }
+ QSurfaceFormat::setDefaultFormat(surfaceFormat);
+
QScopedPointer<QQuickWindow> window(qobject_cast<QQuickWindow *>(topLevel));
if (window) {
engine.setIncubationController(window->incubationController());
@@ -635,19 +656,11 @@ int main(int argc, char ** argv)
if (options.verbose)
new DiagnosticGlContextCreationListener(window.data());
#endif
- QSurfaceFormat surfaceFormat = window->requestedFormat();
- if (options.multisample)
- surfaceFormat.setSamples(16);
if (options.transparent) {
- surfaceFormat.setAlphaBufferSize(8);
window->setClearBeforeRendering(true);
window->setColor(QColor(Qt::transparent));
window->setFlags(Qt::FramelessWindowHint);
}
- if (options.coreProfile) {
- surfaceFormat.setVersion(4, 1);
- surfaceFormat.setProfile(QSurfaceFormat::CoreProfile);
- }
window->setFormat(surfaceFormat);
if (window->flags() == Qt::Window) // Fix window flags unless set by QML.
diff --git a/tools/tools.pro b/tools/tools.pro
index 2e2eb831a9..73cb6e2293 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 {
+qtConfig(thread):!android|android_app:!wasm {
SUBDIRS += \
qml